21"""Base class for BuildStars using src tables or sourceTable_visit tables.
35from .fgcmLoadReferenceCatalog
import FgcmLoadReferenceCatalogTask
39REFSTARS_FORMAT_VERSION = 1
41__all__ = [
'FgcmBuildStarsConfigBase',
'FgcmBuildStarsBaseTask']
45 """Base config for FgcmBuildStars tasks"""
47 instFluxField = pexConfig.Field(
48 doc=(
"Faull name of the source instFlux field to use, including 'instFlux'. "
49 "The associated flag will be implicitly included in badFlags"),
51 default=
'slot_CalibFlux_instFlux',
53 minPerBand = pexConfig.Field(
54 doc=
"Minimum observations per band",
58 matchRadius = pexConfig.Field(
59 doc=
"Match radius (arcseconds)",
63 isolationRadius = pexConfig.Field(
64 doc=
"Isolation radius (arcseconds)",
68 densityCutNside = pexConfig.Field(
69 doc=
"Density cut healpix nside",
73 densityCutMaxPerPixel = pexConfig.Field(
74 doc=
"Density cut number of stars per pixel",
78 randomSeed = pexConfig.Field(
79 doc=
"Random seed for high density down-sampling.",
84 matchNside = pexConfig.Field(
85 doc=
"Healpix Nside for matching",
89 coarseNside = pexConfig.Field(
90 doc=
"Healpix coarse Nside for partitioning matches",
94 physicalFilterMap = pexConfig.DictField(
95 doc=
"Mapping from 'physicalFilter' to band.",
100 requiredBands = pexConfig.ListField(
101 doc=
"Bands required for each star",
105 primaryBands = pexConfig.ListField(
106 doc=(
"Bands for 'primary' star matches. "
107 "A star must be observed in one of these bands to be considered "
108 "as a calibration star."),
112 visitDataRefName = pexConfig.Field(
113 doc=
"dataRef name for the 'visit' field, usually 'visit'.",
116 deprecated=
"The visitDataRefname was only used for gen2; this config will be removed after v24."
118 ccdDataRefName = pexConfig.Field(
119 doc=
"dataRef name for the 'ccd' field, usually 'ccd' or 'detector'.",
122 deprecated=
"The ccdDataRefname was only used for gen2; this config will be removed after v24."
124 doApplyWcsJacobian = pexConfig.Field(
125 doc=
"Apply the jacobian of the WCS to the star observations prior to fit?",
129 doModelErrorsWithBackground = pexConfig.Field(
130 doc=
"Model flux errors with background term?",
134 psfCandidateName = pexConfig.Field(
135 doc=
"Name of field with psf candidate flag for propagation",
137 default=
"calib_psf_candidate"
139 doSubtractLocalBackground = pexConfig.Field(
140 doc=(
"Subtract the local background before performing calibration? "
141 "This is only supported for circular aperture calibration fluxes."),
145 localBackgroundFluxField = pexConfig.Field(
146 doc=
"Full name of the local background instFlux field to use.",
148 default=
'base_LocalBackground_instFlux'
150 sourceSelector = sourceSelectorRegistry.makeField(
151 doc=
"How to select sources",
154 apertureInnerInstFluxField = pexConfig.Field(
155 doc=(
"Full name of instFlux field that contains inner aperture "
156 "flux for aperture correction proxy"),
158 default=
'base_CircularApertureFlux_12_0_instFlux'
160 apertureOuterInstFluxField = pexConfig.Field(
161 doc=(
"Full name of instFlux field that contains outer aperture "
162 "flux for aperture correction proxy"),
164 default=
'base_CircularApertureFlux_17_0_instFlux'
166 doReferenceMatches = pexConfig.Field(
167 doc=
"Match reference catalog as additional constraint on calibration",
171 fgcmLoadReferenceCatalog = pexConfig.ConfigurableField(
172 target=FgcmLoadReferenceCatalogTask,
173 doc=
"FGCM reference object loader",
175 nVisitsPerCheckpoint = pexConfig.Field(
176 doc=
"Number of visits read between checkpoints",
183 sourceSelector.setDefaults()
185 sourceSelector.doFlags =
True
186 sourceSelector.doUnresolved =
True
187 sourceSelector.doSignalToNoise =
True
188 sourceSelector.doIsolated =
True
190 sourceSelector.signalToNoise.minimum = 10.0
191 sourceSelector.signalToNoise.maximum = 1000.0
195 sourceSelector.unresolved.maximum = 0.5
200 Base task to build stars for FGCM
global calibration
205 self.makeSubtask(
"sourceSelector")
207 self.sourceSelector.log.setLevel(self.sourceSelector.log.WARN)
213 calibFluxApertureRadius=None):
215 Compile all good star observations from visits
in visitCat.
219 groupedHandles : `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
220 Dataset handles, grouped by visit.
222 Catalog
with visit data
for FGCM
224 Schema
for the input src catalogs.
226 calibFluxApertureRadius : `float`, optional
227 Aperture radius
for calibration flux.
229 Input observation catalog. If this
is incomplete, observations
230 will be appended
from when it was cut off.
235 Full catalog of good observations.
239 RuntimeError: Raised
if doSubtractLocalBackground
is True and
240 calibFluxApertureRadius
is not set.
242 raise NotImplementedError(
"fgcmMakeAllStarObservations not implemented.")
246 Make a visit catalog with all the keys
from each visit
251 Camera
from the butler
252 groupedHandles: `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
253 Dataset handles, grouped by visit.
254 bkgHandleDict: `dict`, optional
255 Dictionary of `lsst.daf.butler.DeferredDatasetHandle`
for background info.
262 self.log.info("Assembling visitCatalog from %d visits", len(groupedHandles))
269 visitCat.reserve(len(groupedHandles))
270 visitCat.resize(len(groupedHandles))
272 visitCat[
'visit'] =
list(groupedHandles.keys())
274 visitCat[
'sources_read'] =
False
279 bkgHandleDict=bkgHandleDict)
283 def _fillVisitCatalog(self, visitCat, groupedHandles, bkgHandleDict=None):
285 Fill the visit catalog with visit metadata
290 Visit catalog. See _makeFgcmVisitSchema()
for schema definition.
291 groupedHandles : `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
292 Dataset handles, grouped by visit.
293 bkgHandleDict : `dict`, optional
294 Dictionary of `lsst.daf.butler.DeferredDatasetHandle`
297 for i, visit
in enumerate(groupedHandles):
298 if (i % self.config.nVisitsPerCheckpoint) == 0:
299 self.log.
info(
"Retrieving metadata for visit %d (%d/%d)", visit, i, len(groupedHandles))
301 handle = groupedHandles[visit][0]
302 summary = handle.get()
304 summaryRow = summary.find(self.config.referenceCCD)
305 if summaryRow
is None:
307 summaryRow = summary[0]
309 summaryDetector = summaryRow[
'id']
310 visitInfo = summaryRow.getVisitInfo()
311 physicalFilter = summaryRow[
'physical_filter']
313 goodSigma, = np.where(summary[
'psfSigma'] > 0)
314 if goodSigma.size > 2:
315 psfSigma = np.median(summary[
'psfSigma'][goodSigma])
316 elif goodSigma.size > 0:
317 psfSigma = np.mean(summary[
'psfSigma'][goodSigma])
319 self.log.
warning(
"Could not find any good summary psfSigma for visit %d", visit)
324 rec[
'physicalFilter'] = physicalFilter
326 radec = visitInfo.getBoresightRaDec()
327 rec[
'telra'] = radec.getRa().asDegrees()
328 rec[
'teldec'] = radec.getDec().asDegrees()
329 rec[
'telha'] = visitInfo.getBoresightHourAngle().asDegrees()
330 rec[
'telrot'] = visitInfo.getBoresightRotAngle().asDegrees()
331 rec[
'mjd'] = visitInfo.getDate().get(system=DateTime.MJD)
332 rec[
'exptime'] = visitInfo.getExposureTime()
335 rec[
'pmb'] = visitInfo.getWeather().getAirPressure() / 100
339 rec[
'scaling'][:] = 1.0
341 rec[
'deltaAper'] = 0.0
342 rec[
'psfSigma'] = psfSigma
344 if self.config.doModelErrorsWithBackground:
346 bkgHandle = bkgHandleDict[(visit, summaryDetector)]
347 bgList = bkgHandle.get()
349 bgStats = (bg[0].getStatsImage().getImage().array
351 rec[
'skyBackground'] = sum(np.median(bg[np.isfinite(bg)])
for bg
in bgStats)
353 rec[
'skyBackground'] = -1.0
357 def _makeSourceMapper(self, sourceSchema):
359 Make a schema mapper for fgcm sources
364 Default source schema
from the butler
368 sourceMapper: `afwTable.schemaMapper`
369 Mapper to the FGCM source schema
376 sourceMapper.addMapping(sourceSchema[
'coord_ra'].asKey(),
'ra')
377 sourceMapper.addMapping(sourceSchema[
'coord_dec'].asKey(),
'dec')
378 sourceMapper.addMapping(sourceSchema[
'slot_Centroid_x'].asKey(),
'x')
379 sourceMapper.addMapping(sourceSchema[
'slot_Centroid_y'].asKey(),
'y')
385 sourceMapper.addMapping(sourceSchema[self.config.psfCandidateName].asKey(),
388 sourceMapper.editOutputSchema().addField(
389 "psf_candidate", type=
'Flag',
390 doc=(
"Flag set if the source was a candidate for PSF determination, "
391 "as determined by the star selector."))
394 sourceMapper.editOutputSchema().addField(
395 "visit", type=np.int64, doc=
"Visit number")
396 sourceMapper.editOutputSchema().addField(
397 "ccd", type=np.int32, doc=
"CCD number")
398 sourceMapper.editOutputSchema().addField(
399 "instMag", type=np.float32, doc=
"Instrumental magnitude")
400 sourceMapper.editOutputSchema().addField(
401 "instMagErr", type=np.float32, doc=
"Instrumental magnitude error")
402 sourceMapper.editOutputSchema().addField(
403 "jacobian", type=np.float32, doc=
"Relative pixel scale from wcs jacobian")
404 sourceMapper.editOutputSchema().addField(
405 "deltaMagBkg", type=np.float32, doc=
"Change in magnitude due to local background offset")
406 sourceMapper.editOutputSchema().addField(
407 "deltaMagAper", type=np.float32, doc=
"Change in magnitude from larger to smaller aperture")
413 Use FGCM code to match observations into unique stars.
418 Catalog with visit data
for fgcm
420 Full catalog of star observations
for fgcm
421 lutHandle: `lsst.daf.butler.DeferredDatasetHandle`, optional
422 Data reference to fgcm look-up table (used
if matching reference stars).
427 Catalog of unique star identifiers
and index keys
429 Catalog of unique star indices
431 Catalog of matched reference stars.
432 Will be
None if `config.doReferenceMatches`
is False.
436 visitFilterNames = np.zeros(len(visitCat), dtype=
'a30')
437 for i
in range(len(visitCat)):
438 visitFilterNames[i] = visitCat[i][
'physicalFilter']
441 visitIndex = np.searchsorted(visitCat[
'visit'],
444 obsFilterNames = visitFilterNames[visitIndex]
446 if self.config.doReferenceMatches:
448 lutCat = lutHandle.get()
450 stdFilterDict = {filterName: stdFilter
for (filterName, stdFilter)
in
451 zip(lutCat[0][
'physicalFilters'].split(
','),
452 lutCat[0][
'stdPhysicalFilters'].split(
','))}
453 stdLambdaDict = {stdFilter: stdLambda
for (stdFilter, stdLambda)
in
454 zip(lutCat[0][
'stdPhysicalFilters'].split(
','),
455 lutCat[0][
'lambdaStdFilter'])}
462 self.log.
info(
"Using the following reference filters: %s" %
463 (
', '.join(referenceFilterNames)))
467 referenceFilterNames = []
470 starConfig = {
'logger': self.log,
472 'filterToBand': self.config.physicalFilterMap,
473 'requiredBands': self.config.requiredBands,
474 'minPerBand': self.config.minPerBand,
475 'matchRadius': self.config.matchRadius,
476 'isolationRadius': self.config.isolationRadius,
477 'matchNSide': self.config.matchNside,
478 'coarseNSide': self.config.coarseNside,
479 'densNSide': self.config.densityCutNside,
480 'densMaxPerPixel': self.config.densityCutMaxPerPixel,
481 'randomSeed': self.config.randomSeed,
482 'primaryBands': self.config.primaryBands,
483 'referenceFilterNames': referenceFilterNames}
486 fgcmMakeStars = fgcm.FgcmMakeStars(starConfig)
494 conv = obsCat[0][
'ra'].asDegrees() /
float(obsCat[0][
'ra'])
495 fgcmMakeStars.makePrimaryStars(obsCat[
'ra'] * conv,
496 obsCat[
'dec'] * conv,
497 filterNameArray=obsFilterNames,
501 fgcmMakeStars.makeMatchedStars(obsCat[
'ra'] * conv,
502 obsCat[
'dec'] * conv,
505 if self.config.doReferenceMatches:
506 fgcmMakeStars.makeReferenceMatches(self.fgcmLoadReferenceCatalog)
514 fgcmStarIdCat.reserve(fgcmMakeStars.objIndexCat.size)
515 for i
in range(fgcmMakeStars.objIndexCat.size):
516 fgcmStarIdCat.addNew()
519 fgcmStarIdCat[
'fgcm_id'][:] = fgcmMakeStars.objIndexCat[
'fgcm_id']
520 fgcmStarIdCat[
'ra'][:] = fgcmMakeStars.objIndexCat[
'ra']
521 fgcmStarIdCat[
'dec'][:] = fgcmMakeStars.objIndexCat[
'dec']
522 fgcmStarIdCat[
'obsArrIndex'][:] = fgcmMakeStars.objIndexCat[
'obsarrindex']
523 fgcmStarIdCat[
'nObs'][:] = fgcmMakeStars.objIndexCat[
'nobs']
528 fgcmStarIndicesCat.reserve(fgcmMakeStars.obsIndexCat.size)
529 for i
in range(fgcmMakeStars.obsIndexCat.size):
530 fgcmStarIndicesCat.addNew()
532 fgcmStarIndicesCat[
'obsIndex'][:] = fgcmMakeStars.obsIndexCat[
'obsindex']
534 if self.config.doReferenceMatches:
538 fgcmRefCat.reserve(fgcmMakeStars.referenceCat.size)
540 for i
in range(fgcmMakeStars.referenceCat.size):
543 fgcmRefCat[
'fgcm_id'][:] = fgcmMakeStars.referenceCat[
'fgcm_id']
544 fgcmRefCat[
'refMag'][:, :] = fgcmMakeStars.referenceCat[
'refMag']
545 fgcmRefCat[
'refMagErr'][:, :] = fgcmMakeStars.referenceCat[
'refMagErr']
548 md.set(
"REFSTARS_FORMAT_VERSION", REFSTARS_FORMAT_VERSION)
549 md.set(
"FILTERNAMES", referenceFilterNames)
550 fgcmRefCat.setMetadata(md)
555 return fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat
557 def _makeFgcmVisitSchema(self, nCcd):
559 Make a schema for an fgcmVisitCatalog
564 Number of CCDs
in the camera
572 schema.addField('visit', type=np.int64, doc=
"Visit number")
573 schema.addField(
'physicalFilter', type=str, size=30, doc=
"Physical filter")
574 schema.addField(
'telra', type=np.float64, doc=
"Pointing RA (deg)")
575 schema.addField(
'teldec', type=np.float64, doc=
"Pointing Dec (deg)")
576 schema.addField(
'telha', type=np.float64, doc=
"Pointing Hour Angle (deg)")
577 schema.addField(
'telrot', type=np.float64, doc=
"Camera rotation (deg)")
578 schema.addField(
'mjd', type=np.float64, doc=
"MJD of visit")
579 schema.addField(
'exptime', type=np.float32, doc=
"Exposure time")
580 schema.addField(
'pmb', type=np.float32, doc=
"Pressure (millibar)")
581 schema.addField(
'psfSigma', type=np.float32, doc=
"PSF sigma (reference CCD)")
582 schema.addField(
'deltaAper', type=np.float32, doc=
"Delta-aperture")
583 schema.addField(
'skyBackground', type=np.float32, doc=
"Sky background (ADU) (reference CCD)")
585 schema.addField(
'deepFlag', type=np.int32, doc=
"Deep observation")
586 schema.addField(
'scaling', type=
'ArrayD', doc=
"Scaling applied due to flat adjustment",
588 schema.addField(
'used', type=np.int32, doc=
"This visit has been ingested.")
589 schema.addField(
'sources_read', type=
'Flag', doc=
"This visit had sources read.")
593 def _makeFgcmObjSchema(self):
595 Make a schema for the objIndexCat
from fgcmMakeStars
603 objSchema.addField('fgcm_id', type=np.int32, doc=
'FGCM Unique ID')
605 objSchema.addField(
'ra', type=np.float64, doc=
'Mean object RA (deg)')
606 objSchema.addField(
'dec', type=np.float64, doc=
'Mean object Dec (deg)')
607 objSchema.addField(
'obsArrIndex', type=np.int32,
608 doc=
'Index in obsIndexTable for first observation')
609 objSchema.addField(
'nObs', type=np.int32, doc=
'Total number of observations')
613 def _makeFgcmObsSchema(self):
615 Make a schema for the obsIndexCat
from fgcmMakeStars
623 obsSchema.addField('obsIndex', type=np.int32, doc=
'Index in observation table')
627 def _makeFgcmRefSchema(self, nReferenceBands):
629 Make a schema for the referenceCat
from fgcmMakeStars
633 nReferenceBands: `int`
634 Number of reference bands
642 refSchema.addField('fgcm_id', type=np.int32, doc=
'FGCM Unique ID')
643 refSchema.addField(
'refMag', type=
'ArrayF', doc=
'Reference magnitude array (AB)',
644 size=nReferenceBands)
645 refSchema.addField(
'refMagErr', type=
'ArrayF', doc=
'Reference magnitude error array',
646 size=nReferenceBands)
650 def _getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict):
652 Get the reference filter names, in wavelength order,
from the visitCat
and
653 information
from the look-up-table.
658 Catalog
with visit data
for FGCM
659 stdFilterDict: `dict`
660 Mapping of filterName to stdFilterName
from LUT
661 stdLambdaDict: `dict`
662 Mapping of stdFilterName to stdLambda
from LUT
666 referenceFilterNames: `list`
667 Wavelength-ordered list of reference filter names
671 filterNames = np.unique(visitCat.asAstropy()[
'physicalFilter'])
674 stdFilterNames = {stdFilterDict[filterName]
for filterName
in filterNames}
677 referenceFilterNames = sorted(stdFilterNames, key=stdLambdaDict.get)
679 return referenceFilterNames
An immutable representation of a camera.
Defines the fields and offsets for a table.
A mapping between the keys of two Schemas, used to copy data between them.
Class for storing ordered metadata with comments.
def _fillVisitCatalog(self, visitCat, groupedHandles, bkgHandleDict=None)
def __init__(self, initInputs=None, **kwargs)
def _makeFgcmVisitSchema(self, nCcd)
def _getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict)
def _makeFgcmObjSchema(self)
def fgcmMatchStars(self, visitCat, obsCat, lutHandle=None)
def _makeFgcmObsSchema(self)
def fgcmMakeAllStarObservations(self, groupedHandles, visitCat, sourceSchema, camera, calibFluxApertureRadius=None)
def fgcmMakeVisitCatalog(self, camera, groupedHandles, bkgHandleDict=None)
def _makeFgcmRefSchema(self, nReferenceBands)
daf::base::PropertyList * list