Loading [MathJax]/extensions/tex2jax.js
LSST Applications g04dff08e69+fafbcb10e2,g0d33ba9806+e09a96fa4e,g0fba68d861+cc01b48236,g1e78f5e6d3+fb95f9dda6,g1ec0fe41b4+f536777771,g1fd858c14a+ae46bc2a71,g35bb328faa+fcb1d3bbc8,g4af146b050+dd94f3aad7,g4d2262a081+7ee6f976aa,g53246c7159+fcb1d3bbc8,g5a012ec0e7+b20b785ecb,g60b5630c4e+e09a96fa4e,g6273192d42+bf8cfc5e62,g67b6fd64d1+4086c0989b,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g7b71ed6315+fcb1d3bbc8,g87b7deb4dc+831c06c8fc,g8852436030+54b48a5987,g89139ef638+4086c0989b,g9125e01d80+fcb1d3bbc8,g94187f82dc+e09a96fa4e,g989de1cb63+4086c0989b,g9f33ca652e+64be6d9d51,g9f7030ddb1+d11454dffd,ga2b97cdc51+e09a96fa4e,gabe3b4be73+1e0a283bba,gabf8522325+fa80ff7197,gb1101e3267+23605820ec,gb58c049af0+f03b321e39,gb89ab40317+4086c0989b,gcf25f946ba+54b48a5987,gd6cbbdb0b4+af3c3595f5,gd9a9a58781+fcb1d3bbc8,gde0f65d7ad+15f2daff9d,ge278dab8ac+d65b3c2b70,ge410e46f29+4086c0989b,gf67bdafdda+4086c0989b,v29.0.0.rc5
LSST Data Management Base Package
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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
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):
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
244 Returns
245 -------
246 visitCat: `afw.table.BaseCatalog`
247 """
248
249 self.log.info("Assembling visitCatalog from %d visits", len(groupedHandles))
250
251 nCcd = len(camera)
252
253 schema = self._makeFgcmVisitSchema(nCcd)
254
255 visitCat = afwTable.BaseCatalog(schema)
256 visitCat.reserve(len(groupedHandles))
257 visitCat.resize(len(groupedHandles))
258
259 visitCat['visit'] = list(groupedHandles.keys())
260 visitCat['used'] = 0
261 visitCat['sources_read'] = False
262
263 defaultPixelScale = computeReferencePixelScale(camera)
264
265 # No matter what, fill the catalog. This will check if it was
266 # already read.
267 self._fillVisitCatalog(visitCat, groupedHandles, defaultPixelScale)
268
269 return visitCat
270
271 def _fillVisitCatalog(self, visitCat, groupedHandles, defaultPixelScale):
272 """
273 Fill the visit catalog with visit metadata
274
275 Parameters
276 ----------
277 visitCat : `afw.table.BaseCatalog`
278 Visit catalog. See _makeFgcmVisitSchema() for schema definition.
279 groupedHandles : `dict` [`list` [`lsst.daf.butler.DeferredDatasetHandle`]]
280 Dataset handles, grouped by visit.
281 defaultPixelScale : `float`
282 Default pixel scale to use if not in visit summary (arcsecond/pixel).
283 """
284
285 # Guarantee that these are sorted.
286 for i, visit in enumerate(sorted(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 visitInfo = summaryRow.getVisitInfo()
299 physicalFilter = summaryRow['physical_filter']
300 # Compute the median psf sigma and fwhm if possible.
301 if 'pixelScale' in summary.schema:
302 # This is not available in the older test summaries
303 pixelScales = summary['pixelScale']
304 else:
305 pixelScales = np.full(len(summary['psfSigma']), defaultPixelScale)
306 psfSigmas = summary['psfSigma']
307 goodSigma, = np.where((np.nan_to_num(psfSigmas) > 0) & (np.nan_to_num(pixelScales) > 0))
308 if goodSigma.size > 2:
309 psfSigma = np.median(psfSigmas[goodSigma])
310 psfFwhm = np.median(psfSigmas[goodSigma] * pixelScales[goodSigma]) * np.sqrt(8.*np.log(2.))
311 elif goodSigma.size > 0:
312 psfSigma = psfSigmas[goodSigma[0]]
313 psfFwhm = psfSigmas[goodSigma[0]] * pixelScales[goodSigma[0]] * np.sqrt(8.)*np.log(2.)
314 else:
315 self.log.warning("Could not find any good summary psfSigma for visit %d", visit)
316 psfSigma = 0.0
317 psfFwhm = 0.0
318 # Compute median background if possible
319 goodBackground, = np.where(np.nan_to_num(summary['skyBg']) > 0.0)
320 if goodBackground.size > 2:
321 skyBackground = np.median(summary['skyBg'][goodBackground])
322 elif goodBackground.size > 0:
323 skyBackground = summary['skyBg'][goodBackground[0]]
324 else:
325 self.log.warning('Could not find any good summary skyBg for visit %d', visit)
326 skyBackground = -1.0
327
328 rec = visitCat[i]
329 rec['visit'] = visit
330 rec['physicalFilter'] = physicalFilter
331 # TODO DM-26991: Use the wcs to refine the focal-plane center.
332 radec = visitInfo.getBoresightRaDec()
333 rec['telra'] = radec.getRa().asDegrees()
334 rec['teldec'] = radec.getDec().asDegrees()
335 rec['telha'] = visitInfo.getBoresightHourAngle().asDegrees()
336 rec['telrot'] = visitInfo.getBoresightRotAngle().asDegrees()
337 rec['mjd'] = visitInfo.getDate().get(system=DateTime.MJD)
338 rec['exptime'] = visitInfo.getExposureTime()
339 # convert from Pa to millibar
340 # Note that I don't know if this unit will need to be per-camera config
341 rec['pmb'] = visitInfo.getWeather().getAirPressure() / 100
342 # Flag to signify if this is a "deep" field. Not currently used
343 rec['deepFlag'] = 0
344 # Relative flat scaling (1.0 means no relative scaling)
345 rec['scaling'][:] = 1.0
346 # Median delta aperture, to be measured from stars
347 rec['deltaAper'] = 0.0
348 rec['psfSigma'] = psfSigma
349 rec['psfFwhm'] = psfFwhm
350 rec['skyBackground'] = skyBackground
351 rec['used'] = 1
352
353 def _makeSourceMapper(self, sourceSchema):
354 """
355 Make a schema mapper for fgcm sources
356
357 Parameters
358 ----------
359 sourceSchema: `afwTable.Schema`
360 Default source schema from the butler
361
362 Returns
363 -------
364 sourceMapper: `afwTable.schemaMapper`
365 Mapper to the FGCM source schema
366 """
367
368 # create a mapper to the preferred output
369 sourceMapper = afwTable.SchemaMapper(sourceSchema)
370
371 # map to ra/dec
372 sourceMapper.addMapping(sourceSchema['coord_ra'].asKey(), 'ra')
373 sourceMapper.addMapping(sourceSchema['coord_dec'].asKey(), 'dec')
374 sourceMapper.addMapping(sourceSchema['slot_Centroid_x'].asKey(), 'x')
375 sourceMapper.addMapping(sourceSchema['slot_Centroid_y'].asKey(), 'y')
376 # Add the mapping if the field exists in the input catalog.
377 # If the field does not exist, simply add it (set to False).
378 # This field is not required for calibration, but is useful
379 # to collate if available.
380 try:
381 sourceMapper.addMapping(sourceSchema[self.config.psfCandidateName].asKey(),
382 'psf_candidate')
383 except LookupError:
384 sourceMapper.editOutputSchema().addField(
385 "psf_candidate", type='Flag',
386 doc=("Flag set if the source was a candidate for PSF determination, "
387 "as determined by the star selector."))
388
389 # and add the fields we want
390 sourceMapper.editOutputSchema().addField(
391 "visit", type=np.int64, doc="Visit number")
392 sourceMapper.editOutputSchema().addField(
393 "ccd", type=np.int32, doc="CCD number")
394 sourceMapper.editOutputSchema().addField(
395 "instMag", type=np.float32, doc="Instrumental magnitude")
396 sourceMapper.editOutputSchema().addField(
397 "instMagErr", type=np.float32, doc="Instrumental magnitude error")
398 sourceMapper.editOutputSchema().addField(
399 "jacobian", type=np.float32, doc="Relative pixel scale from wcs jacobian")
400 sourceMapper.editOutputSchema().addField(
401 "deltaMagBkg", type=np.float32, doc="Change in magnitude due to local background offset")
402 sourceMapper.editOutputSchema().addField(
403 "deltaMagAper", type=np.float32, doc="Change in magnitude from larger to smaller aperture")
404
405 return sourceMapper
406
407 def fgcmMatchStars(self, visitCat, obsCat, lutHandle=None):
408 """
409 Use FGCM code to match observations into unique stars.
410
411 Parameters
412 ----------
413 visitCat: `afw.table.BaseCatalog`
414 Catalog with visit data for fgcm
415 obsCat: `afw.table.BaseCatalog`
416 Full catalog of star observations for fgcm
417 lutHandle: `lsst.daf.butler.DeferredDatasetHandle`, optional
418 Data reference to fgcm look-up table (used if matching reference stars).
419
420 Returns
421 -------
422 fgcmStarIdCat: `afw.table.BaseCatalog`
423 Catalog of unique star identifiers and index keys
424 fgcmStarIndicesCat: `afwTable.BaseCatalog`
425 Catalog of unique star indices
426 fgcmRefCat: `afw.table.BaseCatalog`
427 Catalog of matched reference stars.
428 Will be None if `config.doReferenceMatches` is False.
429 """
430 # get filter names into a numpy array...
431 # This is the type that is expected by the fgcm code
432 visitFilterNames = np.zeros(len(visitCat), dtype='S30')
433 for i in range(len(visitCat)):
434 visitFilterNames[i] = visitCat[i]['physicalFilter']
435
436 # match to put filterNames with observations
437 visitIndex = np.searchsorted(visitCat['visit'],
438 obsCat['visit'])
439
440 obsFilterNames = visitFilterNames[visitIndex]
441
442 if self.config.doReferenceMatches:
443 # Get the reference filter names, using the LUT
444 lutCat = lutHandle.get()
445
446 stdFilterDict = {filterName: stdFilter for (filterName, stdFilter) in
447 zip(lutCat[0]['physicalFilters'].split(','),
448 lutCat[0]['stdPhysicalFilters'].split(','))}
449 stdLambdaDict = {stdFilter: stdLambda for (stdFilter, stdLambda) in
450 zip(lutCat[0]['stdPhysicalFilters'].split(','),
451 lutCat[0]['lambdaStdFilter'])}
452
453 del lutCat
454
455 referenceFilterNames = self._getReferenceFilterNames(visitCat,
456 stdFilterDict,
457 stdLambdaDict)
458 self.log.info("Using the following reference filters: %s" %
459 (', '.join(referenceFilterNames)))
460
461 else:
462 # This should be an empty list
463 referenceFilterNames = []
464
465 # make the fgcm starConfig dict
466 starConfig = {'logger': self.log,
467 'useHtm': True,
468 'filterToBand': self.config.physicalFilterMap,
469 'requiredBands': self.config.requiredBands,
470 'minPerBand': self.config.minPerBand,
471 'matchRadius': self.config.matchRadius,
472 'isolationRadius': self.config.isolationRadius,
473 'matchNSide': self.config.matchNside,
474 'coarseNSide': self.config.coarseNside,
475 'densNSide': self.config.densityCutNside,
476 'densMaxPerPixel': self.config.densityCutMaxPerPixel,
477 'randomSeed': self.config.randomSeed,
478 'primaryBands': self.config.primaryBands,
479 'referenceFilterNames': referenceFilterNames}
480
481 # initialize the FgcmMakeStars object
482 fgcmMakeStars = fgcm.FgcmMakeStars(starConfig)
483
484 # make the primary stars
485 # note that the ra/dec native Angle format is radians
486 # We determine the conversion from the native units (typically
487 # radians) to degrees for the first observation. This allows us
488 # to treate ra/dec as numpy arrays rather than Angles, which would
489 # be approximately 600x slower.
490 conv = obsCat[0]['ra'].asDegrees() / float(obsCat[0]['ra'])
491 fgcmMakeStars.makePrimaryStars(obsCat['ra'] * conv,
492 obsCat['dec'] * conv,
493 filterNameArray=obsFilterNames,
494 bandSelected=False)
495
496 # and match all the stars
497 fgcmMakeStars.makeMatchedStars(obsCat['ra'] * conv,
498 obsCat['dec'] * conv,
499 obsFilterNames)
500
501 if self.config.doReferenceMatches:
502 fgcmMakeStars.makeReferenceMatches(self.fgcmLoadReferenceCatalog)
503
504 # now persist
505
506 objSchema = self._makeFgcmObjSchema()
507
508 # make catalog and records
509 fgcmStarIdCat = afwTable.BaseCatalog(objSchema)
510 fgcmStarIdCat.reserve(fgcmMakeStars.objIndexCat.size)
511 for i in range(fgcmMakeStars.objIndexCat.size):
512 fgcmStarIdCat.addNew()
513
514 # fill the catalog
515 fgcmStarIdCat['fgcm_id'][:] = fgcmMakeStars.objIndexCat['fgcm_id']
516 fgcmStarIdCat['ra'][:] = fgcmMakeStars.objIndexCat['ra']
517 fgcmStarIdCat['dec'][:] = fgcmMakeStars.objIndexCat['dec']
518 fgcmStarIdCat['obsArrIndex'][:] = fgcmMakeStars.objIndexCat['obsarrindex']
519 fgcmStarIdCat['nObs'][:] = fgcmMakeStars.objIndexCat['nobs']
520
521 obsSchema = self._makeFgcmObsSchema()
522
523 fgcmStarIndicesCat = afwTable.BaseCatalog(obsSchema)
524 fgcmStarIndicesCat.reserve(fgcmMakeStars.obsIndexCat.size)
525 for i in range(fgcmMakeStars.obsIndexCat.size):
526 fgcmStarIndicesCat.addNew()
527
528 fgcmStarIndicesCat['obsIndex'][:] = fgcmMakeStars.obsIndexCat['obsindex']
529
530 if self.config.doReferenceMatches:
531 refSchema = self._makeFgcmRefSchema(len(referenceFilterNames))
532
533 fgcmRefCat = afwTable.BaseCatalog(refSchema)
534 fgcmRefCat.reserve(fgcmMakeStars.referenceCat.size)
535
536 for i in range(fgcmMakeStars.referenceCat.size):
537 fgcmRefCat.addNew()
538
539 fgcmRefCat['fgcm_id'][:] = fgcmMakeStars.referenceCat['fgcm_id']
540 fgcmRefCat['refMag'][:, :] = fgcmMakeStars.referenceCat['refMag']
541 fgcmRefCat['refMagErr'][:, :] = fgcmMakeStars.referenceCat['refMagErr']
542
543 md = PropertyList()
544 md.set("REFSTARS_FORMAT_VERSION", REFSTARS_FORMAT_VERSION)
545 md.set("FILTERNAMES", referenceFilterNames)
546 fgcmRefCat.setMetadata(md)
547
548 else:
549 fgcmRefCat = None
550
551 return fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat
552
553 def _makeFgcmVisitSchema(self, nCcd):
554 """
555 Make a schema for an fgcmVisitCatalog
556
557 Parameters
558 ----------
559 nCcd: `int`
560 Number of CCDs in the camera
561
562 Returns
563 -------
564 schema: `afwTable.Schema`
565 """
566
567 schema = afwTable.Schema()
568 schema.addField('visit', type=np.int64, doc="Visit number")
569 schema.addField('physicalFilter', type=str, size=30, doc="Physical filter")
570 schema.addField('telra', type=np.float64, doc="Pointing RA (deg)")
571 schema.addField('teldec', type=np.float64, doc="Pointing Dec (deg)")
572 schema.addField('telha', type=np.float64, doc="Pointing Hour Angle (deg)")
573 schema.addField('telrot', type=np.float64, doc="Camera rotation (deg)")
574 schema.addField('mjd', type=np.float64, doc="MJD of visit")
575 schema.addField('exptime', type=np.float32, doc="Exposure time")
576 schema.addField('pmb', type=np.float32, doc="Pressure (millibar)")
577 schema.addField('psfSigma', type=np.float32, doc="PSF sigma (median); pixels")
578 schema.addField('psfFwhm', type=np.float32, doc="PSF FWHM (median); arcseconds")
579 schema.addField('deltaAper', type=np.float32, doc="Delta-aperture")
580 schema.addField('skyBackground', type=np.float32, doc="Sky background (ADU) (reference CCD)")
581 # the following field is not used yet
582 schema.addField('deepFlag', type=np.int32, doc="Deep observation")
583 schema.addField('scaling', type='ArrayD', doc="Scaling applied due to flat adjustment",
584 size=nCcd)
585 schema.addField('used', type=np.int32, doc="This visit has been ingested.")
586 schema.addField('sources_read', type='Flag', doc="This visit had sources read.")
587
588 return schema
589
591 """
592 Make a schema for the objIndexCat from fgcmMakeStars
593
594 Returns
595 -------
596 schema: `afwTable.Schema`
597 """
598
599 objSchema = afwTable.Schema()
600 objSchema.addField('fgcm_id', type=np.int32, doc='FGCM Unique ID')
601 # Will investigate making these angles...
602 objSchema.addField('ra', type=np.float64, doc='Mean object RA (deg)')
603 objSchema.addField('dec', type=np.float64, doc='Mean object Dec (deg)')
604 objSchema.addField('obsArrIndex', type=np.int32,
605 doc='Index in obsIndexTable for first observation')
606 objSchema.addField('nObs', type=np.int32, doc='Total number of observations')
607
608 return objSchema
609
611 """
612 Make a schema for the obsIndexCat from fgcmMakeStars
613
614 Returns
615 -------
616 schema: `afwTable.Schema`
617 """
618
619 obsSchema = afwTable.Schema()
620 obsSchema.addField('obsIndex', type=np.int32, doc='Index in observation table')
621
622 return obsSchema
623
624 def _makeFgcmRefSchema(self, nReferenceBands):
625 """
626 Make a schema for the referenceCat from fgcmMakeStars
627
628 Parameters
629 ----------
630 nReferenceBands: `int`
631 Number of reference bands
632
633 Returns
634 -------
635 schema: `afwTable.Schema`
636 """
637
638 refSchema = afwTable.Schema()
639 refSchema.addField('fgcm_id', type=np.int32, doc='FGCM Unique ID')
640 refSchema.addField('refMag', type='ArrayF', doc='Reference magnitude array (AB)',
641 size=nReferenceBands)
642 refSchema.addField('refMagErr', type='ArrayF', doc='Reference magnitude error array',
643 size=nReferenceBands)
644
645 return refSchema
646
647 def _getReferenceFilterNames(self, visitCat, stdFilterDict, stdLambdaDict):
648 """
649 Get the reference filter names, in wavelength order, from the visitCat and
650 information from the look-up-table.
651
652 Parameters
653 ----------
654 visitCat: `afw.table.BaseCatalog`
655 Catalog with visit data for FGCM
656 stdFilterDict: `dict`
657 Mapping of filterName to stdFilterName from LUT
658 stdLambdaDict: `dict`
659 Mapping of stdFilterName to stdLambda from LUT
660
661 Returns
662 -------
663 referenceFilterNames: `list`
664 Wavelength-ordered list of reference filter names
665 """
666
667 # Find the unique list of filter names in visitCat
668 filterNames = np.unique(visitCat.asAstropy()['physicalFilter'])
669
670 # Find the unique list of "standard" filters
671 stdFilterNames = {stdFilterDict[filterName] for filterName in filterNames}
672
673 # And sort these by wavelength
674 referenceFilterNames = sorted(stdFilterNames, key=stdLambdaDict.get)
675
676 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)