21 """Base class for BuildStars using src tables or sourceTable_visit tables.
31 import lsst.pex.config
as pexConfig
32 import lsst.pipe.base
as pipeBase
33 import lsst.afw.table
as afwTable
34 import lsst.geom
as geom
35 from lsst.daf.base
import PropertyList
36 from lsst.daf.base.dateTime
import DateTime
37 from lsst.meas.algorithms.sourceSelector
import sourceSelectorRegistry
39 from .utilities
import computeApertureRadiusFromDataRef
40 from .fgcmLoadReferenceCatalog
import FgcmLoadReferenceCatalogTask
44 REFSTARS_FORMAT_VERSION = 1
46 __all__ = [
'FgcmBuildStarsConfigBase',
'FgcmBuildStarsRunner',
'FgcmBuildStarsBaseTask']
50 """Base config for FgcmBuildStars tasks"""
52 instFluxField = pexConfig.Field(
53 doc=(
"Faull name of the source instFlux field to use, including 'instFlux'. "
54 "The associated flag will be implicitly included in badFlags"),
56 default=
'slot_CalibFlux_instFlux',
58 minPerBand = pexConfig.Field(
59 doc=
"Minimum observations per band",
63 matchRadius = pexConfig.Field(
64 doc=
"Match radius (arcseconds)",
68 isolationRadius = pexConfig.Field(
69 doc=
"Isolation radius (arcseconds)",
73 densityCutNside = pexConfig.Field(
74 doc=
"Density cut healpix nside",
78 densityCutMaxPerPixel = pexConfig.Field(
79 doc=
"Density cut number of stars per pixel",
83 matchNside = pexConfig.Field(
84 doc=
"Healpix Nside for matching",
88 coarseNside = pexConfig.Field(
89 doc=
"Healpix coarse Nside for partitioning matches",
93 filterMap = pexConfig.DictField(
94 doc=
"Mapping from 'filterName' to band.",
99 requiredBands = pexConfig.ListField(
100 doc=
"Bands required for each star",
104 primaryBands = pexConfig.ListField(
105 doc=(
"Bands for 'primary' star matches. "
106 "A star must be observed in one of these bands to be considered "
107 "as a calibration star."),
111 visitDataRefName = pexConfig.Field(
112 doc=
"dataRef name for the 'visit' field, usually 'visit'.",
116 ccdDataRefName = pexConfig.Field(
117 doc=
"dataRef name for the 'ccd' field, usually 'ccd' or 'detector'.",
121 doApplyWcsJacobian = pexConfig.Field(
122 doc=
"Apply the jacobian of the WCS to the star observations prior to fit?",
126 psfCandidateName = pexConfig.Field(
127 doc=
"Name of field with psf candidate flag for propagation",
129 default=
"calib_psf_candidate"
131 doSubtractLocalBackground = pexConfig.Field(
132 doc=(
"Subtract the local background before performing calibration? "
133 "This is only supported for circular aperture calibration fluxes."),
137 localBackgroundFluxField = pexConfig.Field(
138 doc=
"Full name of the local background instFlux field to use.",
140 default=
'base_LocalBackground_instFlux'
142 sourceSelector = sourceSelectorRegistry.makeField(
143 doc=
"How to select sources",
146 apertureInnerInstFluxField = pexConfig.Field(
147 doc=(
"Full name of instFlux field that contains inner aperture "
148 "flux for aperture correction proxy"),
150 default=
'base_CircularApertureFlux_12_0_instFlux'
152 apertureOuterInstFluxField = pexConfig.Field(
153 doc=(
"Full name of instFlux field that contains outer aperture "
154 "flux for aperture correction proxy"),
156 default=
'base_CircularApertureFlux_17_0_instFlux'
158 doReferenceMatches = pexConfig.Field(
159 doc=
"Match reference catalog as additional constraint on calibration",
163 fgcmLoadReferenceCatalog = pexConfig.ConfigurableField(
164 target=FgcmLoadReferenceCatalogTask,
165 doc=
"FGCM reference object loader",
167 nVisitsPerCheckpoint = pexConfig.Field(
168 doc=
"Number of visits read between checkpoints",
175 sourceSelector.setDefaults()
177 sourceSelector.doFlags =
True
178 sourceSelector.doUnresolved =
True
179 sourceSelector.doSignalToNoise =
True
180 sourceSelector.doIsolated =
True
182 sourceSelector.signalToNoise.minimum = 10.0
183 sourceSelector.signalToNoise.maximum = 1000.0
187 sourceSelector.unresolved.maximum = 0.5
191 """Subclass of TaskRunner for FgcmBuildStars tasks
193 fgcmBuildStarsTask.run() and fgcmBuildStarsTableTask.run() take a number of
194 arguments, one of which is the butler (for persistence and mapper data),
195 and a list of dataRefs extracted from the command line. Note that FGCM
196 runs on a large set of dataRefs, and not on single dataRef/tract/patch.
197 This class transforms the process arguments generated by the ArgumentParser
198 into the arguments expected by FgcmBuildStarsTask.run(). This runner does
199 not use any parallelization.
204 Return a list with one element: a tuple with the butler and
208 return [(parsedCmd.butler, parsedCmd.id.refList)]
214 args: `tuple` with (butler, dataRefList)
218 exitStatus: `list` with `lsst.pipe.base.Struct`
219 exitStatus (0: success; 1: failure)
221 butler, dataRefList = args
223 task = self.TaskClass(config=self.config, log=self.log)
227 task.runDataRef(butler, dataRefList)
230 task.runDataRef(butler, dataRefList)
231 except Exception
as e:
233 task.log.fatal(
"Failed: %s" % e)
234 if not isinstance(e, pipeBase.TaskError):
235 traceback.print_exc(file=sys.stderr)
237 task.writeMetadata(butler)
240 return [pipeBase.Struct(exitStatus=exitStatus)]
244 Run the task, with no multiprocessing
248 parsedCmd: `lsst.pipe.base.ArgumentParser` parsed command line
253 if self.precall(parsedCmd):
255 resultList = self(targetList[0])
262 Base task to build stars for FGCM global calibration
266 butler : `lsst.daf.persistence.Butler`
269 pipeBase.CmdLineTask.__init__(self, **kwargs)
270 self.makeSubtask(
"sourceSelector")
272 self.sourceSelector.log.setLevel(self.sourceSelector.log.WARN)
275 def _getMetadataName(self):
281 Cross-match and make star list for FGCM Input
285 butler: `lsst.daf.persistence.Butler`
286 dataRefs: `list` of `lsst.daf.persistence.ButlerDataRef`
287 Source data references for the input visits.
291 RuntimeErrror: Raised if `config.doReferenceMatches` is set and
292 an fgcmLookUpTable is not available, or if computeFluxApertureRadius()
293 fails if the calibFlux is not a CircularAperture flux.
295 datasetType = dataRefs[0].butlerSubset.datasetType
296 self.log.info(
"Running with %d %s dataRefs", len(dataRefs), datasetType)
298 if self.config.doReferenceMatches:
300 if not butler.datasetExists(
'fgcmLookUpTable'):
301 raise RuntimeError(
"Must have fgcmLookUpTable if using config.doReferenceMatches")
304 calibFluxApertureRadius =
None
305 if self.config.doSubtractLocalBackground:
308 self.config.instFluxField)
309 except RuntimeError
as e:
310 raise RuntimeError(
"Could not determine aperture radius from %s. "
311 "Cannot use doSubtractLocalBackground." %
312 (self.config.instFluxField))
from e
316 camera = butler.get(
'camera')
323 visitCatDataRef = butler.dataRef(
'fgcmVisitCatalog')
324 filename = visitCatDataRef.get(
'fgcmVisitCatalog_filename')[0]
325 if os.path.exists(filename):
327 inVisitCat = visitCatDataRef.get()
328 if len(inVisitCat) != len(groupedDataRefs):
329 raise RuntimeError(
"Existing visitCatalog found, but has an inconsistent "
330 "number of visits. Cannot continue.")
335 visitCatDataRef=visitCatDataRef,
336 inVisitCat=inVisitCat)
339 visitCatDataRef.put(visitCat)
341 starObsDataRef = butler.dataRef(
'fgcmStarObservations')
342 filename = starObsDataRef.get(
'fgcmStarObservations_filename')[0]
343 if os.path.exists(filename):
344 inStarObsCat = starObsDataRef.get()
348 rad = calibFluxApertureRadius
351 calibFluxApertureRadius=rad,
352 starObsDataRef=starObsDataRef,
353 visitCatDataRef=visitCatDataRef,
354 inStarObsCat=inStarObsCat)
355 visitCatDataRef.put(visitCat)
356 starObsDataRef.put(fgcmStarObservationCat)
359 fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat = self.
fgcmMatchStars(butler,
361 fgcmStarObservationCat)
364 butler.put(fgcmStarIdCat,
'fgcmStarIds')
365 butler.put(fgcmStarIndicesCat,
'fgcmStarIndices')
366 if fgcmRefCat
is not None:
367 butler.put(fgcmRefCat,
'fgcmReferenceStars')
372 Find and group dataRefs (by visit).
376 butler: `lsst.daf.persistence.Butler`
377 dataRefs: `list` of `lsst.daf.persistence.ButlerDataRef`
378 Data references for the input visits.
382 groupedDataRefs: `dict` [`int`, `list`]
383 Dictionary with visit keys, and `list`s of `lsst.daf.persistence.ButlerDataRef`
385 raise NotImplementedError(
"findAndGroupDataRefs not implemented.")
389 calibFluxApertureRadius=None,
390 visitCatDataRef=None,
394 Compile all good star observations from visits in visitCat. Checkpoint files
395 will be stored if both visitCatDataRef and starObsDataRef are not None.
399 groupedDataRefs: `dict` of `list`s
400 Lists of `lsst.daf.persistence.ButlerDataRef`, grouped by visit.
401 visitCat: `afw.table.BaseCatalog`
402 Catalog with visit data for FGCM
403 calibFluxApertureRadius: `float`, optional
404 Aperture radius for calibration flux.
405 visitCatDataRef: `lsst.daf.persistence.ButlerDataRef`, optional
406 Dataref to write visitCat for checkpoints
407 starObsDataRef: `lsst.daf.persistence.ButlerDataRef`, optional
408 Dataref to write the star observation catalog for checkpoints.
409 inStarObsCat: `afw.table.BaseCatalog`
410 Input observation catalog. If this is incomplete, observations
411 will be appended from when it was cut off.
415 fgcmStarObservations: `afw.table.BaseCatalog`
416 Full catalog of good observations.
420 RuntimeError: Raised if doSubtractLocalBackground is True and
421 calibFluxApertureRadius is not set.
423 raise NotImplementedError(
"fgcmMakeAllStarObservations not implemented.")
426 visitCatDataRef=None, inVisitCat=None):
428 Make a visit catalog with all the keys from each visit
432 camera: `lsst.afw.cameraGeom.Camera`
433 Camera from the butler
434 groupedDataRefs: `dict`
435 Dictionary with visit keys, and `list`s of
436 `lsst.daf.persistence.ButlerDataRef`
437 visitCatDataRef: `lsst.daf.persistence.ButlerDataRef`, optional
438 Dataref to write visitCat for checkpoints
439 inVisitCat: `afw.table.BaseCatalog`
440 Input (possibly incomplete) visit catalog
444 visitCat: `afw.table.BaseCatalog`
447 self.log.info(
"Assembling visitCatalog from %d %ss" %
448 (len(groupedDataRefs), self.config.visitDataRefName))
452 if inVisitCat
is None:
455 visitCat = afwTable.BaseCatalog(schema)
456 visitCat.reserve(len(groupedDataRefs))
458 for i, visit
in enumerate(sorted(groupedDataRefs)):
459 rec = visitCat.addNew()
462 rec[
'sources_read'] = 0
464 visitCat = inVisitCat
469 visitCatDataRef=visitCatDataRef)
473 def _fillVisitCatalog(self, visitCat, groupedDataRefs,
474 visitCatDataRef=None):
476 Fill the visit catalog with visit metadata
480 visitCat: `afw.table.BaseCatalog`
481 Catalog with schema from _makeFgcmVisitSchema()
482 groupedDataRefs: `dict`
483 Dictionary with visit keys, and `list`s of
484 `lsst.daf.persistence.ButlerDataRef
485 visitCatDataRef: `lsst.daf.persistence.ButlerDataRef`, optional
486 Dataref to write visitCat for checkpoints
489 bbox = geom.BoxI(geom.PointI(0, 0), geom.PointI(1, 1))
491 for i, visit
in enumerate(sorted(groupedDataRefs)):
498 if visitCat[
'used'][i]:
501 if (i % self.config.nVisitsPerCheckpoint) == 0:
502 self.log.info(
"Retrieving metadata for %s %d (%d/%d)" %
503 (self.config.visitDataRefName, visit, i, len(groupedDataRefs)))
505 if visitCatDataRef
is not None:
506 visitCatDataRef.put(visitCat)
511 dataRef = groupedDataRefs[visit][0]
513 exp = dataRef.get(datasetType=
'calexp_sub', bbox=bbox,
514 flags=afwTable.SOURCE_IO_NO_FOOTPRINTS)
516 visitInfo = exp.getInfo().getVisitInfo()
522 rec[
'filtername'] = f.getName()
526 radec = visitInfo.getBoresightRaDec()
527 rec[
'telra'] = radec.getRa().asDegrees()
528 rec[
'teldec'] = radec.getDec().asDegrees()
529 rec[
'telha'] = visitInfo.getBoresightHourAngle().asDegrees()
530 rec[
'telrot'] = visitInfo.getBoresightRotAngle().asDegrees()
531 rec[
'mjd'] = visitInfo.getDate().get(system=DateTime.MJD)
532 rec[
'exptime'] = visitInfo.getExposureTime()
535 rec[
'pmb'] = visitInfo.getWeather().getAirPressure() / 100
539 rec[
'scaling'][:] = 1.0
541 rec[
'deltaAper'] = 0.0
543 rec[
'psfSigma'] = psf.computeShape().getDeterminantRadius()
545 if dataRef.datasetExists(datasetType=
'calexpBackground'):
548 bgStats = (bg[0].getStatsImage().getImage().array
549 for bg
in dataRef.get(datasetType=
'calexpBackground'))
550 rec[
'skyBackground'] = sum(np.median(bg[np.isfinite(bg)])
for bg
in bgStats)
552 self.log.warn(
'Sky background not found for visit %d / ccd %d' %
553 (visit, dataRef.dataId[self.config.ccdDataRefName]))
554 rec[
'skyBackground'] = -1.0
558 def _makeSourceMapper(self, sourceSchema):
560 Make a schema mapper for fgcm sources
564 sourceSchema: `afwTable.Schema`
565 Default source schema from the butler
569 sourceMapper: `afwTable.schemaMapper`
570 Mapper to the FGCM source schema
574 sourceMapper = afwTable.SchemaMapper(sourceSchema)
577 sourceMapper.addMapping(sourceSchema[
'coord_ra'].asKey(),
'ra')
578 sourceMapper.addMapping(sourceSchema[
'coord_dec'].asKey(),
'dec')
579 sourceMapper.addMapping(sourceSchema[
'slot_Centroid_x'].asKey(),
'x')
580 sourceMapper.addMapping(sourceSchema[
'slot_Centroid_y'].asKey(),
'y')
586 sourceMapper.addMapping(sourceSchema[self.config.psfCandidateName].asKey(),
589 sourceMapper.editOutputSchema().addField(
590 "psf_candidate", type=
'Flag',
591 doc=(
"Flag set if the source was a candidate for PSF determination, "
592 "as determined by the star selector."))
595 sourceMapper.editOutputSchema().addField(
596 "visit", type=np.int32, doc=
"Visit number")
597 sourceMapper.editOutputSchema().addField(
598 "ccd", type=np.int32, doc=
"CCD number")
599 sourceMapper.editOutputSchema().addField(
600 "instMag", type=np.float32, doc=
"Instrumental magnitude")
601 sourceMapper.editOutputSchema().addField(
602 "instMagErr", type=np.float32, doc=
"Instrumental magnitude error")
603 sourceMapper.editOutputSchema().addField(
604 "jacobian", type=np.float32, doc=
"Relative pixel scale from wcs jacobian")
605 sourceMapper.editOutputSchema().addField(
606 "deltaMagBkg", type=np.float32, doc=
"Change in magnitude due to local background offset")
612 Use FGCM code to match observations into unique stars.
616 butler: `lsst.daf.persistence.Butler`
617 visitCat: `afw.table.BaseCatalog`
618 Catalog with visit data for fgcm
619 obsCat: `afw.table.BaseCatalog`
620 Full catalog of star observations for fgcm
624 fgcmStarIdCat: `afw.table.BaseCatalog`
625 Catalog of unique star identifiers and index keys
626 fgcmStarIndicesCat: `afwTable.BaseCatalog`
627 Catalog of unique star indices
628 fgcmRefCat: `afw.table.BaseCatalog`
629 Catalog of matched reference stars.
630 Will be None if `config.doReferenceMatches` is False.
633 if self.config.doReferenceMatches:
635 self.makeSubtask(
"fgcmLoadReferenceCatalog", butler=butler)
639 visitFilterNames = np.zeros(len(visitCat), dtype=
'a10')
640 for i
in range(len(visitCat)):
641 visitFilterNames[i] = visitCat[i][
'filtername']
644 visitIndex = np.searchsorted(visitCat[
'visit'],
647 obsFilterNames = visitFilterNames[visitIndex]
649 if self.config.doReferenceMatches:
651 lutCat = butler.get(
'fgcmLookUpTable')
653 stdFilterDict = {filterName: stdFilter
for (filterName, stdFilter)
in
654 zip(lutCat[0][
'filterNames'].split(
','),
655 lutCat[0][
'stdFilterNames'].split(
','))}
656 stdLambdaDict = {stdFilter: stdLambda
for (stdFilter, stdLambda)
in
657 zip(lutCat[0][
'stdFilterNames'].split(
','),
658 lutCat[0][
'lambdaStdFilter'])}
665 self.log.info(
"Using the following reference filters: %s" %
666 (
', '.join(referenceFilterNames)))
670 referenceFilterNames = []
674 starConfig = {
'logger': self.log,
675 'filterToBand': self.config.filterMap,
676 'requiredBands': self.config.requiredBands,
677 'minPerBand': self.config.minPerBand,
678 'matchRadius': self.config.matchRadius,
679 'isolationRadius': self.config.isolationRadius,
680 'matchNSide': self.config.matchNside,
681 'coarseNSide': self.config.coarseNside,
682 'densNSide': self.config.densityCutNside,
683 'densMaxPerPixel': self.config.densityCutMaxPerPixel,
684 'primaryBands': self.config.primaryBands,
685 'referenceFilterNames': referenceFilterNames}
688 fgcmMakeStars = fgcm.FgcmMakeStars(starConfig)
696 conv = obsCat[0][
'ra'].asDegrees() / float(obsCat[0][
'ra'])
697 fgcmMakeStars.makePrimaryStars(obsCat[
'ra'] * conv,
698 obsCat[
'dec'] * conv,
699 filterNameArray=obsFilterNames,
703 fgcmMakeStars.makeMatchedStars(obsCat[
'ra'] * conv,
704 obsCat[
'dec'] * conv,
707 if self.config.doReferenceMatches:
708 fgcmMakeStars.makeReferenceMatches(self.fgcmLoadReferenceCatalog)
715 fgcmStarIdCat = afwTable.BaseCatalog(objSchema)
716 fgcmStarIdCat.reserve(fgcmMakeStars.objIndexCat.size)
717 for i
in range(fgcmMakeStars.objIndexCat.size):
718 fgcmStarIdCat.addNew()
721 fgcmStarIdCat[
'fgcm_id'][:] = fgcmMakeStars.objIndexCat[
'fgcm_id']
722 fgcmStarIdCat[
'ra'][:] = fgcmMakeStars.objIndexCat[
'ra']
723 fgcmStarIdCat[
'dec'][:] = fgcmMakeStars.objIndexCat[
'dec']
724 fgcmStarIdCat[
'obsArrIndex'][:] = fgcmMakeStars.objIndexCat[
'obsarrindex']
725 fgcmStarIdCat[
'nObs'][:] = fgcmMakeStars.objIndexCat[
'nobs']
729 fgcmStarIndicesCat = afwTable.BaseCatalog(obsSchema)
730 fgcmStarIndicesCat.reserve(fgcmMakeStars.obsIndexCat.size)
731 for i
in range(fgcmMakeStars.obsIndexCat.size):
732 fgcmStarIndicesCat.addNew()
734 fgcmStarIndicesCat[
'obsIndex'][:] = fgcmMakeStars.obsIndexCat[
'obsindex']
736 if self.config.doReferenceMatches:
739 fgcmRefCat = afwTable.BaseCatalog(refSchema)
740 fgcmRefCat.reserve(fgcmMakeStars.referenceCat.size)
742 for i
in range(fgcmMakeStars.referenceCat.size):
745 fgcmRefCat[
'fgcm_id'][:] = fgcmMakeStars.referenceCat[
'fgcm_id']
746 fgcmRefCat[
'refMag'][:, :] = fgcmMakeStars.referenceCat[
'refMag']
747 fgcmRefCat[
'refMagErr'][:, :] = fgcmMakeStars.referenceCat[
'refMagErr']
750 md.set(
"REFSTARS_FORMAT_VERSION", REFSTARS_FORMAT_VERSION)
751 md.set(
"FILTERNAMES", referenceFilterNames)
752 fgcmRefCat.setMetadata(md)
757 return fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat
759 def _makeFgcmVisitSchema(self, nCcd):
761 Make a schema for an fgcmVisitCatalog
766 Number of CCDs in the camera
770 schema: `afwTable.Schema`
773 schema = afwTable.Schema()
774 schema.addField(
'visit', type=np.int32, doc=
"Visit number")
776 schema.addField(
'filtername', type=str, size=10, doc=
"Filter name")
777 schema.addField(
'telra', type=np.float64, doc=
"Pointing RA (deg)")
778 schema.addField(
'teldec', type=np.float64, doc=
"Pointing Dec (deg)")
779 schema.addField(
'telha', type=np.float64, doc=
"Pointing Hour Angle (deg)")
780 schema.addField(
'telrot', type=np.float64, doc=
"Camera rotation (deg)")
781 schema.addField(
'mjd', type=np.float64, doc=
"MJD of visit")
782 schema.addField(
'exptime', type=np.float32, doc=
"Exposure time")
783 schema.addField(
'pmb', type=np.float32, doc=
"Pressure (millibar)")
784 schema.addField(
'psfSigma', type=np.float32, doc=
"PSF sigma (reference CCD)")
785 schema.addField(
'deltaAper', type=np.float32, doc=
"Delta-aperture")
786 schema.addField(
'skyBackground', type=np.float32, doc=
"Sky background (ADU) (reference CCD)")
788 schema.addField(
'deepFlag', type=np.int32, doc=
"Deep observation")
789 schema.addField(
'scaling', type=
'ArrayD', doc=
"Scaling applied due to flat adjustment",
791 schema.addField(
'used', type=np.int32, doc=
"This visit has been ingested.")
792 schema.addField(
'sources_read', type=
'Flag', doc=
"This visit had sources read.")
796 def _makeFgcmObjSchema(self):
798 Make a schema for the objIndexCat from fgcmMakeStars
802 schema: `afwTable.Schema`
805 objSchema = afwTable.Schema()
806 objSchema.addField(
'fgcm_id', type=np.int32, doc=
'FGCM Unique ID')
808 objSchema.addField(
'ra', type=np.float64, doc=
'Mean object RA (deg)')
809 objSchema.addField(
'dec', type=np.float64, doc=
'Mean object Dec (deg)')
810 objSchema.addField(
'obsArrIndex', type=np.int32,
811 doc=
'Index in obsIndexTable for first observation')
812 objSchema.addField(
'nObs', type=np.int32, doc=
'Total number of observations')
816 def _makeFgcmObsSchema(self):
818 Make a schema for the obsIndexCat from fgcmMakeStars
822 schema: `afwTable.Schema`
825 obsSchema = afwTable.Schema()
826 obsSchema.addField(
'obsIndex', type=np.int32, doc=
'Index in observation table')
830 def _makeFgcmRefSchema(self, nReferenceBands):
832 Make a schema for the referenceCat from fgcmMakeStars
836 nReferenceBands: `int`
837 Number of reference bands
841 schema: `afwTable.Schema`
844 refSchema = afwTable.Schema()
845 refSchema.addField(
'fgcm_id', type=np.int32, doc=
'FGCM Unique ID')
846 refSchema.addField(
'refMag', type=
'ArrayF', doc=
'Reference magnitude array (AB)',
847 size=nReferenceBands)
848 refSchema.addField(
'refMagErr', type=
'ArrayF', doc=
'Reference magnitude error array',
849 size=nReferenceBands)
853 def _getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict):
855 Get the reference filter names, in wavelength order, from the visitCat and
856 information from the look-up-table.
860 visitCat: `afw.table.BaseCatalog`
861 Catalog with visit data for FGCM
862 stdFilterDict: `dict`
863 Mapping of filterName to stdFilterName from LUT
864 stdLambdaDict: `dict`
865 Mapping of stdFilterName to stdLambda from LUT
869 referenceFilterNames: `list`
870 Wavelength-ordered list of reference filter names
874 filterNames = np.unique(visitCat.asAstropy()[
'filtername'])
877 stdFilterNames = {stdFilterDict[filterName]
for filterName
in filterNames}
880 referenceFilterNames = sorted(stdFilterNames, key=stdLambdaDict.get)
882 return referenceFilterNames