LSST Applications g070148d5b3+33e5256705,g0d53e28543+25c8b88941,g0da5cf3356+2dd1178308,g1081da9e2a+62d12e78cb,g17e5ecfddb+7e422d6136,g1c76d35bf8+ede3a706f7,g295839609d+225697d880,g2e2c1a68ba+cc1f6f037e,g2ffcdf413f+853cd4dcde,g38293774b4+62d12e78cb,g3b44f30a73+d953f1ac34,g48ccf36440+885b902d19,g4b2f1765b6+7dedbde6d2,g5320a0a9f6+0c5d6105b6,g56b687f8c9+ede3a706f7,g5c4744a4d9+ef6ac23297,g5ffd174ac0+0c5d6105b6,g6075d09f38+66af417445,g667d525e37+2ced63db88,g670421136f+2ced63db88,g71f27ac40c+2ced63db88,g774830318a+463cbe8d1f,g7876bc68e5+1d137996f1,g7985c39107+62d12e78cb,g7fdac2220c+0fd8241c05,g96f01af41f+368e6903a7,g9ca82378b8+2ced63db88,g9d27549199+ef6ac23297,gabe93b2c52+e3573e3735,gb065e2a02a+3dfbe639da,gbc3249ced9+0c5d6105b6,gbec6a3398f+0c5d6105b6,gc9534b9d65+35b9f25267,gd01420fc67+0c5d6105b6,geee7ff78d7+a14128c129,gf63283c776+ede3a706f7,gfed783d017+0c5d6105b6,w.2022.47
LSST Data Management Base Package
Loading...
Searching...
No Matches
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 doApplyWcsJacobian = pexConfig.Field(
113 doc="Apply the jacobian of the WCS to the star observations prior to fit?",
114 dtype=bool,
115 default=True
116 )
117 doModelErrorsWithBackground = pexConfig.Field(
118 doc="Model flux errors with background term?",
119 dtype=bool,
120 default=True
121 )
122 psfCandidateName = pexConfig.Field(
123 doc="Name of field with psf candidate flag for propagation",
124 dtype=str,
125 default="calib_psf_candidate"
126 )
127 doSubtractLocalBackground = pexConfig.Field(
128 doc=("Subtract the local background before performing calibration? "
129 "This is only supported for circular aperture calibration fluxes."),
130 dtype=bool,
131 default=False
132 )
133 localBackgroundFluxField = pexConfig.Field(
134 doc="Full name of the local background instFlux field to use.",
135 dtype=str,
136 default='base_LocalBackground_instFlux'
137 )
138 sourceSelector = sourceSelectorRegistry.makeField(
139 doc="How to select sources",
140 default="science"
141 )
142 apertureInnerInstFluxField = pexConfig.Field(
143 doc=("Full name of instFlux field that contains inner aperture "
144 "flux for aperture correction proxy"),
145 dtype=str,
146 default='base_CircularApertureFlux_12_0_instFlux'
147 )
148 apertureOuterInstFluxField = pexConfig.Field(
149 doc=("Full name of instFlux field that contains outer aperture "
150 "flux for aperture correction proxy"),
151 dtype=str,
152 default='base_CircularApertureFlux_17_0_instFlux'
153 )
154 doReferenceMatches = pexConfig.Field(
155 doc="Match reference catalog as additional constraint on calibration",
156 dtype=bool,
157 default=True,
158 )
159 fgcmLoadReferenceCatalog = pexConfig.ConfigurableField(
160 target=FgcmLoadReferenceCatalogTask,
161 doc="FGCM reference object loader",
162 )
163 nVisitsPerCheckpoint = pexConfig.Field(
164 doc="Number of visits read between checkpoints",
165 dtype=int,
166 default=500,
167 )
168
169 def setDefaults(self):
170 sourceSelector = self.sourceSelector["science"]
171 sourceSelector.setDefaults()
172
173 sourceSelector.doFlags = True
174 sourceSelector.doUnresolved = True
175 sourceSelector.doSignalToNoise = True
176 sourceSelector.doIsolated = True
177 sourceSelector.doRequireFiniteRaDec = True
178
179 sourceSelector.signalToNoise.minimum = 10.0
180 sourceSelector.signalToNoise.maximum = 1000.0
181
182 # FGCM operates on unresolved sources, and this setting is
183 # appropriate for the current base_ClassificationExtendedness
184 sourceSelector.unresolved.maximum = 0.5
185
186
187class FgcmBuildStarsBaseTask(pipeBase.PipelineTask, abc.ABC):
188 """
189 Base task to build stars for FGCM global calibration
190 """
191 def __init__(self, initInputs=None, **kwargs):
192 super().__init__(**kwargs)
193
194 self.makeSubtask("sourceSelector")
195 # Only log warning and fatal errors from the sourceSelector
196 self.sourceSelector.log.setLevel(self.sourceSelector.log.WARN)
197
198 @abc.abstractmethod
199 def fgcmMakeAllStarObservations(self, groupedHandles, visitCat,
200 sourceSchema,
201 camera,
202 calibFluxApertureRadius=None):
203 """
204 Compile all good star observations from visits in visitCat.
205
206 Parameters
207 ----------
208 groupedHandles : `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
209 Dataset handles, grouped by visit.
210 visitCat : `afw.table.BaseCatalog`
211 Catalog with visit data for FGCM
212 sourceSchema : `lsst.afw.table.Schema`
213 Schema for the input src catalogs.
215 calibFluxApertureRadius : `float`, optional
216 Aperture radius for calibration flux.
217 inStarObsCat : `afw.table.BaseCatalog`
218 Input observation catalog. If this is incomplete, observations
219 will be appended from when it was cut off.
220
221 Returns
222 -------
223 fgcmStarObservations : `afw.table.BaseCatalog`
224 Full catalog of good observations.
225
226 Raises
227 ------
228 RuntimeError: Raised if doSubtractLocalBackground is True and
229 calibFluxApertureRadius is not set.
230 """
231 raise NotImplementedError("fgcmMakeAllStarObservations not implemented.")
232
233 def fgcmMakeVisitCatalog(self, camera, groupedHandles, bkgHandleDict=None):
234 """
235 Make a visit catalog with all the keys from each visit
236
237 Parameters
238 ----------
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.
245
246 Returns
247 -------
248 visitCat: `afw.table.BaseCatalog`
249 """
250
251 self.log.info("Assembling visitCatalog from %d visits", len(groupedHandles))
252
253 nCcd = len(camera)
254
255 schema = self._makeFgcmVisitSchema(nCcd)
256
257 visitCat = afwTable.BaseCatalog(schema)
258 visitCat.reserve(len(groupedHandles))
259 visitCat.resize(len(groupedHandles))
260
261 visitCat['visit'] = list(groupedHandles.keys())
262 visitCat['used'] = 0
263 visitCat['sources_read'] = False
264
265 # No matter what, fill the catalog. This will check if it was
266 # already read.
267 self._fillVisitCatalog(visitCat, groupedHandles,
268 bkgHandleDict=bkgHandleDict)
269
270 return visitCat
271
272 def _fillVisitCatalog(self, visitCat, groupedHandles, bkgHandleDict=None):
273 """
274 Fill the visit catalog with visit metadata
275
276 Parameters
277 ----------
278 visitCat : `afw.table.BaseCatalog`
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`
284 for background info.
285 """
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))
289
290 handle = groupedHandles[visit][0]
291 summary = handle.get()
292
293 summaryRow = summary.find(self.config.referenceCCD)
294 if summaryRow is None:
295 # Take the first available ccd if reference isn't available
296 summaryRow = summary[0]
297
298 summaryDetector = summaryRow['id']
299 visitInfo = summaryRow.getVisitInfo()
300 physicalFilter = summaryRow['physical_filter']
301 # Compute the median psf sigma if possible
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])
307 else:
308 self.log.warning("Could not find any good summary psfSigma for visit %d", visit)
309 psfSigma = 0.0
310
311 rec = visitCat[i]
312 rec['visit'] = visit
313 rec['physicalFilter'] = physicalFilter
314 # TODO DM-26991: Use the wcs to refine the focal-plane center.
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()
322 # convert from Pa to millibar
323 # Note that I don't know if this unit will need to be per-camera config
324 rec['pmb'] = visitInfo.getWeather().getAirPressure() / 100
325 # Flag to signify if this is a "deep" field. Not currently used
326 rec['deepFlag'] = 0
327 # Relative flat scaling (1.0 means no relative scaling)
328 rec['scaling'][:] = 1.0
329 # Median delta aperture, to be measured from stars
330 rec['deltaAper'] = 0.0
331 rec['psfSigma'] = psfSigma
332
333 if self.config.doModelErrorsWithBackground:
334 # Use the same detector used from the summary.
335 bkgHandle = bkgHandleDict[(visit, summaryDetector)]
336 bgList = bkgHandle.get()
337
338 bgStats = (bg[0].getStatsImage().getImage().array
339 for bg in bgList)
340 rec['skyBackground'] = sum(np.median(bg[np.isfinite(bg)]) for bg in bgStats)
341 else:
342 rec['skyBackground'] = -1.0
343
344 rec['used'] = 1
345
346 def _makeSourceMapper(self, sourceSchema):
347 """
348 Make a schema mapper for fgcm sources
349
350 Parameters
351 ----------
352 sourceSchema: `afwTable.Schema`
353 Default source schema from the butler
354
355 Returns
356 -------
357 sourceMapper: `afwTable.schemaMapper`
358 Mapper to the FGCM source schema
359 """
360
361 # create a mapper to the preferred output
362 sourceMapper = afwTable.SchemaMapper(sourceSchema)
363
364 # map to ra/dec
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')
369 # Add the mapping if the field exists in the input catalog.
370 # If the field does not exist, simply add it (set to False).
371 # This field is not required for calibration, but is useful
372 # to collate if available.
373 try:
374 sourceMapper.addMapping(sourceSchema[self.config.psfCandidateName].asKey(),
375 'psf_candidate')
376 except LookupError:
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."))
381
382 # and add the fields we want
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")
397
398 return sourceMapper
399
400 def fgcmMatchStars(self, visitCat, obsCat, lutHandle=None):
401 """
402 Use FGCM code to match observations into unique stars.
403
404 Parameters
405 ----------
406 visitCat: `afw.table.BaseCatalog`
407 Catalog with visit data for fgcm
408 obsCat: `afw.table.BaseCatalog`
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).
412
413 Returns
414 -------
415 fgcmStarIdCat: `afw.table.BaseCatalog`
416 Catalog of unique star identifiers and index keys
417 fgcmStarIndicesCat: `afwTable.BaseCatalog`
418 Catalog of unique star indices
419 fgcmRefCat: `afw.table.BaseCatalog`
420 Catalog of matched reference stars.
421 Will be None if `config.doReferenceMatches` is False.
422 """
423 # get filter names into a numpy array...
424 # This is the type that is expected by the fgcm code
425 visitFilterNames = np.zeros(len(visitCat), dtype='a30')
426 for i in range(len(visitCat)):
427 visitFilterNames[i] = visitCat[i]['physicalFilter']
428
429 # match to put filterNames with observations
430 visitIndex = np.searchsorted(visitCat['visit'],
431 obsCat['visit'])
432
433 obsFilterNames = visitFilterNames[visitIndex]
434
435 if self.config.doReferenceMatches:
436 # Get the reference filter names, using the LUT
437 lutCat = lutHandle.get()
438
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'])}
445
446 del lutCat
447
448 referenceFilterNames = self._getReferenceFilterNames(visitCat,
449 stdFilterDict,
450 stdLambdaDict)
451 self.log.info("Using the following reference filters: %s" %
452 (', '.join(referenceFilterNames)))
453
454 else:
455 # This should be an empty list
456 referenceFilterNames = []
457
458 # make the fgcm starConfig dict
459 starConfig = {'logger': self.log,
460 'useHtm': True,
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}
473
474 # initialize the FgcmMakeStars object
475 fgcmMakeStars = fgcm.FgcmMakeStars(starConfig)
476
477 # make the primary stars
478 # note that the ra/dec native Angle format is radians
479 # We determine the conversion from the native units (typically
480 # radians) to degrees for the first observation. This allows us
481 # to treate ra/dec as numpy arrays rather than Angles, which would
482 # be approximately 600x slower.
483 conv = obsCat[0]['ra'].asDegrees() / float(obsCat[0]['ra'])
484 fgcmMakeStars.makePrimaryStars(obsCat['ra'] * conv,
485 obsCat['dec'] * conv,
486 filterNameArray=obsFilterNames,
487 bandSelected=False)
488
489 # and match all the stars
490 fgcmMakeStars.makeMatchedStars(obsCat['ra'] * conv,
491 obsCat['dec'] * conv,
492 obsFilterNames)
493
494 if self.config.doReferenceMatches:
495 fgcmMakeStars.makeReferenceMatches(self.fgcmLoadReferenceCatalog)
496
497 # now persist
498
499 objSchema = self._makeFgcmObjSchema()
500
501 # make catalog and records
502 fgcmStarIdCat = afwTable.BaseCatalog(objSchema)
503 fgcmStarIdCat.reserve(fgcmMakeStars.objIndexCat.size)
504 for i in range(fgcmMakeStars.objIndexCat.size):
505 fgcmStarIdCat.addNew()
506
507 # fill the catalog
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']
513
514 obsSchema = self._makeFgcmObsSchema()
515
516 fgcmStarIndicesCat = afwTable.BaseCatalog(obsSchema)
517 fgcmStarIndicesCat.reserve(fgcmMakeStars.obsIndexCat.size)
518 for i in range(fgcmMakeStars.obsIndexCat.size):
519 fgcmStarIndicesCat.addNew()
520
521 fgcmStarIndicesCat['obsIndex'][:] = fgcmMakeStars.obsIndexCat['obsindex']
522
523 if self.config.doReferenceMatches:
524 refSchema = self._makeFgcmRefSchema(len(referenceFilterNames))
525
526 fgcmRefCat = afwTable.BaseCatalog(refSchema)
527 fgcmRefCat.reserve(fgcmMakeStars.referenceCat.size)
528
529 for i in range(fgcmMakeStars.referenceCat.size):
530 fgcmRefCat.addNew()
531
532 fgcmRefCat['fgcm_id'][:] = fgcmMakeStars.referenceCat['fgcm_id']
533 fgcmRefCat['refMag'][:, :] = fgcmMakeStars.referenceCat['refMag']
534 fgcmRefCat['refMagErr'][:, :] = fgcmMakeStars.referenceCat['refMagErr']
535
536 md = PropertyList()
537 md.set("REFSTARS_FORMAT_VERSION", REFSTARS_FORMAT_VERSION)
538 md.set("FILTERNAMES", referenceFilterNames)
539 fgcmRefCat.setMetadata(md)
540
541 else:
542 fgcmRefCat = None
543
544 return fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat
545
546 def _makeFgcmVisitSchema(self, nCcd):
547 """
548 Make a schema for an fgcmVisitCatalog
549
550 Parameters
551 ----------
552 nCcd: `int`
553 Number of CCDs in the camera
554
555 Returns
556 -------
557 schema: `afwTable.Schema`
558 """
559
560 schema = afwTable.Schema()
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)")
573 # the following field is not used yet
574 schema.addField('deepFlag', type=np.int32, doc="Deep observation")
575 schema.addField('scaling', type='ArrayD', doc="Scaling applied due to flat adjustment",
576 size=nCcd)
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.")
579
580 return schema
581
582 def _makeFgcmObjSchema(self):
583 """
584 Make a schema for the objIndexCat from fgcmMakeStars
585
586 Returns
587 -------
588 schema: `afwTable.Schema`
589 """
590
591 objSchema = afwTable.Schema()
592 objSchema.addField('fgcm_id', type=np.int32, doc='FGCM Unique ID')
593 # Will investigate making these angles...
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')
599
600 return objSchema
601
602 def _makeFgcmObsSchema(self):
603 """
604 Make a schema for the obsIndexCat from fgcmMakeStars
605
606 Returns
607 -------
608 schema: `afwTable.Schema`
609 """
610
611 obsSchema = afwTable.Schema()
612 obsSchema.addField('obsIndex', type=np.int32, doc='Index in observation table')
613
614 return obsSchema
615
616 def _makeFgcmRefSchema(self, nReferenceBands):
617 """
618 Make a schema for the referenceCat from fgcmMakeStars
619
620 Parameters
621 ----------
622 nReferenceBands: `int`
623 Number of reference bands
624
625 Returns
626 -------
627 schema: `afwTable.Schema`
628 """
629
630 refSchema = afwTable.Schema()
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)
636
637 return refSchema
638
639 def _getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict):
640 """
641 Get the reference filter names, in wavelength order, from the visitCat and
642 information from the look-up-table.
643
644 Parameters
645 ----------
646 visitCat: `afw.table.BaseCatalog`
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
652
653 Returns
654 -------
655 referenceFilterNames: `list`
656 Wavelength-ordered list of reference filter names
657 """
658
659 # Find the unique list of filter names in visitCat
660 filterNames = np.unique(visitCat.asAstropy()['physicalFilter'])
661
662 # Find the unique list of "standard" filters
663 stdFilterNames = {stdFilterDict[filterName] for filterName in filterNames}
664
665 # And sort these by wavelength
666 referenceFilterNames = sorted(stdFilterNames, key=stdLambdaDict.get)
667
668 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:928