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 doApplyWcsJacobian = pexConfig.Field(
113 doc=
"Apply the jacobian of the WCS to the star observations prior to fit?",
117 doModelErrorsWithBackground = pexConfig.Field(
118 doc=
"Model flux errors with background term?",
122 psfCandidateName = pexConfig.Field(
123 doc=
"Name of field with psf candidate flag for propagation",
125 default=
"calib_psf_candidate"
127 doSubtractLocalBackground = pexConfig.Field(
128 doc=(
"Subtract the local background before performing calibration? "
129 "This is only supported for circular aperture calibration fluxes."),
133 localBackgroundFluxField = pexConfig.Field(
134 doc=
"Full name of the local background instFlux field to use.",
136 default=
'base_LocalBackground_instFlux'
138 sourceSelector = sourceSelectorRegistry.makeField(
139 doc=
"How to select sources",
142 apertureInnerInstFluxField = pexConfig.Field(
143 doc=(
"Full name of instFlux field that contains inner aperture "
144 "flux for aperture correction proxy"),
146 default=
'base_CircularApertureFlux_12_0_instFlux'
148 apertureOuterInstFluxField = pexConfig.Field(
149 doc=(
"Full name of instFlux field that contains outer aperture "
150 "flux for aperture correction proxy"),
152 default=
'base_CircularApertureFlux_17_0_instFlux'
154 doReferenceMatches = pexConfig.Field(
155 doc=
"Match reference catalog as additional constraint on calibration",
159 fgcmLoadReferenceCatalog = pexConfig.ConfigurableField(
160 target=FgcmLoadReferenceCatalogTask,
161 doc=
"FGCM reference object loader",
163 nVisitsPerCheckpoint = pexConfig.Field(
164 doc=
"Number of visits read between checkpoints",
171 sourceSelector.setDefaults()
173 sourceSelector.doFlags =
True
174 sourceSelector.doUnresolved =
True
175 sourceSelector.doSignalToNoise =
True
176 sourceSelector.doIsolated =
True
177 sourceSelector.doRequireFiniteRaDec =
True
179 sourceSelector.signalToNoise.minimum = 10.0
180 sourceSelector.signalToNoise.maximum = 1000.0
184 sourceSelector.unresolved.maximum = 0.5
189 Base task to build stars for FGCM
global calibration
191 def __init__(self, initInputs=None, **kwargs):
192 super().__init__(**kwargs)
194 self.makeSubtask(
"sourceSelector")
196 self.sourceSelector.log.setLevel(self.sourceSelector.log.WARN)
202 calibFluxApertureRadius=None):
204 Compile all good star observations from visits
in visitCat.
208 groupedHandles : `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
209 Dataset handles, grouped by visit.
211 Catalog
with visit data
for FGCM
213 Schema
for the input src catalogs.
215 calibFluxApertureRadius : `float`, optional
216 Aperture radius
for calibration flux.
218 Input observation catalog. If this
is incomplete, observations
219 will be appended
from when it was cut off.
224 Full catalog of good observations.
228 RuntimeError: Raised
if doSubtractLocalBackground
is True and
229 calibFluxApertureRadius
is not set.
231 raise NotImplementedError(
"fgcmMakeAllStarObservations not implemented.")
235 Make a visit catalog with all the keys
from each visit
240 Camera
from the butler
241 groupedHandles: `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
242 Dataset handles, grouped by visit.
243 bkgHandleDict: `dict`, optional
244 Dictionary of `lsst.daf.butler.DeferredDatasetHandle`
for background info.
251 self.log.info("Assembling visitCatalog from %d visits", len(groupedHandles))
258 visitCat.reserve(len(groupedHandles))
259 visitCat.resize(len(groupedHandles))
261 visitCat[
'visit'] =
list(groupedHandles.keys())
263 visitCat[
'sources_read'] =
False
268 bkgHandleDict=bkgHandleDict)
272 def _fillVisitCatalog(self, visitCat, groupedHandles, bkgHandleDict=None):
274 Fill the visit catalog with visit metadata
279 Visit catalog. See _makeFgcmVisitSchema()
for schema definition.
280 groupedHandles : `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
281 Dataset handles, grouped by visit.
282 bkgHandleDict : `dict`, optional
283 Dictionary of `lsst.daf.butler.DeferredDatasetHandle`
286 for i, visit
in enumerate(groupedHandles):
287 if (i % self.config.nVisitsPerCheckpoint) == 0:
288 self.log.info(
"Retrieving metadata for visit %d (%d/%d)", visit, i, len(groupedHandles))
290 handle = groupedHandles[visit][0]
291 summary = handle.get()
293 summaryRow = summary.find(self.config.referenceCCD)
294 if summaryRow
is None:
296 summaryRow = summary[0]
298 summaryDetector = summaryRow[
'id']
299 visitInfo = summaryRow.getVisitInfo()
300 physicalFilter = summaryRow[
'physical_filter']
302 goodSigma, = np.where(summary[
'psfSigma'] > 0)
303 if goodSigma.size > 2:
304 psfSigma = np.median(summary[
'psfSigma'][goodSigma])
305 elif goodSigma.size > 0:
306 psfSigma = np.mean(summary[
'psfSigma'][goodSigma])
308 self.log.warning(
"Could not find any good summary psfSigma for visit %d", visit)
313 rec[
'physicalFilter'] = physicalFilter
315 radec = visitInfo.getBoresightRaDec()
316 rec[
'telra'] = radec.getRa().asDegrees()
317 rec[
'teldec'] = radec.getDec().asDegrees()
318 rec[
'telha'] = visitInfo.getBoresightHourAngle().asDegrees()
319 rec[
'telrot'] = visitInfo.getBoresightRotAngle().asDegrees()
320 rec[
'mjd'] = visitInfo.getDate().get(system=DateTime.MJD)
321 rec[
'exptime'] = visitInfo.getExposureTime()
324 rec[
'pmb'] = visitInfo.getWeather().getAirPressure() / 100
328 rec[
'scaling'][:] = 1.0
330 rec[
'deltaAper'] = 0.0
331 rec[
'psfSigma'] = psfSigma
333 if self.config.doModelErrorsWithBackground:
335 bkgHandle = bkgHandleDict[(visit, summaryDetector)]
336 bgList = bkgHandle.get()
338 bgStats = (bg[0].getStatsImage().getImage().array
340 rec[
'skyBackground'] = sum(np.median(bg[np.isfinite(bg)])
for bg
in bgStats)
342 rec[
'skyBackground'] = -1.0
346 def _makeSourceMapper(self, sourceSchema):
348 Make a schema mapper for fgcm sources
353 Default source schema
from the butler
357 sourceMapper: `afwTable.schemaMapper`
358 Mapper to the FGCM source schema
365 sourceMapper.addMapping(sourceSchema[
'coord_ra'].asKey(),
'ra')
366 sourceMapper.addMapping(sourceSchema[
'coord_dec'].asKey(),
'dec')
367 sourceMapper.addMapping(sourceSchema[
'slot_Centroid_x'].asKey(),
'x')
368 sourceMapper.addMapping(sourceSchema[
'slot_Centroid_y'].asKey(),
'y')
374 sourceMapper.addMapping(sourceSchema[self.config.psfCandidateName].asKey(),
377 sourceMapper.editOutputSchema().addField(
378 "psf_candidate", type=
'Flag',
379 doc=(
"Flag set if the source was a candidate for PSF determination, "
380 "as determined by the star selector."))
383 sourceMapper.editOutputSchema().addField(
384 "visit", type=np.int64, doc=
"Visit number")
385 sourceMapper.editOutputSchema().addField(
386 "ccd", type=np.int32, doc=
"CCD number")
387 sourceMapper.editOutputSchema().addField(
388 "instMag", type=np.float32, doc=
"Instrumental magnitude")
389 sourceMapper.editOutputSchema().addField(
390 "instMagErr", type=np.float32, doc=
"Instrumental magnitude error")
391 sourceMapper.editOutputSchema().addField(
392 "jacobian", type=np.float32, doc=
"Relative pixel scale from wcs jacobian")
393 sourceMapper.editOutputSchema().addField(
394 "deltaMagBkg", type=np.float32, doc=
"Change in magnitude due to local background offset")
395 sourceMapper.editOutputSchema().addField(
396 "deltaMagAper", type=np.float32, doc=
"Change in magnitude from larger to smaller aperture")
402 Use FGCM code to match observations into unique stars.
407 Catalog with visit data
for fgcm
409 Full catalog of star observations
for fgcm
410 lutHandle: `lsst.daf.butler.DeferredDatasetHandle`, optional
411 Data reference to fgcm look-up table (used
if matching reference stars).
416 Catalog of unique star identifiers
and index keys
418 Catalog of unique star indices
420 Catalog of matched reference stars.
421 Will be
None if `config.doReferenceMatches`
is False.
425 visitFilterNames = np.zeros(len(visitCat), dtype=
'a30')
426 for i
in range(len(visitCat)):
427 visitFilterNames[i] = visitCat[i][
'physicalFilter']
430 visitIndex = np.searchsorted(visitCat[
'visit'],
433 obsFilterNames = visitFilterNames[visitIndex]
435 if self.config.doReferenceMatches:
437 lutCat = lutHandle.get()
439 stdFilterDict = {filterName: stdFilter
for (filterName, stdFilter)
in
440 zip(lutCat[0][
'physicalFilters'].split(
','),
441 lutCat[0][
'stdPhysicalFilters'].split(
','))}
442 stdLambdaDict = {stdFilter: stdLambda
for (stdFilter, stdLambda)
in
443 zip(lutCat[0][
'stdPhysicalFilters'].split(
','),
444 lutCat[0][
'lambdaStdFilter'])}
451 self.log.info(
"Using the following reference filters: %s" %
452 (
', '.join(referenceFilterNames)))
456 referenceFilterNames = []
459 starConfig = {
'logger': self.log,
461 'filterToBand': self.config.physicalFilterMap,
462 'requiredBands': self.config.requiredBands,
463 'minPerBand': self.config.minPerBand,
464 'matchRadius': self.config.matchRadius,
465 'isolationRadius': self.config.isolationRadius,
466 'matchNSide': self.config.matchNside,
467 'coarseNSide': self.config.coarseNside,
468 'densNSide': self.config.densityCutNside,
469 'densMaxPerPixel': self.config.densityCutMaxPerPixel,
470 'randomSeed': self.config.randomSeed,
471 'primaryBands': self.config.primaryBands,
472 'referenceFilterNames': referenceFilterNames}
475 fgcmMakeStars = fgcm.FgcmMakeStars(starConfig)
483 conv = obsCat[0][
'ra'].asDegrees() / float(obsCat[0][
'ra'])
484 fgcmMakeStars.makePrimaryStars(obsCat[
'ra'] * conv,
485 obsCat[
'dec'] * conv,
486 filterNameArray=obsFilterNames,
490 fgcmMakeStars.makeMatchedStars(obsCat[
'ra'] * conv,
491 obsCat[
'dec'] * conv,
494 if self.config.doReferenceMatches:
495 fgcmMakeStars.makeReferenceMatches(self.fgcmLoadReferenceCatalog)
503 fgcmStarIdCat.reserve(fgcmMakeStars.objIndexCat.size)
504 for i
in range(fgcmMakeStars.objIndexCat.size):
505 fgcmStarIdCat.addNew()
508 fgcmStarIdCat[
'fgcm_id'][:] = fgcmMakeStars.objIndexCat[
'fgcm_id']
509 fgcmStarIdCat[
'ra'][:] = fgcmMakeStars.objIndexCat[
'ra']
510 fgcmStarIdCat[
'dec'][:] = fgcmMakeStars.objIndexCat[
'dec']
511 fgcmStarIdCat[
'obsArrIndex'][:] = fgcmMakeStars.objIndexCat[
'obsarrindex']
512 fgcmStarIdCat[
'nObs'][:] = fgcmMakeStars.objIndexCat[
'nobs']
517 fgcmStarIndicesCat.reserve(fgcmMakeStars.obsIndexCat.size)
518 for i
in range(fgcmMakeStars.obsIndexCat.size):
519 fgcmStarIndicesCat.addNew()
521 fgcmStarIndicesCat[
'obsIndex'][:] = fgcmMakeStars.obsIndexCat[
'obsindex']
523 if self.config.doReferenceMatches:
527 fgcmRefCat.reserve(fgcmMakeStars.referenceCat.size)
529 for i
in range(fgcmMakeStars.referenceCat.size):
532 fgcmRefCat[
'fgcm_id'][:] = fgcmMakeStars.referenceCat[
'fgcm_id']
533 fgcmRefCat[
'refMag'][:, :] = fgcmMakeStars.referenceCat[
'refMag']
534 fgcmRefCat[
'refMagErr'][:, :] = fgcmMakeStars.referenceCat[
'refMagErr']
537 md.set(
"REFSTARS_FORMAT_VERSION", REFSTARS_FORMAT_VERSION)
538 md.set(
"FILTERNAMES", referenceFilterNames)
539 fgcmRefCat.setMetadata(md)
544 return fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat
546 def _makeFgcmVisitSchema(self, nCcd):
548 Make a schema for an fgcmVisitCatalog
553 Number of CCDs
in the camera
561 schema.addField('visit', type=np.int64, doc=
"Visit number")
562 schema.addField(
'physicalFilter', type=str, size=30, doc=
"Physical filter")
563 schema.addField(
'telra', type=np.float64, doc=
"Pointing RA (deg)")
564 schema.addField(
'teldec', type=np.float64, doc=
"Pointing Dec (deg)")
565 schema.addField(
'telha', type=np.float64, doc=
"Pointing Hour Angle (deg)")
566 schema.addField(
'telrot', type=np.float64, doc=
"Camera rotation (deg)")
567 schema.addField(
'mjd', type=np.float64, doc=
"MJD of visit")
568 schema.addField(
'exptime', type=np.float32, doc=
"Exposure time")
569 schema.addField(
'pmb', type=np.float32, doc=
"Pressure (millibar)")
570 schema.addField(
'psfSigma', type=np.float32, doc=
"PSF sigma (reference CCD)")
571 schema.addField(
'deltaAper', type=np.float32, doc=
"Delta-aperture")
572 schema.addField(
'skyBackground', type=np.float32, doc=
"Sky background (ADU) (reference CCD)")
574 schema.addField(
'deepFlag', type=np.int32, doc=
"Deep observation")
575 schema.addField(
'scaling', type=
'ArrayD', doc=
"Scaling applied due to flat adjustment",
577 schema.addField(
'used', type=np.int32, doc=
"This visit has been ingested.")
578 schema.addField(
'sources_read', type=
'Flag', doc=
"This visit had sources read.")
582 def _makeFgcmObjSchema(self):
584 Make a schema for the objIndexCat
from fgcmMakeStars
592 objSchema.addField('fgcm_id', type=np.int32, doc=
'FGCM Unique ID')
594 objSchema.addField(
'ra', type=np.float64, doc=
'Mean object RA (deg)')
595 objSchema.addField(
'dec', type=np.float64, doc=
'Mean object Dec (deg)')
596 objSchema.addField(
'obsArrIndex', type=np.int32,
597 doc=
'Index in obsIndexTable for first observation')
598 objSchema.addField(
'nObs', type=np.int32, doc=
'Total number of observations')
602 def _makeFgcmObsSchema(self):
604 Make a schema for the obsIndexCat
from fgcmMakeStars
612 obsSchema.addField('obsIndex', type=np.int32, doc=
'Index in observation table')
616 def _makeFgcmRefSchema(self, nReferenceBands):
618 Make a schema for the referenceCat
from fgcmMakeStars
622 nReferenceBands: `int`
623 Number of reference bands
631 refSchema.addField('fgcm_id', type=np.int32, doc=
'FGCM Unique ID')
632 refSchema.addField(
'refMag', type=
'ArrayF', doc=
'Reference magnitude array (AB)',
633 size=nReferenceBands)
634 refSchema.addField(
'refMagErr', type=
'ArrayF', doc=
'Reference magnitude error array',
635 size=nReferenceBands)
639 def _getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict):
641 Get the reference filter names, in wavelength order,
from the visitCat
and
642 information
from the look-up-table.
647 Catalog
with visit data
for FGCM
648 stdFilterDict: `dict`
649 Mapping of filterName to stdFilterName
from LUT
650 stdLambdaDict: `dict`
651 Mapping of stdFilterName to stdLambda
from LUT
655 referenceFilterNames: `list`
656 Wavelength-ordered list of reference filter names
660 filterNames = np.unique(visitCat.asAstropy()[
'physicalFilter'])
663 stdFilterNames = {stdFilterDict[filterName]
for filterName
in filterNames}
666 referenceFilterNames = sorted(stdFilterNames, key=stdLambdaDict.get)
668 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 _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