LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
fgcmBuildStarsBase.py
Go to the documentation of this file.
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 BuildStars using src tables or sourceTable_visit tables.
22"""
23
24import abc
25
26import numpy as np
27
28import lsst.pex.config as pexConfig
29import lsst.pipe.base as pipeBase
30import lsst.afw.table as afwTable
31from lsst.daf.base import PropertyList
32from lsst.daf.base.dateTime import DateTime
33from lsst.meas.algorithms.sourceSelector import sourceSelectorRegistry
34
35from .fgcmLoadReferenceCatalog import FgcmLoadReferenceCatalogTask
36
37import fgcm
38
39REFSTARS_FORMAT_VERSION = 1
40
41__all__ = ['FgcmBuildStarsConfigBase', 'FgcmBuildStarsBaseTask']
42
43
44class FgcmBuildStarsConfigBase(pexConfig.Config):
45 """Base config for FgcmBuildStars tasks"""
46
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"),
50 dtype=str,
51 default='slot_CalibFlux_instFlux',
52 )
53 minPerBand = pexConfig.Field(
54 doc="Minimum observations per band",
55 dtype=int,
56 default=2,
57 )
58 matchRadius = pexConfig.Field(
59 doc="Match radius (arcseconds)",
60 dtype=float,
61 default=1.0,
62 )
63 isolationRadius = pexConfig.Field(
64 doc="Isolation radius (arcseconds)",
65 dtype=float,
66 default=2.0,
67 )
68 densityCutNside = pexConfig.Field(
69 doc="Density cut healpix nside",
70 dtype=int,
71 default=128,
72 )
73 densityCutMaxPerPixel = pexConfig.Field(
74 doc="Density cut number of stars per pixel",
75 dtype=int,
76 default=1000,
77 )
78 randomSeed = pexConfig.Field(
79 doc="Random seed for high density down-sampling.",
80 dtype=int,
81 default=None,
82 optional=True,
83 )
84 matchNside = pexConfig.Field(
85 doc="Healpix Nside for matching",
86 dtype=int,
87 default=4096,
88 )
89 coarseNside = pexConfig.Field(
90 doc="Healpix coarse Nside for partitioning matches",
91 dtype=int,
92 default=8,
93 )
94 physicalFilterMap = pexConfig.DictField(
95 doc="Mapping from 'physicalFilter' to band.",
96 keytype=str,
97 itemtype=str,
98 default={},
99 )
100 requiredBands = pexConfig.ListField(
101 doc="Bands required for each star",
102 dtype=str,
103 default=(),
104 )
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."),
109 dtype=str,
110 default=None
111 )
112 visitDataRefName = pexConfig.Field(
113 doc="dataRef name for the 'visit' field, usually 'visit'.",
114 dtype=str,
115 default="visit",
116 deprecated="The visitDataRefname was only used for gen2; this config will be removed after v24."
117 )
118 ccdDataRefName = pexConfig.Field(
119 doc="dataRef name for the 'ccd' field, usually 'ccd' or 'detector'.",
120 dtype=str,
121 default="ccd",
122 deprecated="The ccdDataRefname was only used for gen2; this config will be removed after v24."
123 )
124 doApplyWcsJacobian = pexConfig.Field(
125 doc="Apply the jacobian of the WCS to the star observations prior to fit?",
126 dtype=bool,
127 default=True
128 )
129 doModelErrorsWithBackground = pexConfig.Field(
130 doc="Model flux errors with background term?",
131 dtype=bool,
132 default=True
133 )
134 psfCandidateName = pexConfig.Field(
135 doc="Name of field with psf candidate flag for propagation",
136 dtype=str,
137 default="calib_psf_candidate"
138 )
139 doSubtractLocalBackground = pexConfig.Field(
140 doc=("Subtract the local background before performing calibration? "
141 "This is only supported for circular aperture calibration fluxes."),
142 dtype=bool,
143 default=False
144 )
145 localBackgroundFluxField = pexConfig.Field(
146 doc="Full name of the local background instFlux field to use.",
147 dtype=str,
148 default='base_LocalBackground_instFlux'
149 )
150 sourceSelector = sourceSelectorRegistry.makeField(
151 doc="How to select sources",
152 default="science"
153 )
154 apertureInnerInstFluxField = pexConfig.Field(
155 doc=("Full name of instFlux field that contains inner aperture "
156 "flux for aperture correction proxy"),
157 dtype=str,
158 default='base_CircularApertureFlux_12_0_instFlux'
159 )
160 apertureOuterInstFluxField = pexConfig.Field(
161 doc=("Full name of instFlux field that contains outer aperture "
162 "flux for aperture correction proxy"),
163 dtype=str,
164 default='base_CircularApertureFlux_17_0_instFlux'
165 )
166 doReferenceMatches = pexConfig.Field(
167 doc="Match reference catalog as additional constraint on calibration",
168 dtype=bool,
169 default=True,
170 )
171 fgcmLoadReferenceCatalog = pexConfig.ConfigurableField(
172 target=FgcmLoadReferenceCatalogTask,
173 doc="FGCM reference object loader",
174 )
175 nVisitsPerCheckpoint = pexConfig.Field(
176 doc="Number of visits read between checkpoints",
177 dtype=int,
178 default=500,
179 )
180
181 def setDefaults(self):
182 sourceSelector = self.sourceSelectorsourceSelector["science"]
183 sourceSelector.setDefaults()
184
185 sourceSelector.doFlags = True
186 sourceSelector.doUnresolved = True
187 sourceSelector.doSignalToNoise = True
188 sourceSelector.doIsolated = True
189
190 sourceSelector.signalToNoise.minimum = 10.0
191 sourceSelector.signalToNoise.maximum = 1000.0
192
193 # FGCM operates on unresolved sources, and this setting is
194 # appropriate for the current base_ClassificationExtendedness
195 sourceSelector.unresolved.maximum = 0.5
196
197
198class FgcmBuildStarsBaseTask(pipeBase.PipelineTask, abc.ABC):
199 """
200 Base task to build stars for FGCM global calibration
201 """
202 def __init__(self, initInputs=None, **kwargs):
203 super().__init__(**kwargs)
204
205 self.makeSubtask("sourceSelector")
206 # Only log warning and fatal errors from the sourceSelector
207 self.sourceSelector.log.setLevel(self.sourceSelector.log.WARN)
208
209 @abc.abstractmethod
210 def fgcmMakeAllStarObservations(self, groupedHandles, visitCat,
211 sourceSchema,
212 camera,
213 calibFluxApertureRadius=None):
214 """
215 Compile all good star observations from visits in visitCat.
216
217 Parameters
218 ----------
219 groupedHandles : `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
220 Dataset handles, grouped by visit.
221 visitCat : `afw.table.BaseCatalog`
222 Catalog with visit data for FGCM
223 sourceSchema : `lsst.afw.table.Schema`
224 Schema for the input src catalogs.
226 calibFluxApertureRadius : `float`, optional
227 Aperture radius for calibration flux.
228 inStarObsCat : `afw.table.BaseCatalog`
229 Input observation catalog. If this is incomplete, observations
230 will be appended from when it was cut off.
231
232 Returns
233 -------
234 fgcmStarObservations : `afw.table.BaseCatalog`
235 Full catalog of good observations.
236
237 Raises
238 ------
239 RuntimeError: Raised if doSubtractLocalBackground is True and
240 calibFluxApertureRadius is not set.
241 """
242 raise NotImplementedError("fgcmMakeAllStarObservations not implemented.")
243
244 def fgcmMakeVisitCatalog(self, camera, groupedHandles, bkgHandleDict=None):
245 """
246 Make a visit catalog with all the keys from each visit
247
248 Parameters
249 ----------
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.
256
257 Returns
258 -------
259 visitCat: `afw.table.BaseCatalog`
260 """
261
262 self.log.info("Assembling visitCatalog from %d visits", len(groupedHandles))
263
264 nCcd = len(camera)
265
266 schema = self._makeFgcmVisitSchema_makeFgcmVisitSchema(nCcd)
267
268 visitCat = afwTable.BaseCatalog(schema)
269 visitCat.reserve(len(groupedHandles))
270 visitCat.resize(len(groupedHandles))
271
272 visitCat['visit'] = list(groupedHandles.keys())
273 visitCat['used'] = 0
274 visitCat['sources_read'] = False
275
276 # No matter what, fill the catalog. This will check if it was
277 # already read.
278 self._fillVisitCatalog_fillVisitCatalog(visitCat, groupedHandles,
279 bkgHandleDict=bkgHandleDict)
280
281 return visitCat
282
283 def _fillVisitCatalog(self, visitCat, groupedHandles, bkgHandleDict=None):
284 """
285 Fill the visit catalog with visit metadata
286
287 Parameters
288 ----------
289 visitCat : `afw.table.BaseCatalog`
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`
295 for background info.
296 """
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))
300
301 handle = groupedHandles[visit][0]
302 summary = handle.get()
303
304 summaryRow = summary.find(self.config.referenceCCD)
305 if summaryRow is None:
306 # Take the first available ccd if reference isn't available
307 summaryRow = summary[0]
308
309 summaryDetector = summaryRow['id']
310 visitInfo = summaryRow.getVisitInfo()
311 physicalFilter = summaryRow['physical_filter']
312 # Compute the median psf sigma if possible
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])
318 else:
319 self.log.warning("Could not find any good summary psfSigma for visit %d", visit)
320 psfSigma = 0.0
321
322 rec = visitCat[i]
323 rec['visit'] = visit
324 rec['physicalFilter'] = physicalFilter
325 # TODO DM-26991: Use the wcs to refine the focal-plane center.
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()
333 # convert from Pa to millibar
334 # Note that I don't know if this unit will need to be per-camera config
335 rec['pmb'] = visitInfo.getWeather().getAirPressure() / 100
336 # Flag to signify if this is a "deep" field. Not currently used
337 rec['deepFlag'] = 0
338 # Relative flat scaling (1.0 means no relative scaling)
339 rec['scaling'][:] = 1.0
340 # Median delta aperture, to be measured from stars
341 rec['deltaAper'] = 0.0
342 rec['psfSigma'] = psfSigma
343
344 if self.config.doModelErrorsWithBackground:
345 # Use the same detector used from the summary.
346 bkgHandle = bkgHandleDict[(visit, summaryDetector)]
347 bgList = bkgHandle.get()
348
349 bgStats = (bg[0].getStatsImage().getImage().array
350 for bg in bgList)
351 rec['skyBackground'] = sum(np.median(bg[np.isfinite(bg)]) for bg in bgStats)
352 else:
353 rec['skyBackground'] = -1.0
354
355 rec['used'] = 1
356
357 def _makeSourceMapper(self, sourceSchema):
358 """
359 Make a schema mapper for fgcm sources
360
361 Parameters
362 ----------
363 sourceSchema: `afwTable.Schema`
364 Default source schema from the butler
365
366 Returns
367 -------
368 sourceMapper: `afwTable.schemaMapper`
369 Mapper to the FGCM source schema
370 """
371
372 # create a mapper to the preferred output
373 sourceMapper = afwTable.SchemaMapper(sourceSchema)
374
375 # map to ra/dec
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')
380 # Add the mapping if the field exists in the input catalog.
381 # If the field does not exist, simply add it (set to False).
382 # This field is not required for calibration, but is useful
383 # to collate if available.
384 try:
385 sourceMapper.addMapping(sourceSchema[self.config.psfCandidateName].asKey(),
386 'psf_candidate')
387 except LookupError:
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."))
392
393 # and add the fields we want
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")
408
409 return sourceMapper
410
411 def fgcmMatchStars(self, visitCat, obsCat, lutHandle=None):
412 """
413 Use FGCM code to match observations into unique stars.
414
415 Parameters
416 ----------
417 visitCat: `afw.table.BaseCatalog`
418 Catalog with visit data for fgcm
419 obsCat: `afw.table.BaseCatalog`
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).
423
424 Returns
425 -------
426 fgcmStarIdCat: `afw.table.BaseCatalog`
427 Catalog of unique star identifiers and index keys
428 fgcmStarIndicesCat: `afwTable.BaseCatalog`
429 Catalog of unique star indices
430 fgcmRefCat: `afw.table.BaseCatalog`
431 Catalog of matched reference stars.
432 Will be None if `config.doReferenceMatches` is False.
433 """
434 # get filter names into a numpy array...
435 # This is the type that is expected by the fgcm code
436 visitFilterNames = np.zeros(len(visitCat), dtype='a30')
437 for i in range(len(visitCat)):
438 visitFilterNames[i] = visitCat[i]['physicalFilter']
439
440 # match to put filterNames with observations
441 visitIndex = np.searchsorted(visitCat['visit'],
442 obsCat['visit'])
443
444 obsFilterNames = visitFilterNames[visitIndex]
445
446 if self.config.doReferenceMatches:
447 # Get the reference filter names, using the LUT
448 lutCat = lutHandle.get()
449
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'])}
456
457 del lutCat
458
459 referenceFilterNames = self._getReferenceFilterNames_getReferenceFilterNames(visitCat,
460 stdFilterDict,
461 stdLambdaDict)
462 self.log.info("Using the following reference filters: %s" %
463 (', '.join(referenceFilterNames)))
464
465 else:
466 # This should be an empty list
467 referenceFilterNames = []
468
469 # make the fgcm starConfig dict
470 starConfig = {'logger': self.log,
471 'useHtm': True,
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}
484
485 # initialize the FgcmMakeStars object
486 fgcmMakeStars = fgcm.FgcmMakeStars(starConfig)
487
488 # make the primary stars
489 # note that the ra/dec native Angle format is radians
490 # We determine the conversion from the native units (typically
491 # radians) to degrees for the first observation. This allows us
492 # to treate ra/dec as numpy arrays rather than Angles, which would
493 # be approximately 600x slower.
494 conv = obsCat[0]['ra'].asDegrees() / float(obsCat[0]['ra'])
495 fgcmMakeStars.makePrimaryStars(obsCat['ra'] * conv,
496 obsCat['dec'] * conv,
497 filterNameArray=obsFilterNames,
498 bandSelected=False)
499
500 # and match all the stars
501 fgcmMakeStars.makeMatchedStars(obsCat['ra'] * conv,
502 obsCat['dec'] * conv,
503 obsFilterNames)
504
505 if self.config.doReferenceMatches:
506 fgcmMakeStars.makeReferenceMatches(self.fgcmLoadReferenceCatalog)
507
508 # now persist
509
510 objSchema = self._makeFgcmObjSchema_makeFgcmObjSchema()
511
512 # make catalog and records
513 fgcmStarIdCat = afwTable.BaseCatalog(objSchema)
514 fgcmStarIdCat.reserve(fgcmMakeStars.objIndexCat.size)
515 for i in range(fgcmMakeStars.objIndexCat.size):
516 fgcmStarIdCat.addNew()
517
518 # fill the catalog
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']
524
525 obsSchema = self._makeFgcmObsSchema_makeFgcmObsSchema()
526
527 fgcmStarIndicesCat = afwTable.BaseCatalog(obsSchema)
528 fgcmStarIndicesCat.reserve(fgcmMakeStars.obsIndexCat.size)
529 for i in range(fgcmMakeStars.obsIndexCat.size):
530 fgcmStarIndicesCat.addNew()
531
532 fgcmStarIndicesCat['obsIndex'][:] = fgcmMakeStars.obsIndexCat['obsindex']
533
534 if self.config.doReferenceMatches:
535 refSchema = self._makeFgcmRefSchema_makeFgcmRefSchema(len(referenceFilterNames))
536
537 fgcmRefCat = afwTable.BaseCatalog(refSchema)
538 fgcmRefCat.reserve(fgcmMakeStars.referenceCat.size)
539
540 for i in range(fgcmMakeStars.referenceCat.size):
541 fgcmRefCat.addNew()
542
543 fgcmRefCat['fgcm_id'][:] = fgcmMakeStars.referenceCat['fgcm_id']
544 fgcmRefCat['refMag'][:, :] = fgcmMakeStars.referenceCat['refMag']
545 fgcmRefCat['refMagErr'][:, :] = fgcmMakeStars.referenceCat['refMagErr']
546
547 md = PropertyList()
548 md.set("REFSTARS_FORMAT_VERSION", REFSTARS_FORMAT_VERSION)
549 md.set("FILTERNAMES", referenceFilterNames)
550 fgcmRefCat.setMetadata(md)
551
552 else:
553 fgcmRefCat = None
554
555 return fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat
556
557 def _makeFgcmVisitSchema(self, nCcd):
558 """
559 Make a schema for an fgcmVisitCatalog
560
561 Parameters
562 ----------
563 nCcd: `int`
564 Number of CCDs in the camera
565
566 Returns
567 -------
568 schema: `afwTable.Schema`
569 """
570
571 schema = afwTable.Schema()
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)")
584 # the following field is not used yet
585 schema.addField('deepFlag', type=np.int32, doc="Deep observation")
586 schema.addField('scaling', type='ArrayD', doc="Scaling applied due to flat adjustment",
587 size=nCcd)
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.")
590
591 return schema
592
593 def _makeFgcmObjSchema(self):
594 """
595 Make a schema for the objIndexCat from fgcmMakeStars
596
597 Returns
598 -------
599 schema: `afwTable.Schema`
600 """
601
602 objSchema = afwTable.Schema()
603 objSchema.addField('fgcm_id', type=np.int32, doc='FGCM Unique ID')
604 # Will investigate making these angles...
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')
610
611 return objSchema
612
613 def _makeFgcmObsSchema(self):
614 """
615 Make a schema for the obsIndexCat from fgcmMakeStars
616
617 Returns
618 -------
619 schema: `afwTable.Schema`
620 """
621
622 obsSchema = afwTable.Schema()
623 obsSchema.addField('obsIndex', type=np.int32, doc='Index in observation table')
624
625 return obsSchema
626
627 def _makeFgcmRefSchema(self, nReferenceBands):
628 """
629 Make a schema for the referenceCat from fgcmMakeStars
630
631 Parameters
632 ----------
633 nReferenceBands: `int`
634 Number of reference bands
635
636 Returns
637 -------
638 schema: `afwTable.Schema`
639 """
640
641 refSchema = afwTable.Schema()
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)
647
648 return refSchema
649
650 def _getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict):
651 """
652 Get the reference filter names, in wavelength order, from the visitCat and
653 information from the look-up-table.
654
655 Parameters
656 ----------
657 visitCat: `afw.table.BaseCatalog`
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
663
664 Returns
665 -------
666 referenceFilterNames: `list`
667 Wavelength-ordered list of reference filter names
668 """
669
670 # Find the unique list of filter names in visitCat
671 filterNames = np.unique(visitCat.asAstropy()['physicalFilter'])
672
673 # Find the unique list of "standard" filters
674 stdFilterNames = {stdFilterDict[filterName] for filterName in filterNames}
675
676 # And sort these by wavelength
677 referenceFilterNames = sorted(stdFilterNames, key=stdLambdaDict.get)
678
679 return referenceFilterNames
An immutable representation of a camera.
Definition: Camera.h:43
Defines the fields and offsets for a table.
Definition: Schema.h:51
A mapping between the keys of two Schemas, used to copy data between them.
Definition: SchemaMapper.h:21
Class for storing ordered metadata with comments.
Definition: PropertyList.h:68
def _fillVisitCatalog(self, visitCat, groupedHandles, bkgHandleDict=None)
def _getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict)
def fgcmMatchStars(self, visitCat, obsCat, lutHandle=None)
def fgcmMakeAllStarObservations(self, groupedHandles, visitCat, sourceSchema, camera, calibFluxApertureRadius=None)
def fgcmMakeVisitCatalog(self, camera, groupedHandles, bkgHandleDict=None)
daf::base::PropertyList * list
Definition: fits.cc:913