LSST Applications g013ef56533+7c9321ec0f,g042eb84c57+c6cfa41bc3,g199a45376c+0ba108daf9,g1fd858c14a+fcad0d0313,g210f2d0738+c0f94c6586,g262e1987ae+a7e710680e,g29ae962dfc+fb55f2edb0,g2ac17093b6+61d6563b1e,g2b1d02342f+df6f932764,g2cef7863aa+aef1011c0b,g2f7ad74990+c0f94c6586,g35bb328faa+8c5ae1fdc5,g3fd5ace14f+53cf87ae69,g47891489e3+4316d04fff,g511e8cfd20+baa56acf6c,g53246c7159+8c5ae1fdc5,g54cd7ddccb+fd7ad03fde,g64539dfbff+c0f94c6586,g67b6fd64d1+4316d04fff,g67fd3c3899+c0f94c6586,g6985122a63+4316d04fff,g74acd417e5+ca833bee28,g786e29fd12+668abc6043,g81db2e9a8d+b2ec8e584f,g87389fa792+8856018cbb,g89139ef638+4316d04fff,g8d7436a09f+0a24083b20,g8ea07a8fe4+760ca7c3fc,g90f42f885a+033b1d468d,g97be763408+11eb8fd5b8,gbf99507273+8c5ae1fdc5,gcdda8b9158+e4c84c9d5c,gce8aa8abaa+8c5ae1fdc5,gd7ef33dd92+4316d04fff,gdab6d2f7ff+ca833bee28,ge410e46f29+4316d04fff,geaed405ab2+c4bbc419c6,gf9a733ac38+8c5ae1fdc5,w.2025.40
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
36from .utilities import computeReferencePixelScale, countDetectors
37
38import fgcm
39
40REFSTARS_FORMAT_VERSION = 1
41
42__all__ = ['FgcmBuildStarsConfigBase', 'FgcmBuildStarsBaseTask']
43
44
45class FgcmBuildStarsConfigBase(pexConfig.Config):
46 """Base config for FgcmBuildStars tasks"""
47
48 instFluxField = pexConfig.Field(
49 doc=("Faull name of the source instFlux field to use, including 'instFlux'. "
50 "The associated flag will be implicitly included in badFlags"),
51 dtype=str,
52 default='slot_CalibFlux_instFlux',
53 )
54 minPerBand = pexConfig.Field(
55 doc="Minimum observations per band",
56 dtype=int,
57 default=2,
58 )
59 matchRadius = pexConfig.Field(
60 doc="Match radius (arcseconds)",
61 dtype=float,
62 default=1.0,
63 )
64 isolationRadius = pexConfig.Field(
65 doc="Isolation radius (arcseconds)",
66 dtype=float,
67 default=2.0,
68 )
69 densityCutNside = pexConfig.Field(
70 doc="Density cut healpix nside",
71 dtype=int,
72 default=128,
73 )
74 densityCutMaxPerPixel = pexConfig.Field(
75 doc="Density cut number of stars per pixel",
76 dtype=int,
77 default=1000,
78 )
79 randomSeed = pexConfig.Field(
80 doc="Random seed for high density down-sampling.",
81 dtype=int,
82 default=None,
83 optional=True,
84 )
85 matchNside = pexConfig.Field(
86 doc="Healpix Nside for matching",
87 dtype=int,
88 default=4096,
89 )
90 coarseNside = pexConfig.Field(
91 doc="Healpix coarse Nside for partitioning matches",
92 dtype=int,
93 default=8,
94 )
95 physicalFilterMap = pexConfig.DictField(
96 doc="Mapping from 'physicalFilter' to band.",
97 keytype=str,
98 itemtype=str,
99 default={},
100 )
101 requiredBands = pexConfig.ListField(
102 doc="Bands required for each star",
103 dtype=str,
104 default=(),
105 )
106 primaryBands = pexConfig.ListField(
107 doc=("Bands for 'primary' star matches. "
108 "A star must be observed in one of these bands to be considered "
109 "as a calibration star."),
110 dtype=str,
111 default=None
112 )
113 doApplyWcsJacobian = pexConfig.Field(
114 doc="Apply the jacobian of the WCS to the star observations prior to fit?",
115 dtype=bool,
116 default=True
117 )
118 doModelErrorsWithBackground = pexConfig.Field(
119 doc="Model flux errors with background term?",
120 dtype=bool,
121 default=True
122 )
123 psfCandidateName = pexConfig.Field(
124 doc="Name of field with psf candidate flag for propagation",
125 dtype=str,
126 default="calib_psf_candidate"
127 )
128 doSubtractLocalBackground = pexConfig.Field(
129 doc=("Subtract the local background before performing calibration? "
130 "This is only supported for circular aperture calibration fluxes."),
131 dtype=bool,
132 default=False
133 )
134 localBackgroundFluxField = pexConfig.Field(
135 doc="Full name of the local background instFlux field to use.",
136 dtype=str,
137 default='base_LocalBackground_instFlux'
138 )
139 sourceSelector = sourceSelectorRegistry.makeField(
140 doc="How to select sources",
141 default="science"
142 )
143 apertureInnerInstFluxField = pexConfig.Field(
144 doc=("Full name of instFlux field that contains inner aperture "
145 "flux for aperture correction proxy"),
146 dtype=str,
147 default='base_CircularApertureFlux_12_0_instFlux'
148 )
149 apertureOuterInstFluxField = pexConfig.Field(
150 doc=("Full name of instFlux field that contains outer aperture "
151 "flux for aperture correction proxy"),
152 dtype=str,
153 default='base_CircularApertureFlux_17_0_instFlux'
154 )
155 doReferenceMatches = pexConfig.Field(
156 doc="Match reference catalog as additional constraint on calibration",
157 dtype=bool,
158 default=True,
159 )
160 fgcmLoadReferenceCatalog = pexConfig.ConfigurableField(
161 target=FgcmLoadReferenceCatalogTask,
162 doc="FGCM reference object loader",
163 )
164 nVisitsPerCheckpoint = pexConfig.Field(
165 doc="Number of visits read between checkpoints",
166 dtype=int,
167 default=500,
168 )
169
170 def setDefaults(self):
171 sourceSelector = self.sourceSelector["science"]
172 sourceSelector.setDefaults()
173
174 sourceSelector.doFlags = True
175 sourceSelector.doUnresolved = True
176 sourceSelector.doSignalToNoise = True
177 sourceSelector.doIsolated = True
178 sourceSelector.doRequireFiniteRaDec = True
179
180 sourceSelector.signalToNoise.minimum = 10.0
181 sourceSelector.signalToNoise.maximum = 1000.0
182
183 # FGCM operates on unresolved sources, and this setting is
184 # appropriate for the current base_ClassificationExtendedness
185 sourceSelector.unresolved.maximum = 0.5
186
187
188class FgcmBuildStarsBaseTask(pipeBase.PipelineTask, abc.ABC):
189 """
190 Base task to build stars for FGCM global calibration
191 """
192 def __init__(self, initInputs=None, **kwargs):
193 super().__init__(**kwargs)
194
195 self.makeSubtask("sourceSelector")
196 # Only log warning and fatal errors from the sourceSelector
197 self.sourceSelector.log.setLevel(self.sourceSelector.log.WARN)
198
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.
214 camera : `lsst.afw.cameraGeom.Camera`
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, useScienceDetectors=False):
234 """
235 Make a visit catalog with all the keys from each visit
236
237 Parameters
238 ----------
239 camera : `lsst.afw.cameraGeom.Camera`
240 Camera from the butler
241 groupedHandles : `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
242 Dataset handles, grouped by visit.
243 useScienceDetectors : `bool`, optional
244 Limit to science detectors?
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 = countDetectors(camera, useScienceDetectors)
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 defaultPixelScale = computeReferencePixelScale(camera, useScienceDetectors=useScienceDetectors)
266
267 # No matter what, fill the catalog. This will check if it was
268 # already read.
269 self._fillVisitCatalog(visitCat, groupedHandles, defaultPixelScale)
270
271 return visitCat
272
273 def _fillVisitCatalog(self, visitCat, groupedHandles, defaultPixelScale):
274 """
275 Fill the visit catalog with visit metadata
276
277 Parameters
278 ----------
279 visitCat : `afw.table.BaseCatalog`
280 Visit catalog. See _makeFgcmVisitSchema() for schema definition.
281 groupedHandles : `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
282 Dataset handles, grouped by visit.
283 defaultPixelScale : `float`
284 Default pixel scale to use if not in visit summary (arcsecond/pixel).
285 """
286
287 # Guarantee that these are sorted.
288 for i, visit in enumerate(sorted(groupedHandles)):
289 if (i % self.config.nVisitsPerCheckpoint) == 0:
290 self.log.info("Retrieving metadata for visit %d (%d/%d)", visit, i, len(groupedHandles))
291
292 handle = groupedHandles[visit][0]
293 summary = handle.get()
294
295 summaryRow = summary.find(self.config.referenceCCD)
296 if summaryRow is None:
297 # Take the first available ccd if reference isn't available
298 summaryRow = summary[0]
299
300 visitInfo = summaryRow.getVisitInfo()
301 physicalFilter = summaryRow['physical_filter']
302 # Compute the median psf sigma and fwhm if possible.
303 if 'pixelScale' in summary.schema:
304 # This is not available in the older test summaries
305 pixelScales = summary['pixelScale']
306 else:
307 pixelScales = np.full(len(summary['psfSigma']), defaultPixelScale)
308 psfSigmas = summary['psfSigma']
309 goodSigma, = np.where((np.nan_to_num(psfSigmas) > 0) & (np.nan_to_num(pixelScales) > 0))
310 if goodSigma.size > 2:
311 psfSigma = np.median(psfSigmas[goodSigma])
312 psfFwhm = np.median(psfSigmas[goodSigma] * pixelScales[goodSigma]) * np.sqrt(8.*np.log(2.))
313 elif goodSigma.size > 0:
314 psfSigma = psfSigmas[goodSigma[0]]
315 psfFwhm = psfSigmas[goodSigma[0]] * pixelScales[goodSigma[0]] * np.sqrt(8.)*np.log(2.)
316 else:
317 self.log.warning("Could not find any good summary psfSigma for visit %d", visit)
318 psfSigma = 0.0
319 psfFwhm = 0.0
320 # Compute median background if possible
321 goodBackground, = np.where(np.nan_to_num(summary['skyBg']) > 0.0)
322 if goodBackground.size > 2:
323 skyBackground = np.median(summary['skyBg'][goodBackground])
324 elif goodBackground.size > 0:
325 skyBackground = summary['skyBg'][goodBackground[0]]
326 else:
327 self.log.warning('Could not find any good summary skyBg for visit %d', visit)
328 skyBackground = -1.0
329
330 rec = visitCat[i]
331 rec['visit'] = visit
332 rec['physicalFilter'] = physicalFilter
333 # TODO DM-26991: Use the wcs to refine the focal-plane center.
334 radec = visitInfo.getBoresightRaDec()
335 rec['telra'] = radec.getRa().asDegrees()
336 rec['teldec'] = radec.getDec().asDegrees()
337 rec['telha'] = visitInfo.getBoresightHourAngle().asDegrees()
338 rec['telrot'] = visitInfo.getBoresightRotAngle().asDegrees()
339 rec['mjd'] = visitInfo.getDate().get(system=DateTime.MJD)
340 rec['exptime'] = visitInfo.getExposureTime()
341 # convert from Pa to millibar
342 # Note that I don't know if this unit will need to be per-camera config
343 rec['pmb'] = visitInfo.getWeather().getAirPressure() / 100
344 # Flag to signify if this is a "deep" field. Not currently used
345 rec['deepFlag'] = 0
346 # Relative flat scaling (1.0 means no relative scaling)
347 rec['scaling'][:] = 1.0
348 # Median delta aperture, to be measured from stars
349 rec['deltaAper'] = -9999.0
350 rec['deltaAperDetector'][:] = -9999.0
351 rec['psfSigma'] = psfSigma
352 rec['psfFwhm'] = psfFwhm
353 rec['skyBackground'] = skyBackground
354 rec['used'] = 1
355
356 def _makeSourceMapper(self, sourceSchema):
357 """
358 Make a schema mapper for fgcm sources
359
360 Parameters
361 ----------
362 sourceSchema: `afwTable.Schema`
363 Default source schema from the butler
364
365 Returns
366 -------
367 sourceMapper: `afwTable.schemaMapper`
368 Mapper to the FGCM source schema
369 """
370
371 # create a mapper to the preferred output
372 sourceMapper = afwTable.SchemaMapper(sourceSchema)
373
374 # map to ra/dec
375 sourceMapper.addMapping(sourceSchema['coord_ra'].asKey(), 'ra')
376 sourceMapper.addMapping(sourceSchema['coord_dec'].asKey(), 'dec')
377 sourceMapper.addMapping(sourceSchema['slot_Centroid_x'].asKey(), 'x')
378 sourceMapper.addMapping(sourceSchema['slot_Centroid_y'].asKey(), 'y')
379 # Add the mapping if the field exists in the input catalog.
380 # If the field does not exist, simply add it (set to False).
381 # This field is not required for calibration, but is useful
382 # to collate if available.
383 try:
384 sourceMapper.addMapping(sourceSchema[self.config.psfCandidateName].asKey(),
385 'psf_candidate')
386 except LookupError:
387 sourceMapper.editOutputSchema().addField(
388 "psf_candidate", type='Flag',
389 doc=("Flag set if the source was a candidate for PSF determination, "
390 "as determined by the star selector."))
391
392 # and add the fields we want
393 sourceMapper.editOutputSchema().addField(
394 "visit", type=np.int64, doc="Visit number")
395 sourceMapper.editOutputSchema().addField(
396 "ccd", type=np.int32, doc="CCD number")
397 sourceMapper.editOutputSchema().addField(
398 "instMag", type=np.float32, doc="Instrumental magnitude")
399 sourceMapper.editOutputSchema().addField(
400 "instMagErr", type=np.float32, doc="Instrumental magnitude error")
401 sourceMapper.editOutputSchema().addField(
402 "jacobian", type=np.float32, doc="Relative pixel scale from wcs jacobian")
403 sourceMapper.editOutputSchema().addField(
404 "deltaMagBkg", type=np.float32, doc="Change in magnitude due to local background offset")
405 sourceMapper.editOutputSchema().addField(
406 "deltaMagAper", type=np.float32, doc="Change in magnitude from larger to smaller aperture")
407
408 return sourceMapper
409
410 def fgcmMatchStars(self, visitCat, obsCat, lutHandle=None):
411 """
412 Use FGCM code to match observations into unique stars.
413
414 Parameters
415 ----------
416 visitCat: `afw.table.BaseCatalog`
417 Catalog with visit data for fgcm
418 obsCat: `afw.table.BaseCatalog`
419 Full catalog of star observations for fgcm
420 lutHandle: `lsst.daf.butler.DeferredDatasetHandle`, optional
421 Data reference to fgcm look-up table (used if matching reference stars).
422
423 Returns
424 -------
425 fgcmStarIdCat: `afw.table.BaseCatalog`
426 Catalog of unique star identifiers and index keys
427 fgcmStarIndicesCat: `afwTable.BaseCatalog`
428 Catalog of unique star indices
429 fgcmRefCat: `afw.table.BaseCatalog`
430 Catalog of matched reference stars.
431 Will be None if `config.doReferenceMatches` is False.
432 """
433 # get filter names into a numpy array...
434 # This is the type that is expected by the fgcm code
435 visitFilterNames = np.zeros(len(visitCat), dtype='S30')
436 for i in range(len(visitCat)):
437 visitFilterNames[i] = visitCat[i]['physicalFilter']
438
439 # match to put filterNames with observations
440 visitIndex = np.searchsorted(visitCat['visit'],
441 obsCat['visit'])
442
443 obsFilterNames = visitFilterNames[visitIndex]
444
445 if self.config.doReferenceMatches:
446 # Get the reference filter names, using the LUT
447 lutCat = lutHandle.get()
448
449 stdFilterDict = {filterName: stdFilter for (filterName, stdFilter) in
450 zip(lutCat[0]['physicalFilters'].split(','),
451 lutCat[0]['stdPhysicalFilters'].split(','))}
452 stdLambdaDict = {stdFilter: stdLambda for (stdFilter, stdLambda) in
453 zip(lutCat[0]['stdPhysicalFilters'].split(','),
454 lutCat[0]['lambdaStdFilter'])}
455
456 del lutCat
457
458 referenceFilterNames = self._getReferenceFilterNames(visitCat,
459 stdFilterDict,
460 stdLambdaDict)
461 self.log.info("Using the following reference filters: %s" %
462 (', '.join(referenceFilterNames)))
463
464 else:
465 # This should be an empty list
466 referenceFilterNames = []
467
468 # make the fgcm starConfig dict
469 starConfig = {'logger': self.log,
470 'useHtm': True,
471 'filterToBand': self.config.physicalFilterMap,
472 'requiredBands': self.config.requiredBands,
473 'minPerBand': self.config.minPerBand,
474 'matchRadius': self.config.matchRadius,
475 'isolationRadius': self.config.isolationRadius,
476 'matchNSide': self.config.matchNside,
477 'coarseNSide': self.config.coarseNside,
478 'densNSide': self.config.densityCutNside,
479 'densMaxPerPixel': self.config.densityCutMaxPerPixel,
480 'randomSeed': self.config.randomSeed,
481 'primaryBands': self.config.primaryBands,
482 'referenceFilterNames': referenceFilterNames}
483
484 # initialize the FgcmMakeStars object
485 fgcmMakeStars = fgcm.FgcmMakeStars(starConfig)
486
487 # make the primary stars
488 # note that the ra/dec native Angle format is radians
489 # We determine the conversion from the native units (typically
490 # radians) to degrees for the first observation. This allows us
491 # to treate ra/dec as numpy arrays rather than Angles, which would
492 # be approximately 600x slower.
493 conv = obsCat[0]['ra'].asDegrees() / float(obsCat[0]['ra'])
494 fgcmMakeStars.makePrimaryStars(obsCat['ra'] * conv,
495 obsCat['dec'] * conv,
496 filterNameArray=obsFilterNames,
497 bandSelected=False)
498
499 # and match all the stars
500 fgcmMakeStars.makeMatchedStars(obsCat['ra'] * conv,
501 obsCat['dec'] * conv,
502 obsFilterNames)
503
504 if self.config.doReferenceMatches:
505 fgcmMakeStars.makeReferenceMatches(self.fgcmLoadReferenceCatalog)
506
507 # now persist
508
509 objSchema = self._makeFgcmObjSchema()
510
511 # make catalog and records
512 fgcmStarIdCat = afwTable.BaseCatalog(objSchema)
513 fgcmStarIdCat.reserve(fgcmMakeStars.objIndexCat.size)
514 for i in range(fgcmMakeStars.objIndexCat.size):
515 fgcmStarIdCat.addNew()
516
517 # fill the catalog
518 fgcmStarIdCat['fgcm_id'][:] = fgcmMakeStars.objIndexCat['fgcm_id']
519 fgcmStarIdCat['ra'][:] = fgcmMakeStars.objIndexCat['ra']
520 fgcmStarIdCat['dec'][:] = fgcmMakeStars.objIndexCat['dec']
521 fgcmStarIdCat['obsArrIndex'][:] = fgcmMakeStars.objIndexCat['obsarrindex']
522 fgcmStarIdCat['nObs'][:] = fgcmMakeStars.objIndexCat['nobs']
523
524 obsSchema = self._makeFgcmObsSchema()
525
526 fgcmStarIndicesCat = afwTable.BaseCatalog(obsSchema)
527 fgcmStarIndicesCat.reserve(fgcmMakeStars.obsIndexCat.size)
528 for i in range(fgcmMakeStars.obsIndexCat.size):
529 fgcmStarIndicesCat.addNew()
530
531 fgcmStarIndicesCat['obsIndex'][:] = fgcmMakeStars.obsIndexCat['obsindex']
532
533 if self.config.doReferenceMatches:
534 refSchema = self._makeFgcmRefSchema(len(referenceFilterNames))
535
536 fgcmRefCat = afwTable.BaseCatalog(refSchema)
537 fgcmRefCat.reserve(fgcmMakeStars.referenceCat.size)
538
539 for i in range(fgcmMakeStars.referenceCat.size):
540 fgcmRefCat.addNew()
541
542 fgcmRefCat['fgcm_id'][:] = fgcmMakeStars.referenceCat['fgcm_id']
543 fgcmRefCat['refMag'][:, :] = fgcmMakeStars.referenceCat['refMag']
544 fgcmRefCat['refMagErr'][:, :] = fgcmMakeStars.referenceCat['refMagErr']
545
546 md = PropertyList()
547 md.set("REFSTARS_FORMAT_VERSION", REFSTARS_FORMAT_VERSION)
548 md.set("FILTERNAMES", referenceFilterNames)
549 fgcmRefCat.setMetadata(md)
550
551 else:
552 fgcmRefCat = None
553
554 return fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat
555
556 def _makeFgcmVisitSchema(self, nCcd):
557 """
558 Make a schema for an fgcmVisitCatalog
559
560 Parameters
561 ----------
562 nCcd: `int`
563 Number of CCDs in the camera
564
565 Returns
566 -------
567 schema: `afwTable.Schema`
568 """
569
570 schema = afwTable.Schema()
571 schema.addField('visit', type=np.int64, doc="Visit number")
572 schema.addField('physicalFilter', type=str, size=30, doc="Physical filter")
573 schema.addField('telra', type=np.float64, doc="Pointing RA (deg)")
574 schema.addField('teldec', type=np.float64, doc="Pointing Dec (deg)")
575 schema.addField('telha', type=np.float64, doc="Pointing Hour Angle (deg)")
576 schema.addField('telrot', type=np.float64, doc="Camera rotation (deg)")
577 schema.addField('mjd', type=np.float64, doc="MJD of visit")
578 schema.addField('exptime', type=np.float32, doc="Exposure time")
579 schema.addField('pmb', type=np.float32, doc="Pressure (millibar)")
580 schema.addField('psfSigma', type=np.float32, doc="PSF sigma (median); pixels")
581 schema.addField('psfFwhm', type=np.float32, doc="PSF FWHM (median); arcseconds")
582 schema.addField('deltaAper', type=np.float32, doc="Delta-aperture")
583 schema.addField('deltaAperDetector', type='ArrayF', doc='Delta-aperture per detector', size=nCcd)
584 schema.addField('skyBackground', type=np.float32, doc="Sky background (ADU) (reference CCD)")
585 # the following field is not used yet
586 schema.addField('deepFlag', type=np.int32, doc="Deep observation")
587 schema.addField('scaling', type='ArrayD', doc="Scaling applied due to flat adjustment",
588 size=nCcd)
589 schema.addField('used', type=np.int32, doc="This visit has been ingested.")
590 schema.addField('sources_read', type='Flag', doc="This visit had sources read.")
591
592 return schema
593
595 """
596 Make a schema for the objIndexCat from fgcmMakeStars
597
598 Returns
599 -------
600 schema: `afwTable.Schema`
601 """
602
603 objSchema = afwTable.Schema()
604 objSchema.addField('fgcm_id', type=np.int32, doc='FGCM Unique ID')
605 # Will investigate making these angles...
606 objSchema.addField('ra', type=np.float64, doc='Mean object RA (deg)')
607 objSchema.addField('dec', type=np.float64, doc='Mean object Dec (deg)')
608 objSchema.addField('obsArrIndex', type=np.int32,
609 doc='Index in obsIndexTable for first observation')
610 objSchema.addField('nObs', type=np.int32, doc='Total number of observations')
611
612 return objSchema
613
615 """
616 Make a schema for the obsIndexCat from fgcmMakeStars
617
618 Returns
619 -------
620 schema: `afwTable.Schema`
621 """
622
623 obsSchema = afwTable.Schema()
624 obsSchema.addField('obsIndex', type=np.int32, doc='Index in observation table')
625
626 return obsSchema
627
628 def _makeFgcmRefSchema(self, nReferenceBands):
629 """
630 Make a schema for the referenceCat from fgcmMakeStars
631
632 Parameters
633 ----------
634 nReferenceBands: `int`
635 Number of reference bands
636
637 Returns
638 -------
639 schema: `afwTable.Schema`
640 """
641
642 refSchema = afwTable.Schema()
643 refSchema.addField('fgcm_id', type=np.int32, doc='FGCM Unique ID')
644 refSchema.addField('refMag', type='ArrayF', doc='Reference magnitude array (AB)',
645 size=nReferenceBands)
646 refSchema.addField('refMagErr', type='ArrayF', doc='Reference magnitude error array',
647 size=nReferenceBands)
648
649 return refSchema
650
651 def _getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict):
652 """
653 Get the reference filter names, in wavelength order, from the visitCat and
654 information from the look-up-table.
655
656 Parameters
657 ----------
658 visitCat: `afw.table.BaseCatalog`
659 Catalog with visit data for FGCM
660 stdFilterDict: `dict`
661 Mapping of filterName to stdFilterName from LUT
662 stdLambdaDict: `dict`
663 Mapping of stdFilterName to stdLambda from LUT
664
665 Returns
666 -------
667 referenceFilterNames: `list`
668 Wavelength-ordered list of reference filter names
669 """
670
671 # Find the unique list of filter names in visitCat
672 filterNames = np.unique(visitCat.asAstropy()['physicalFilter'])
673
674 # Find the unique list of "standard" filters
675 stdFilterNames = {stdFilterDict[filterName] for filterName in filterNames}
676
677 # And sort these by wavelength
678 referenceFilterNames = sorted(stdFilterNames, key=stdLambdaDict.get)
679
680 return referenceFilterNames
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.
Class for storing ordered metadata with comments.
fgcmMatchStars(self, visitCat, obsCat, lutHandle=None)
_getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict)
_fillVisitCatalog(self, visitCat, groupedHandles, defaultPixelScale)
fgcmMakeAllStarObservations(self, groupedHandles, visitCat, sourceSchema, camera, calibFluxApertureRadius=None)
fgcmMakeVisitCatalog(self, camera, groupedHandles, useScienceDetectors=False)