Coverage for python/lsst/fgcmcal/fgcmCalibrateTractBase.py: 16%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of fgcmcal.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://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 <https://www.gnu.org/licenses/>.
21"""Base class for running fgcmcal on a single tract using src tables
22or sourceTable_visit tables.
23"""
25import sys
26import traceback
27import abc
29import numpy as np
31import lsst.daf.persistence as dafPersist
32import lsst.pex.config as pexConfig
33import lsst.pipe.base as pipeBase
35from .fgcmBuildStars import FgcmBuildStarsTask, FgcmBuildStarsConfig
36from .fgcmFitCycle import FgcmFitCycleConfig
37from .fgcmOutputProducts import FgcmOutputProductsTask
38from .utilities import makeConfigDict, translateFgcmLut, translateVisitCatalog
39from .utilities import computeCcdOffsets, computeApertureRadiusFromDataRef, extractReferenceMags
40from .utilities import makeZptSchema, makeZptCat
41from .utilities import makeAtmSchema, makeAtmCat
42from .utilities import makeStdSchema, makeStdCat
44import fgcm
46__all__ = ['FgcmCalibrateTractConfigBase', 'FgcmCalibrateTractBaseTask', 'FgcmCalibrateTractRunner']
49class FgcmCalibrateTractConfigBase(pexConfig.Config):
50 """Config for FgcmCalibrateTract"""
52 fgcmBuildStars = pexConfig.ConfigurableField(
53 target=FgcmBuildStarsTask,
54 doc="Task to load and match stars for fgcm",
55 )
56 fgcmFitCycle = pexConfig.ConfigField(
57 dtype=FgcmFitCycleConfig,
58 doc="Config to run a single fgcm fit cycle",
59 )
60 fgcmOutputProducts = pexConfig.ConfigurableField(
61 target=FgcmOutputProductsTask,
62 doc="Task to output fgcm products",
63 )
64 convergenceTolerance = pexConfig.Field(
65 doc="Tolerance on repeatability convergence (per band)",
66 dtype=float,
67 default=0.005,
68 )
69 maxFitCycles = pexConfig.Field(
70 doc="Maximum number of fit cycles",
71 dtype=int,
72 default=5,
73 )
74 doDebuggingPlots = pexConfig.Field(
75 doc="Make plots for debugging purposes?",
76 dtype=bool,
77 default=False,
78 )
80 def setDefaults(self):
81 pexConfig.Config.setDefaults(self)
83 self.fgcmFitCycle.quietMode = True
84 self.fgcmFitCycle.doPlots = False
85 self.fgcmOutputProducts.doReferenceCalibration = False
86 self.fgcmOutputProducts.doRefcatOutput = False
87 self.fgcmOutputProducts.cycleNumber = 0
88 self.fgcmOutputProducts.photoCal.applyColorTerms = False
90 def validate(self):
91 super().validate()
93 for band in self.fgcmFitCycle.bands:
94 if not self.fgcmFitCycle.useRepeatabilityForExpGrayCutsDict[band]:
95 msg = 'Must set useRepeatabilityForExpGrayCutsDict[band]=True for all bands'
96 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.useRepeatabilityForExpGrayCutsDict,
97 self, msg)
100class FgcmCalibrateTractRunner(pipeBase.ButlerInitializedTaskRunner):
101 """Subclass of TaskRunner for FgcmCalibrateTractTask
103 fgcmCalibrateTractTask.run() takes a number of arguments, one of which is
104 the butler (for persistence and mapper data), and a list of dataRefs
105 extracted from the command line. This task runs on a constrained set
106 of dataRefs, typically a single tract.
107 This class transforms the process arguments generated by the ArgumentParser
108 into the arguments expected by FgcmCalibrateTractTask.run().
109 This runner does not use any parallelization.
110 """
112 @staticmethod
113 def getTargetList(parsedCmd):
114 """
115 Return a list with one element: a tuple with the butler and
116 list of dataRefs.
117 """
118 return [(parsedCmd.butler, parsedCmd.id.refList)]
120 def __call__(self, args):
121 """
122 Parameters
123 ----------
124 args: `tuple` with (butler, dataRefList)
126 Returns
127 -------
128 exitStatus: `list` with `lsst.pipe.base.Struct`
129 exitStatus (0: success; 1: failure)
130 May also contain results if `self.doReturnResults` is `True`.
131 """
132 butler, dataRefList = args
134 task = self.TaskClass(config=self.config, log=self.log)
136 exitStatus = 0
137 if self.doRaise:
138 results = task.runDataRef(butler, dataRefList)
139 else:
140 try:
141 results = task.runDataRef(butler, dataRefList)
142 except Exception as e:
143 exitStatus = 1
144 task.log.fatal("Failed: %s" % e)
145 if not isinstance(e, pipeBase.TaskError):
146 traceback.print_exc(file=sys.stderr)
148 task.writeMetadata(butler)
150 if self.doReturnResults:
151 return [pipeBase.Struct(exitStatus=exitStatus,
152 results=results)]
153 else:
154 return [pipeBase.Struct(exitStatus=exitStatus)]
156 def run(self, parsedCmd):
157 """
158 Run the task, with no multiprocessing
160 Parameters
161 ----------
162 parsedCmd: `lsst.pipe.base.ArgumentParser` parsed command line
163 """
165 resultList = []
167 if self.precall(parsedCmd):
168 targetList = self.getTargetList(parsedCmd)
169 resultList = self(targetList[0])
171 return resultList
174class FgcmCalibrateTractBaseTask(pipeBase.PipelineTask, pipeBase.CmdLineTask, abc.ABC):
175 """Base class to calibrate a single tract using fgcmcal
176 """
177 def __init__(self, initInputs=None, butler=None, **kwargs):
178 """
179 Instantiate an `FgcmCalibrateTractTask`.
181 Parameters
182 ----------
183 butler : `lsst.daf.persistence.Butler`, optional
184 """
185 super().__init__(**kwargs)
186 self.makeSubtask("fgcmBuildStars", initInputs=initInputs, butler=butler)
187 self.makeSubtask("fgcmOutputProducts", butler=butler)
189 # no saving of metadata for now
190 def _getMetadataName(self):
191 return None
193 @pipeBase.timeMethod
194 def runDataRef(self, butler, dataRefs):
195 """
196 Run full FGCM calibration on a single tract, including building star list,
197 fitting multiple cycles, and making outputs.
199 Parameters
200 ----------
201 butler: `lsst.daf.persistence.Butler`
202 dataRefs: `list` of `lsst.daf.persistence.ButlerDataRef`
203 Data references for the input visits.
204 These may be either per-ccd "src" or per-visit"sourceTable_visit"
205 references.
207 Raises
208 ------
209 RuntimeError: Raised if `config.fgcmBuildStars.doReferenceMatches` is
210 not True, or if fgcmLookUpTable is not available, or if
211 doSubtractLocalBackground is True and aperture radius cannot be
212 determined.
213 """
214 datasetType = dataRefs[0].butlerSubset.datasetType
215 self.log.info("Running with %d %s dataRefs" % (len(dataRefs), datasetType))
217 if not butler.datasetExists('fgcmLookUpTable'):
218 raise RuntimeError("Must run FgcmCalibrateTract with an fgcmLookUpTable")
220 if not self.config.fgcmBuildStars.doReferenceMatches:
221 raise RuntimeError("Must run FgcmCalibrateTract with fgcmBuildStars.doReferenceMatches")
222 if isinstance(self.config.fgcmBuildStars, FgcmBuildStarsConfig):
223 if self.config.fgcmBuildStars.checkAllCcds:
224 raise RuntimeError("Cannot run FgcmCalibrateTract with "
225 "fgcmBuildStars.checkAllCcds set to True")
227 tract = int(dataRefs[0].dataId['tract'])
228 camera = butler.get('camera')
230 dataRefDict = {}
231 dataRefDict['camera'] = camera
232 dataRefDict['source_catalogs'] = dataRefs
233 dataRefDict['sourceSchema'] = butler.get('src_schema', immediate=True).schema
234 dataRefDict['fgcmLookUpTable'] = butler.dataRef('fgcmLookUpTable')
236 struct = self.run(dataRefDict, tract, butler=butler, returnCatalogs=False)
238 visitDataRefName = self.config.fgcmBuildStars.visitDataRefName
239 ccdDataRefName = self.config.fgcmBuildStars.ccdDataRefName
241 if struct.photoCalibs is not None:
242 self.log.info("Outputting photoCalib files.")
244 for visit, detector, physicalFilter, photoCalib in struct.photoCalibs:
245 butler.put(photoCalib, 'fgcm_tract_photoCalib',
246 dataId={visitDataRefName: visit,
247 ccdDataRefName: detector,
248 'filter': physicalFilter,
249 'tract': tract})
251 self.log.info("Done outputting photoCalib files.")
253 if struct.atmospheres is not None:
254 self.log.info("Outputting atmosphere files.")
255 for visit, atm in struct.atmospheres:
256 butler.put(atm, "transmission_atmosphere_fgcm_tract",
257 dataId={visitDataRefName: visit,
258 'tract': tract})
259 self.log.info("Done outputting atmosphere transmissions.")
261 return pipeBase.Struct(repeatability=struct.repeatability)
263 def run(self, dataRefDict, tract,
264 buildStarsRefObjLoader=None, returnCatalogs=True, butler=None):
265 """Run the calibrations for a single tract with fgcm.
267 Parameters
268 ----------
269 dataRefDict : `dict`
270 All dataRefs are `lsst.daf.persistence.ButlerDataRef` (gen2) or
271 `lsst.daf.butler.DeferredDatasetHandle` (gen3)
272 dataRef dictionary with the following keys. Note that all
273 keys need not be set based on config parameters.
275 ``"camera"``
276 Camera object (`lsst.afw.cameraGeom.Camera`)
277 ``"source_catalogs"``
278 `list` of dataRefs for input source catalogs.
279 ``"sourceSchema"``
280 Schema for the source catalogs.
281 ``"fgcmLookUpTable"``
282 dataRef for the FGCM look-up table.
283 ``"calexps"``
284 `list` of dataRefs for the input calexps (Gen3 only)
285 ``"fgcmPhotoCalibs"``
286 `dict` of output photoCalib dataRefs. Key is
287 (tract, visit, detector). (Gen3 only)
288 Present if doZeropointOutput is True.
289 ``"fgcmTransmissionAtmospheres"``
290 `dict` of output atmosphere transmission dataRefs.
291 Key is (tract, visit). (Gen3 only)
292 Present if doAtmosphereOutput is True.
293 tract : `int`
294 Tract number
295 buildStarsRefObjLoader : `lsst.meas.algorithms.ReferenceObjectLoader`, optional
296 Reference object loader object for fgcmBuildStars.
297 returnCatalogs : `bool`, optional
298 Return photoCalibs as per-visit exposure catalogs.
299 butler : `lsst.daf.persistence.Butler`, optional
300 Gen2 butler used for reference star outputs
302 Returns
303 -------
304 outstruct : `lsst.pipe.base.Struct`
305 Output structure with keys:
307 offsets : `np.ndarray`
308 Final reference offsets, per band.
309 repeatability : `np.ndarray`
310 Raw fgcm repeatability for bright stars, per band.
311 atmospheres : `generator` [(`int`, `lsst.afw.image.TransmissionCurve`)]
312 Generator that returns (visit, transmissionCurve) tuples.
313 photoCalibs : `generator` [(`int`, `int`, `str`, `lsst.afw.image.PhotoCalib`)]
314 Generator that returns (visit, ccd, filtername, photoCalib) tuples.
315 (returned if returnCatalogs is False).
316 photoCalibCatalogs : `generator` [(`int`, `lsst.afw.table.ExposureCatalog`)]
317 Generator that returns (visit, exposureCatalog) tuples.
318 (returned if returnCatalogs is True).
319 """
320 self.log.info("Running on tract %d", (tract))
322 # Compute the aperture radius if necessary. This is useful to do now before
323 # any heavy lifting has happened (fail early).
324 calibFluxApertureRadius = None
325 if self.config.fgcmBuildStars.doSubtractLocalBackground:
326 try:
327 field = self.config.fgcmBuildStars.instFluxField
328 calibFluxApertureRadius = computeApertureRadiusFromDataRef(dataRefDict['source_catalogs'][0],
329 field)
330 except RuntimeError:
331 raise RuntimeError("Could not determine aperture radius from %s. "
332 "Cannot use doSubtractLocalBackground." %
333 (field))
335 # Run the build stars tasks
337 # Note that we will need visitCat at the end of the procedure for the outputs
338 if isinstance(butler, dafPersist.Butler):
339 # Gen2
340 groupedDataRefs = self.fgcmBuildStars._findAndGroupDataRefsGen2(butler, dataRefDict['camera'],
341 dataRefDict['source_catalogs'])
342 else:
343 # Gen3
344 groupedDataRefs = self.fgcmBuildStars._groupDataRefs(dataRefDict['sourceTableDataRefDict'],
345 dataRefDict['visitSummaryDataRefDict'])
346 visitCat = self.fgcmBuildStars.fgcmMakeVisitCatalog(dataRefDict['camera'], groupedDataRefs)
347 rad = calibFluxApertureRadius
348 fgcmStarObservationCat = self.fgcmBuildStars.fgcmMakeAllStarObservations(groupedDataRefs,
349 visitCat,
350 dataRefDict['sourceSchema'],
351 dataRefDict['camera'],
352 calibFluxApertureRadius=rad)
354 if self.fgcmBuildStars.config.doReferenceMatches:
355 lutDataRef = dataRefDict['fgcmLookUpTable']
356 if buildStarsRefObjLoader is not None:
357 self.fgcmBuildStars.makeSubtask("fgcmLoadReferenceCatalog",
358 refObjLoader=buildStarsRefObjLoader)
359 else:
360 self.fgcmBuildStars.makeSubtask("fgcmLoadReferenceCatalog", butler=butler)
361 else:
362 lutDataRef = None
364 fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat = \
365 self.fgcmBuildStars.fgcmMatchStars(visitCat,
366 fgcmStarObservationCat,
367 lutDataRef=lutDataRef)
369 # Load the LUT
370 lutCat = dataRefDict['fgcmLookUpTable'].get()
371 fgcmLut, lutIndexVals, lutStd = translateFgcmLut(lutCat,
372 dict(self.config.fgcmFitCycle.physicalFilterMap))
373 del lutCat
375 # Translate the visit catalog into fgcm format
376 fgcmExpInfo = translateVisitCatalog(visitCat)
378 configDict = makeConfigDict(self.config.fgcmFitCycle, self.log, dataRefDict['camera'],
379 self.config.fgcmFitCycle.maxIterBeforeFinalCycle,
380 True, False, lutIndexVals[0]['FILTERNAMES'],
381 tract=tract)
383 # Use the first orientation.
384 # TODO: DM-21215 will generalize to arbitrary camera orientations
385 ccdOffsets = computeCcdOffsets(dataRefDict['camera'], fgcmExpInfo['TELROT'][0])
387 # Set up the fit cycle task
389 noFitsDict = {'lutIndex': lutIndexVals,
390 'lutStd': lutStd,
391 'expInfo': fgcmExpInfo,
392 'ccdOffsets': ccdOffsets}
394 fgcmFitCycle = fgcm.FgcmFitCycle(configDict, useFits=False,
395 noFitsDict=noFitsDict, noOutput=True)
397 # We determine the conversion from the native units (typically radians) to
398 # degrees for the first star. This allows us to treat coord_ra/coord_dec as
399 # numpy arrays rather than Angles, which would we approximately 600x slower.
400 conv = fgcmStarObservationCat[0]['ra'].asDegrees() / float(fgcmStarObservationCat[0]['ra'])
402 # To load the stars, we need an initial parameter object
403 fgcmPars = fgcm.FgcmParameters.newParsWithArrays(fgcmFitCycle.fgcmConfig,
404 fgcmLut,
405 fgcmExpInfo)
407 # Match star observations to visits
408 # Only those star observations that match visits from fgcmExpInfo['VISIT'] will
409 # actually be transferred into fgcm using the indexing below.
411 obsIndex = fgcmStarIndicesCat['obsIndex']
412 visitIndex = np.searchsorted(fgcmExpInfo['VISIT'],
413 fgcmStarObservationCat['visit'][obsIndex])
415 refMag, refMagErr = extractReferenceMags(fgcmRefCat,
416 self.config.fgcmFitCycle.bands,
417 self.config.fgcmFitCycle.physicalFilterMap)
418 refId = fgcmRefCat['fgcm_id'][:]
420 fgcmStars = fgcm.FgcmStars(fgcmFitCycle.fgcmConfig)
421 fgcmStars.loadStars(fgcmPars,
422 fgcmStarObservationCat['visit'][obsIndex],
423 fgcmStarObservationCat['ccd'][obsIndex],
424 fgcmStarObservationCat['ra'][obsIndex] * conv,
425 fgcmStarObservationCat['dec'][obsIndex] * conv,
426 fgcmStarObservationCat['instMag'][obsIndex],
427 fgcmStarObservationCat['instMagErr'][obsIndex],
428 fgcmExpInfo['FILTERNAME'][visitIndex],
429 fgcmStarIdCat['fgcm_id'][:],
430 fgcmStarIdCat['ra'][:],
431 fgcmStarIdCat['dec'][:],
432 fgcmStarIdCat['obsArrIndex'][:],
433 fgcmStarIdCat['nObs'][:],
434 obsX=fgcmStarObservationCat['x'][obsIndex],
435 obsY=fgcmStarObservationCat['y'][obsIndex],
436 obsDeltaMagBkg=fgcmStarObservationCat['deltaMagBkg'][obsIndex],
437 psfCandidate=fgcmStarObservationCat['psf_candidate'][obsIndex],
438 refID=refId,
439 refMag=refMag,
440 refMagErr=refMagErr,
441 flagID=None,
442 flagFlag=None,
443 computeNobs=True)
445 # Clear out some memory
446 del fgcmStarIdCat
447 del fgcmStarIndicesCat
448 del fgcmRefCat
450 fgcmFitCycle.setLUT(fgcmLut)
451 fgcmFitCycle.setStars(fgcmStars, fgcmPars)
453 converged = False
454 cycleNumber = 0
456 previousReservedRawRepeatability = np.zeros(fgcmPars.nBands) + 1000.0
457 previousParInfo = None
458 previousParams = None
459 previousSuperStar = None
461 while (not converged and cycleNumber < self.config.maxFitCycles):
463 fgcmFitCycle.fgcmConfig.updateCycleNumber(cycleNumber)
465 if cycleNumber > 0:
466 # Use parameters from previous cycle
467 fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(fgcmFitCycle.fgcmConfig,
468 fgcmExpInfo,
469 previousParInfo,
470 previousParams,
471 previousSuperStar)
472 # We need to reset the star magnitudes and errors for the next
473 # cycle
474 fgcmFitCycle.fgcmStars.reloadStarMagnitudes(fgcmStarObservationCat['instMag'][obsIndex],
475 fgcmStarObservationCat['instMagErr'][obsIndex])
476 fgcmFitCycle.initialCycle = False
478 fgcmFitCycle.setPars(fgcmPars)
479 fgcmFitCycle.finishSetup()
481 fgcmFitCycle.run()
483 # Grab the parameters for the next cycle
484 previousParInfo, previousParams = fgcmFitCycle.fgcmPars.parsToArrays()
485 previousSuperStar = fgcmFitCycle.fgcmPars.parSuperStarFlat.copy()
487 self.log.info("Raw repeatability after cycle number %d is:" % (cycleNumber))
488 for i, band in enumerate(fgcmFitCycle.fgcmPars.bands):
489 if not fgcmFitCycle.fgcmPars.hasExposuresInBand[i]:
490 continue
491 rep = fgcmFitCycle.fgcmPars.compReservedRawRepeatability[i] * 1000.0
492 self.log.info(" Band %s, repeatability: %.2f mmag" % (band, rep))
494 # Check for convergence
495 if np.alltrue((previousReservedRawRepeatability
496 - fgcmFitCycle.fgcmPars.compReservedRawRepeatability)
497 < self.config.convergenceTolerance):
498 self.log.info("Raw repeatability has converged after cycle number %d." % (cycleNumber))
499 converged = True
500 else:
501 fgcmFitCycle.fgcmConfig.expGrayPhotometricCut[:] = fgcmFitCycle.updatedPhotometricCut
502 fgcmFitCycle.fgcmConfig.expGrayHighCut[:] = fgcmFitCycle.updatedHighCut
503 fgcmFitCycle.fgcmConfig.precomputeSuperStarInitialCycle = False
504 fgcmFitCycle.fgcmConfig.freezeStdAtmosphere = False
505 previousReservedRawRepeatability[:] = fgcmFitCycle.fgcmPars.compReservedRawRepeatability
506 self.log.info("Setting exposure gray photometricity cuts to:")
507 for i, band in enumerate(fgcmFitCycle.fgcmPars.bands):
508 if not fgcmFitCycle.fgcmPars.hasExposuresInBand[i]:
509 continue
510 cut = fgcmFitCycle.updatedPhotometricCut[i] * 1000.0
511 self.log.info(" Band %s, photometricity cut: %.2f mmag" % (band, cut))
513 cycleNumber += 1
515 # Log warning if not converged
516 if not converged:
517 self.log.warning("Maximum number of fit cycles exceeded (%d) without convergence.", cycleNumber)
519 # Do final clean-up iteration
520 fgcmFitCycle.fgcmConfig.freezeStdAtmosphere = False
521 fgcmFitCycle.fgcmConfig.resetParameters = False
522 fgcmFitCycle.fgcmConfig.maxIter = 0
523 fgcmFitCycle.fgcmConfig.outputZeropoints = True
524 fgcmFitCycle.fgcmConfig.outputStandards = True
525 fgcmFitCycle.fgcmConfig.doPlots = self.config.doDebuggingPlots
526 fgcmFitCycle.fgcmConfig.updateCycleNumber(cycleNumber)
527 fgcmFitCycle.initialCycle = False
529 fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(fgcmFitCycle.fgcmConfig,
530 fgcmExpInfo,
531 previousParInfo,
532 previousParams,
533 previousSuperStar)
534 fgcmFitCycle.fgcmStars.reloadStarMagnitudes(fgcmStarObservationCat['instMag'][obsIndex],
535 fgcmStarObservationCat['instMagErr'][obsIndex])
536 fgcmFitCycle.setPars(fgcmPars)
537 fgcmFitCycle.finishSetup()
539 self.log.info("Running final clean-up fit cycle...")
540 fgcmFitCycle.run()
542 self.log.info("Raw repeatability after clean-up cycle is:")
543 for i, band in enumerate(fgcmFitCycle.fgcmPars.bands):
544 if not fgcmFitCycle.fgcmPars.hasExposuresInBand[i]:
545 continue
546 rep = fgcmFitCycle.fgcmPars.compReservedRawRepeatability[i] * 1000.0
547 self.log.info(" Band %s, repeatability: %.2f mmag" % (band, rep))
549 # Do the outputs. Need to keep track of tract.
551 superStarChebSize = fgcmFitCycle.fgcmZpts.zpStruct['FGCM_FZPT_SSTAR_CHEB'].shape[1]
552 zptChebSize = fgcmFitCycle.fgcmZpts.zpStruct['FGCM_FZPT_CHEB'].shape[1]
554 zptSchema = makeZptSchema(superStarChebSize, zptChebSize)
555 zptCat = makeZptCat(zptSchema, fgcmFitCycle.fgcmZpts.zpStruct)
557 atmSchema = makeAtmSchema()
558 atmCat = makeAtmCat(atmSchema, fgcmFitCycle.fgcmZpts.atmStruct)
560 stdStruct, goodBands = fgcmFitCycle.fgcmStars.retrieveStdStarCatalog(fgcmFitCycle.fgcmPars)
561 stdSchema = makeStdSchema(len(goodBands))
562 stdCat = makeStdCat(stdSchema, stdStruct, goodBands)
564 outStruct = self.fgcmOutputProducts.generateTractOutputProducts(dataRefDict,
565 tract,
566 visitCat,
567 zptCat, atmCat, stdCat,
568 self.config.fgcmBuildStars,
569 returnCatalogs=returnCatalogs,
570 butler=butler)
572 outStruct.repeatability = fgcmFitCycle.fgcmPars.compReservedRawRepeatability
574 fgcmFitCycle.freeSharedMemory()
576 return outStruct