21 """Utility functions for fgcmcal.
23 This file contains utility functions that are used by more than one task,
24 and do not need to be part of a task.
30 from deprecated.sphinx
import deprecated
32 from lsst.daf.base
import PropertyList
33 import lsst.daf.persistence
as dafPersist
34 import lsst.afw.cameraGeom
as afwCameraGeom
35 import lsst.afw.table
as afwTable
36 import lsst.afw.image
as afwImage
37 import lsst.afw.math
as afwMath
38 import lsst.geom
as geom
39 from lsst.obs.base
import createInitialSkyWcs
40 from lsst.obs.base
import Instrument
45 FGCM_EXP_FIELD =
'VISIT'
46 FGCM_CCD_FIELD =
'DETECTOR'
47 FGCM_ILLEGAL_VALUE = -9999.0
51 resetFitParameters, outputZeropoints,
52 lutFilterNames, tract=None):
54 Make the FGCM fit cycle configuration dict
58 config: `lsst.fgcmcal.FgcmFitCycleConfig`
62 camera: `lsst.afw.cameraGeom.Camera`
63 Camera from the butler
65 Maximum number of iterations
66 resetFitParameters: `bool`
67 Reset fit parameters before fitting?
68 outputZeropoints: `bool`
69 Compute zeropoints for output?
70 lutFilterNames : array-like, `str`
71 Array of physical filter names in the LUT.
72 tract: `int`, optional
73 Tract number for extending the output file name for debugging.
79 Configuration dictionary for fgcm
82 notFitBands = [b
for b
in config.bands
if b
not in config.fitBands]
86 for ccut
in config.starColorCuts:
87 parts = ccut.split(
',')
88 starColorCutList.append([parts[0], parts[1], float(parts[2]), float(parts[3])])
93 mirrorArea = np.pi*(camera.telescopeDiameter*100./2.)**2.
96 gains = [amp.getGain()
for detector
in camera
for amp
in detector.getAmplifiers()]
97 cameraGain = float(np.median(gains))
100 filterToBand = {filterName: config.physicalFilterMap[filterName]
for
101 filterName
in lutFilterNames}
104 outfileBase = config.outfileBase
106 outfileBase =
'%s-%06d' % (config.outfileBase, tract)
109 configDict = {
'outfileBase': outfileBase,
111 'exposureFile':
None,
115 'mirrorArea': mirrorArea,
116 'cameraGain': cameraGain,
117 'ccdStartIndex': camera[0].getId(),
118 'expField': FGCM_EXP_FIELD,
119 'ccdField': FGCM_CCD_FIELD,
120 'seeingField':
'DELTA_APER',
121 'fwhmField':
'PSFSIGMA',
122 'skyBrightnessField':
'SKYBACKGROUND',
123 'deepFlag':
'DEEPFLAG',
124 'bands': list(config.bands),
125 'fitBands': list(config.fitBands),
126 'notFitBands': notFitBands,
127 'requiredBands': list(config.requiredBands),
128 'filterToBand': filterToBand,
130 'nCore': config.nCore,
131 'nStarPerRun': config.nStarPerRun,
132 'nExpPerRun': config.nExpPerRun,
133 'reserveFraction': config.reserveFraction,
134 'freezeStdAtmosphere': config.freezeStdAtmosphere,
135 'precomputeSuperStarInitialCycle': config.precomputeSuperStarInitialCycle,
136 'superStarSubCCDDict': dict(config.superStarSubCcdDict),
137 'superStarSubCCDChebyshevOrder': config.superStarSubCcdChebyshevOrder,
138 'superStarSubCCDTriangular': config.superStarSubCcdTriangular,
139 'superStarSigmaClip': config.superStarSigmaClip,
140 'focalPlaneSigmaClip': config.focalPlaneSigmaClip,
141 'ccdGraySubCCDDict': dict(config.ccdGraySubCcdDict),
142 'ccdGraySubCCDChebyshevOrder': config.ccdGraySubCcdChebyshevOrder,
143 'ccdGraySubCCDTriangular': config.ccdGraySubCcdTriangular,
144 'ccdGrayFocalPlaneDict': dict(config.ccdGrayFocalPlaneDict),
145 'ccdGrayFocalPlaneChebyshevOrder': config.ccdGrayFocalPlaneChebyshevOrder,
146 'ccdGrayFocalPlaneFitMinCcd': config.ccdGrayFocalPlaneFitMinCcd,
147 'cycleNumber': config.cycleNumber,
149 'deltaMagBkgOffsetPercentile': config.deltaMagBkgOffsetPercentile,
150 'deltaMagBkgPerCcd': config.deltaMagBkgPerCcd,
151 'UTBoundary': config.utBoundary,
152 'washMJDs': config.washMjds,
153 'epochMJDs': config.epochMjds,
154 'coatingMJDs': config.coatingMjds,
155 'minObsPerBand': config.minObsPerBand,
156 'latitude': config.latitude,
157 'defaultCameraOrientation': config.defaultCameraOrientation,
158 'brightObsGrayMax': config.brightObsGrayMax,
159 'minStarPerCCD': config.minStarPerCcd,
160 'minCCDPerExp': config.minCcdPerExp,
161 'maxCCDGrayErr': config.maxCcdGrayErr,
162 'minStarPerExp': config.minStarPerExp,
163 'minExpPerNight': config.minExpPerNight,
164 'expGrayInitialCut': config.expGrayInitialCut,
165 'expGrayPhotometricCutDict': dict(config.expGrayPhotometricCutDict),
166 'expGrayHighCutDict': dict(config.expGrayHighCutDict),
167 'expGrayRecoverCut': config.expGrayRecoverCut,
168 'expVarGrayPhotometricCutDict': dict(config.expVarGrayPhotometricCutDict),
169 'expGrayErrRecoverCut': config.expGrayErrRecoverCut,
170 'refStarSnMin': config.refStarSnMin,
171 'refStarOutlierNSig': config.refStarOutlierNSig,
172 'applyRefStarColorCuts': config.applyRefStarColorCuts,
173 'illegalValue': FGCM_ILLEGAL_VALUE,
174 'starColorCuts': starColorCutList,
175 'aperCorrFitNBins': config.aperCorrFitNBins,
176 'aperCorrInputSlopeDict': dict(config.aperCorrInputSlopeDict),
177 'sedBoundaryTermDict': config.sedboundaryterms.toDict()[
'data'],
178 'sedTermDict': config.sedterms.toDict()[
'data'],
179 'colorSplitBands': list(config.colorSplitBands),
180 'sigFgcmMaxErr': config.sigFgcmMaxErr,
181 'sigFgcmMaxEGrayDict': dict(config.sigFgcmMaxEGrayDict),
182 'ccdGrayMaxStarErr': config.ccdGrayMaxStarErr,
183 'approxThroughputDict': dict(config.approxThroughputDict),
184 'sigmaCalRange': list(config.sigmaCalRange),
185 'sigmaCalFitPercentile': list(config.sigmaCalFitPercentile),
186 'sigmaCalPlotPercentile': list(config.sigmaCalPlotPercentile),
187 'sigma0Phot': config.sigma0Phot,
188 'mapLongitudeRef': config.mapLongitudeRef,
189 'mapNSide': config.mapNSide,
192 'useRetrievedPwv':
False,
193 'useNightlyRetrievedPwv':
False,
194 'pwvRetrievalSmoothBlock': 25,
195 'useQuadraticPwv': config.useQuadraticPwv,
196 'useRetrievedTauInit':
False,
197 'tauRetrievalMinCCDPerNight': 500,
198 'modelMagErrors': config.modelMagErrors,
199 'instrumentParsPerBand': config.instrumentParsPerBand,
200 'instrumentSlopeMinDeltaT': config.instrumentSlopeMinDeltaT,
201 'fitMirrorChromaticity': config.fitMirrorChromaticity,
202 'useRepeatabilityForExpGrayCutsDict': dict(config.useRepeatabilityForExpGrayCutsDict),
203 'autoPhotometricCutNSig': config.autoPhotometricCutNSig,
204 'autoHighCutNSig': config.autoHighCutNSig,
206 'quietMode': config.quietMode,
207 'randomSeed': config.randomSeed,
208 'outputStars':
False,
209 'outputPath': os.path.abspath(
'.'),
212 'resetParameters': resetFitParameters,
213 'doPlots': config.doPlots,
214 'outputFgcmcalZpts':
True,
215 'outputZeropoints': outputZeropoints}
222 Translate the FGCM look-up-table into an fgcm-compatible object
226 lutCat: `lsst.afw.table.BaseCatalog`
227 Catalog describing the FGCM look-up table
228 physicalFilterMap: `dict`
229 Physical filter to band mapping
233 fgcmLut: `lsst.fgcm.FgcmLut`
234 Lookup table for FGCM
235 lutIndexVals: `numpy.ndarray`
236 Numpy array with LUT index information for FGCM
237 lutStd: `numpy.ndarray`
238 Numpy array with LUT standard throughput values for FGCM
242 After running this code, it is wise to `del lutCat` to clear the memory.
246 lutFilterNames = np.array(lutCat[0][
'physicalFilters'].split(
','), dtype=
'U')
247 lutStdFilterNames = np.array(lutCat[0][
'stdPhysicalFilters'].split(
','), dtype=
'U')
252 lutIndexVals = np.zeros(1, dtype=[(
'FILTERNAMES', lutFilterNames.dtype.str,
253 lutFilterNames.size),
254 (
'STDFILTERNAMES', lutStdFilterNames.dtype.str,
255 lutStdFilterNames.size),
256 (
'PMB',
'f8', lutCat[0][
'pmb'].size),
257 (
'PMBFACTOR',
'f8', lutCat[0][
'pmbFactor'].size),
258 (
'PMBELEVATION',
'f8'),
259 (
'LAMBDANORM',
'f8'),
260 (
'PWV',
'f8', lutCat[0][
'pwv'].size),
261 (
'O3',
'f8', lutCat[0][
'o3'].size),
262 (
'TAU',
'f8', lutCat[0][
'tau'].size),
263 (
'ALPHA',
'f8', lutCat[0][
'alpha'].size),
264 (
'ZENITH',
'f8', lutCat[0][
'zenith'].size),
267 lutIndexVals[
'FILTERNAMES'][:] = lutFilterNames
268 lutIndexVals[
'STDFILTERNAMES'][:] = lutStdFilterNames
269 lutIndexVals[
'PMB'][:] = lutCat[0][
'pmb']
270 lutIndexVals[
'PMBFACTOR'][:] = lutCat[0][
'pmbFactor']
271 lutIndexVals[
'PMBELEVATION'] = lutCat[0][
'pmbElevation']
272 lutIndexVals[
'LAMBDANORM'] = lutCat[0][
'lambdaNorm']
273 lutIndexVals[
'PWV'][:] = lutCat[0][
'pwv']
274 lutIndexVals[
'O3'][:] = lutCat[0][
'o3']
275 lutIndexVals[
'TAU'][:] = lutCat[0][
'tau']
276 lutIndexVals[
'ALPHA'][:] = lutCat[0][
'alpha']
277 lutIndexVals[
'ZENITH'][:] = lutCat[0][
'zenith']
278 lutIndexVals[
'NCCD'] = lutCat[0][
'nCcd']
281 lutStd = np.zeros(1, dtype=[(
'PMBSTD',
'f8'),
287 (
'LAMBDARANGE',
'f8', 2),
288 (
'LAMBDASTEP',
'f8'),
289 (
'LAMBDASTD',
'f8', lutFilterNames.size),
290 (
'LAMBDASTDFILTER',
'f8', lutStdFilterNames.size),
291 (
'I0STD',
'f8', lutFilterNames.size),
292 (
'I1STD',
'f8', lutFilterNames.size),
293 (
'I10STD',
'f8', lutFilterNames.size),
294 (
'I2STD',
'f8', lutFilterNames.size),
295 (
'LAMBDAB',
'f8', lutFilterNames.size),
296 (
'ATMLAMBDA',
'f8', lutCat[0][
'atmLambda'].size),
297 (
'ATMSTDTRANS',
'f8', lutCat[0][
'atmStdTrans'].size)])
298 lutStd[
'PMBSTD'] = lutCat[0][
'pmbStd']
299 lutStd[
'PWVSTD'] = lutCat[0][
'pwvStd']
300 lutStd[
'O3STD'] = lutCat[0][
'o3Std']
301 lutStd[
'TAUSTD'] = lutCat[0][
'tauStd']
302 lutStd[
'ALPHASTD'] = lutCat[0][
'alphaStd']
303 lutStd[
'ZENITHSTD'] = lutCat[0][
'zenithStd']
304 lutStd[
'LAMBDARANGE'][:] = lutCat[0][
'lambdaRange'][:]
305 lutStd[
'LAMBDASTEP'] = lutCat[0][
'lambdaStep']
306 lutStd[
'LAMBDASTD'][:] = lutCat[0][
'lambdaStd']
307 lutStd[
'LAMBDASTDFILTER'][:] = lutCat[0][
'lambdaStdFilter']
308 lutStd[
'I0STD'][:] = lutCat[0][
'i0Std']
309 lutStd[
'I1STD'][:] = lutCat[0][
'i1Std']
310 lutStd[
'I10STD'][:] = lutCat[0][
'i10Std']
311 lutStd[
'I2STD'][:] = lutCat[0][
'i2Std']
312 lutStd[
'LAMBDAB'][:] = lutCat[0][
'lambdaB']
313 lutStd[
'ATMLAMBDA'][:] = lutCat[0][
'atmLambda'][:]
314 lutStd[
'ATMSTDTRANS'][:] = lutCat[0][
'atmStdTrans'][:]
316 lutTypes = [row[
'luttype']
for row
in lutCat]
319 lutFlat = np.zeros(lutCat[0][
'lut'].size, dtype=[(
'I0',
'f4'),
322 lutFlat[
'I0'][:] = lutCat[lutTypes.index(
'I0')][
'lut'][:]
323 lutFlat[
'I1'][:] = lutCat[lutTypes.index(
'I1')][
'lut'][:]
325 lutDerivFlat = np.zeros(lutCat[0][
'lut'].size, dtype=[(
'D_LNPWV',
'f4'),
329 (
'D_SECZENITH',
'f4'),
330 (
'D_LNPWV_I1',
'f4'),
332 (
'D_LNTAU_I1',
'f4'),
333 (
'D_ALPHA_I1',
'f4'),
334 (
'D_SECZENITH_I1',
'f4')])
336 for name
in lutDerivFlat.dtype.names:
337 lutDerivFlat[name][:] = lutCat[lutTypes.index(name)][
'lut'][:]
344 fgcmLut = fgcm.FgcmLUT(lutIndexVals, lutFlat, lutDerivFlat, lutStd,
345 filterToBand=physicalFilterMap)
347 return fgcmLut, lutIndexVals, lutStd
352 Translate the FGCM visit catalog to an fgcm-compatible object
356 visitCat: `lsst.afw.table.BaseCatalog`
357 FGCM visitCat from `lsst.fgcmcal.FgcmBuildStarsTask`
361 fgcmExpInfo: `numpy.ndarray`
362 Numpy array for visit information for FGCM
366 After running this code, it is wise to `del visitCat` to clear the memory.
369 fgcmExpInfo = np.zeros(len(visitCat), dtype=[(
'VISIT',
'i8'),
373 (
'DELTA_APER',
'f8'),
374 (
'SKYBACKGROUND',
'f8'),
381 (
'FILTERNAME',
'a50')])
382 fgcmExpInfo[
'VISIT'][:] = visitCat[
'visit']
383 fgcmExpInfo[
'MJD'][:] = visitCat[
'mjd']
384 fgcmExpInfo[
'EXPTIME'][:] = visitCat[
'exptime']
385 fgcmExpInfo[
'DEEPFLAG'][:] = visitCat[
'deepFlag']
386 fgcmExpInfo[
'TELHA'][:] = visitCat[
'telha']
387 fgcmExpInfo[
'TELRA'][:] = visitCat[
'telra']
388 fgcmExpInfo[
'TELDEC'][:] = visitCat[
'teldec']
389 fgcmExpInfo[
'TELROT'][:] = visitCat[
'telrot']
390 fgcmExpInfo[
'PMB'][:] = visitCat[
'pmb']
391 fgcmExpInfo[
'PSFSIGMA'][:] = visitCat[
'psfSigma']
392 fgcmExpInfo[
'DELTA_APER'][:] = visitCat[
'deltaAper']
393 fgcmExpInfo[
'SKYBACKGROUND'][:] = visitCat[
'skyBackground']
396 fgcmExpInfo[
'FILTERNAME'][:] = visitCat.asAstropy()[
'physicalFilter']
401 @deprecated(reason="This method is no longer used in fgcmcal. It will be removed after v23.",
version="v23.0", category=FutureWarning)
404 Compute the CCD offsets in ra/dec and x/y space
408 camera: `lsst.afw.cameraGeom.Camera`
409 defaultOrientation: `float`
410 Default camera orientation (degrees)
414 ccdOffsets: `numpy.ndarray`
415 Numpy array with ccd offset information for input to FGCM.
416 Angular units are degrees, and x/y units are pixels.
421 ccdOffsets = np.zeros(len(camera), dtype=[(
'CCDNUM',
'i4'),
431 boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees)
436 if camera.getName() ==
'HSC' and np.isnan(defaultOrientation):
437 orientation = 270*geom.degrees
439 orientation = defaultOrientation*geom.degrees
443 visitInfo = afwImage.VisitInfo(boresightRaDec=boresight,
444 boresightRotAngle=orientation,
445 rotType=afwImage.RotType.SKY)
447 for i, detector
in enumerate(camera):
448 ccdOffsets[
'CCDNUM'][i] = detector.getId()
450 wcs = createInitialSkyWcs(visitInfo, detector, flipX)
452 detCenter = wcs.pixelToSky(detector.getCenter(afwCameraGeom.PIXELS))
453 ccdOffsets[
'DELTA_RA'][i] = (detCenter.getRa() - boresight.getRa()).asDegrees()
454 ccdOffsets[
'DELTA_DEC'][i] = (detCenter.getDec() - boresight.getDec()).asDegrees()
456 bbox = detector.getBBox()
458 detCorner1 = wcs.pixelToSky(geom.Point2D(bbox.getMin()))
459 detCorner2 = wcs.pixelToSky(geom.Point2D(bbox.getMax()))
461 ccdOffsets[
'RA_SIZE'][i] = np.abs((detCorner2.getRa() - detCorner1.getRa()).asDegrees())
462 ccdOffsets[
'DEC_SIZE'][i] = np.abs((detCorner2.getDec() - detCorner1.getDec()).asDegrees())
464 ccdOffsets[
'X_SIZE'][i] = bbox.getMaxX()
465 ccdOffsets[
'Y_SIZE'][i] = bbox.getMaxY()
472 Compute the median pixel scale in the camera
477 Average pixel scale (arcsecond) over the camera
480 boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees)
481 orientation = 0.0*geom.degrees
485 visitInfo = afwImage.VisitInfo(boresightRaDec=boresight,
486 boresightRotAngle=orientation,
487 rotType=afwImage.RotType.SKY)
489 pixelScales = np.zeros(len(camera))
490 for i, detector
in enumerate(camera):
491 wcs = createInitialSkyWcs(visitInfo, detector, flipX)
492 pixelScales[i] = wcs.getPixelScale().asArcseconds()
494 ok, = np.where(pixelScales > 0.0)
495 return np.median(pixelScales[ok])
500 Compute the approximate pixel area bounded fields from the camera
505 camera: `lsst.afw.cameraGeom.Camera`
509 approxPixelAreaFields: `dict`
510 Dictionary of approximate area fields, keyed with detector ID
517 boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees)
522 visitInfo = afwImage.VisitInfo(boresightRaDec=boresight,
523 boresightRotAngle=0.0*geom.degrees,
524 rotType=afwImage.RotType.SKY)
526 approxPixelAreaFields = {}
528 for i, detector
in enumerate(camera):
529 key = detector.getId()
531 wcs = createInitialSkyWcs(visitInfo, detector, flipX)
532 bbox = detector.getBBox()
534 areaField = afwMath.PixelAreaBoundedField(bbox, wcs,
535 unit=geom.arcseconds, scaling=areaScaling)
536 approxAreaField = afwMath.ChebyshevBoundedField.approximate(areaField)
538 approxPixelAreaFields[key] = approxAreaField
540 return approxPixelAreaFields
545 Make the zeropoint schema
549 superStarChebyshevSize: `int`
550 Length of the superstar chebyshev array
551 zptChebyshevSize: `int`
552 Length of the zeropoint chebyshev array
556 zptSchema: `lsst.afw.table.schema`
559 zptSchema = afwTable.Schema()
561 zptSchema.addField(
'visit', type=np.int32, doc=
'Visit number')
562 zptSchema.addField(
'detector', type=np.int32, doc=
'Detector ID number')
563 zptSchema.addField(
'fgcmFlag', type=np.int32, doc=(
'FGCM flag value: '
564 '1: Photometric, used in fit; '
565 '2: Photometric, not used in fit; '
566 '4: Non-photometric, on partly photometric night; '
567 '8: Non-photometric, on non-photometric night; '
568 '16: No zeropoint could be determined; '
569 '32: Too few stars for reliable gray computation'))
570 zptSchema.addField(
'fgcmZpt', type=np.float64, doc=
'FGCM zeropoint (center of CCD)')
571 zptSchema.addField(
'fgcmZptErr', type=np.float64,
572 doc=
'Error on zeropoint, estimated from repeatability + number of obs')
573 zptSchema.addField(
'fgcmfZptChebXyMax', type=
'ArrayD', size=2,
574 doc=
'maximum x/maximum y to scale to apply chebyshev parameters')
575 zptSchema.addField(
'fgcmfZptCheb', type=
'ArrayD',
576 size=zptChebyshevSize,
577 doc=
'Chebyshev parameters (flattened) for zeropoint')
578 zptSchema.addField(
'fgcmfZptSstarCheb', type=
'ArrayD',
579 size=superStarChebyshevSize,
580 doc=
'Chebyshev parameters (flattened) for superStarFlat')
581 zptSchema.addField(
'fgcmI0', type=np.float64, doc=
'Integral of the passband')
582 zptSchema.addField(
'fgcmI10', type=np.float64, doc=
'Normalized chromatic integral')
583 zptSchema.addField(
'fgcmR0', type=np.float64,
584 doc=
'Retrieved i0 integral, estimated from stars (only for flag 1)')
585 zptSchema.addField(
'fgcmR10', type=np.float64,
586 doc=
'Retrieved i10 integral, estimated from stars (only for flag 1)')
587 zptSchema.addField(
'fgcmGry', type=np.float64,
588 doc=
'Estimated gray extinction relative to atmospheric solution; '
589 'only for fgcmFlag <= 4 (see fgcmFlag) ')
590 zptSchema.addField(
'fgcmDeltaChrom', type=np.float64,
591 doc=
'Mean chromatic correction for stars in this ccd; '
592 'only for fgcmFlag <= 4 (see fgcmFlag)')
593 zptSchema.addField(
'fgcmZptVar', type=np.float64, doc=
'Variance of zeropoint over ccd')
594 zptSchema.addField(
'fgcmTilings', type=np.float64,
595 doc=
'Number of photometric tilings used for solution for ccd')
596 zptSchema.addField(
'fgcmFpGry', type=np.float64,
597 doc=
'Average gray extinction over the full focal plane '
598 '(same for all ccds in a visit)')
599 zptSchema.addField(
'fgcmFpGryBlue', type=np.float64,
600 doc=
'Average gray extinction over the full focal plane '
601 'for 25% bluest stars')
602 zptSchema.addField(
'fgcmFpGryBlueErr', type=np.float64,
603 doc=
'Error on Average gray extinction over the full focal plane '
604 'for 25% bluest stars')
605 zptSchema.addField(
'fgcmFpGryRed', type=np.float64,
606 doc=
'Average gray extinction over the full focal plane '
607 'for 25% reddest stars')
608 zptSchema.addField(
'fgcmFpGryRedErr', type=np.float64,
609 doc=
'Error on Average gray extinction over the full focal plane '
610 'for 25% reddest stars')
611 zptSchema.addField(
'fgcmFpVar', type=np.float64,
612 doc=
'Variance of gray extinction over the full focal plane '
613 '(same for all ccds in a visit)')
614 zptSchema.addField(
'fgcmDust', type=np.float64,
615 doc=
'Gray dust extinction from the primary/corrector'
616 'at the time of the exposure')
617 zptSchema.addField(
'fgcmFlat', type=np.float64, doc=
'Superstarflat illumination correction')
618 zptSchema.addField(
'fgcmAperCorr', type=np.float64, doc=
'Aperture correction estimated by fgcm')
619 zptSchema.addField(
'fgcmDeltaMagBkg', type=np.float64,
620 doc=(
'Local background correction from brightest percentile '
621 '(value set by deltaMagBkgOffsetPercentile) calibration '
623 zptSchema.addField(
'exptime', type=np.float32, doc=
'Exposure time')
624 zptSchema.addField(
'filtername', type=str, size=10, doc=
'Filter name')
631 Make the zeropoint catalog for persistence
635 zptSchema: `lsst.afw.table.Schema`
636 Zeropoint catalog schema
637 zpStruct: `numpy.ndarray`
638 Zeropoint structure from fgcm
642 zptCat: `afwTable.BaseCatalog`
643 Zeropoint catalog for persistence
646 zptCat = afwTable.BaseCatalog(zptSchema)
647 zptCat.reserve(zpStruct.size)
649 for filterName
in zpStruct[
'FILTERNAME']:
650 rec = zptCat.addNew()
651 rec[
'filtername'] = filterName.decode(
'utf-8')
653 zptCat[
'visit'][:] = zpStruct[FGCM_EXP_FIELD]
654 zptCat[
'detector'][:] = zpStruct[FGCM_CCD_FIELD]
655 zptCat[
'fgcmFlag'][:] = zpStruct[
'FGCM_FLAG']
656 zptCat[
'fgcmZpt'][:] = zpStruct[
'FGCM_ZPT']
657 zptCat[
'fgcmZptErr'][:] = zpStruct[
'FGCM_ZPTERR']
658 zptCat[
'fgcmfZptChebXyMax'][:, :] = zpStruct[
'FGCM_FZPT_XYMAX']
659 zptCat[
'fgcmfZptCheb'][:, :] = zpStruct[
'FGCM_FZPT_CHEB']
660 zptCat[
'fgcmfZptSstarCheb'][:, :] = zpStruct[
'FGCM_FZPT_SSTAR_CHEB']
661 zptCat[
'fgcmI0'][:] = zpStruct[
'FGCM_I0']
662 zptCat[
'fgcmI10'][:] = zpStruct[
'FGCM_I10']
663 zptCat[
'fgcmR0'][:] = zpStruct[
'FGCM_R0']
664 zptCat[
'fgcmR10'][:] = zpStruct[
'FGCM_R10']
665 zptCat[
'fgcmGry'][:] = zpStruct[
'FGCM_GRY']
666 zptCat[
'fgcmDeltaChrom'][:] = zpStruct[
'FGCM_DELTACHROM']
667 zptCat[
'fgcmZptVar'][:] = zpStruct[
'FGCM_ZPTVAR']
668 zptCat[
'fgcmTilings'][:] = zpStruct[
'FGCM_TILINGS']
669 zptCat[
'fgcmFpGry'][:] = zpStruct[
'FGCM_FPGRY']
670 zptCat[
'fgcmFpGryBlue'][:] = zpStruct[
'FGCM_FPGRY_CSPLIT'][:, 0]
671 zptCat[
'fgcmFpGryBlueErr'][:] = zpStruct[
'FGCM_FPGRY_CSPLITERR'][:, 0]
672 zptCat[
'fgcmFpGryRed'][:] = zpStruct[
'FGCM_FPGRY_CSPLIT'][:, 2]
673 zptCat[
'fgcmFpGryRedErr'][:] = zpStruct[
'FGCM_FPGRY_CSPLITERR'][:, 2]
674 zptCat[
'fgcmFpVar'][:] = zpStruct[
'FGCM_FPVAR']
675 zptCat[
'fgcmDust'][:] = zpStruct[
'FGCM_DUST']
676 zptCat[
'fgcmFlat'][:] = zpStruct[
'FGCM_FLAT']
677 zptCat[
'fgcmAperCorr'][:] = zpStruct[
'FGCM_APERCORR']
678 zptCat[
'fgcmDeltaMagBkg'][:] = zpStruct[
'FGCM_DELTAMAGBKG']
679 zptCat[
'exptime'][:] = zpStruct[
'EXPTIME']
686 Make the atmosphere schema
690 atmSchema: `lsst.afw.table.Schema`
693 atmSchema = afwTable.Schema()
695 atmSchema.addField(
'visit', type=np.int32, doc=
'Visit number')
696 atmSchema.addField(
'pmb', type=np.float64, doc=
'Barometric pressure (mb)')
697 atmSchema.addField(
'pwv', type=np.float64, doc=
'Water vapor (mm)')
698 atmSchema.addField(
'tau', type=np.float64, doc=
'Aerosol optical depth')
699 atmSchema.addField(
'alpha', type=np.float64, doc=
'Aerosol slope')
700 atmSchema.addField(
'o3', type=np.float64, doc=
'Ozone (dobson)')
701 atmSchema.addField(
'secZenith', type=np.float64, doc=
'Secant(zenith) (~ airmass)')
702 atmSchema.addField(
'cTrans', type=np.float64, doc=
'Transmission correction factor')
703 atmSchema.addField(
'lamStd', type=np.float64, doc=
'Wavelength for transmission correction')
710 Make the atmosphere catalog for persistence
714 atmSchema: `lsst.afw.table.Schema`
715 Atmosphere catalog schema
716 atmStruct: `numpy.ndarray`
717 Atmosphere structure from fgcm
721 atmCat: `lsst.afw.table.BaseCatalog`
722 Atmosphere catalog for persistence
725 atmCat = afwTable.BaseCatalog(atmSchema)
726 atmCat.resize(atmStruct.size)
728 atmCat[
'visit'][:] = atmStruct[
'VISIT']
729 atmCat[
'pmb'][:] = atmStruct[
'PMB']
730 atmCat[
'pwv'][:] = atmStruct[
'PWV']
731 atmCat[
'tau'][:] = atmStruct[
'TAU']
732 atmCat[
'alpha'][:] = atmStruct[
'ALPHA']
733 atmCat[
'o3'][:] = atmStruct[
'O3']
734 atmCat[
'secZenith'][:] = atmStruct[
'SECZENITH']
735 atmCat[
'cTrans'][:] = atmStruct[
'CTRANS']
736 atmCat[
'lamStd'][:] = atmStruct[
'LAMSTD']
743 Make the standard star schema
748 Number of bands in standard star catalog
752 stdSchema: `lsst.afw.table.Schema`
755 stdSchema = afwTable.SimpleTable.makeMinimalSchema()
756 stdSchema.addField(
'ngood', type=
'ArrayI', doc=
'Number of good observations',
758 stdSchema.addField(
'ntotal', type=
'ArrayI', doc=
'Number of total observations',
760 stdSchema.addField(
'mag_std_noabs', type=
'ArrayF',
761 doc=
'Standard magnitude (no absolute calibration)',
763 stdSchema.addField(
'magErr_std', type=
'ArrayF',
764 doc=
'Standard magnitude error',
766 stdSchema.addField(
'npsfcand', type=
'ArrayI',
767 doc=
'Number of observations flagged as psf candidates',
773 def makeStdCat(stdSchema, stdStruct, goodBands):
775 Make the standard star catalog for persistence
779 stdSchema: `lsst.afw.table.Schema`
780 Standard star catalog schema
781 stdStruct: `numpy.ndarray`
782 Standard star structure in FGCM format
784 List of good band names used in stdStruct
788 stdCat: `lsst.afw.table.BaseCatalog`
789 Standard star catalog for persistence
792 stdCat = afwTable.SimpleCatalog(stdSchema)
793 stdCat.resize(stdStruct.size)
795 stdCat[
'id'][:] = stdStruct[
'FGCM_ID']
796 stdCat[
'coord_ra'][:] = stdStruct[
'RA'] * geom.degrees
797 stdCat[
'coord_dec'][:] = stdStruct[
'DEC'] * geom.degrees
798 stdCat[
'ngood'][:, :] = stdStruct[
'NGOOD'][:, :]
799 stdCat[
'ntotal'][:, :] = stdStruct[
'NTOTAL'][:, :]
800 stdCat[
'mag_std_noabs'][:, :] = stdStruct[
'MAG_STD'][:, :]
801 stdCat[
'magErr_std'][:, :] = stdStruct[
'MAGERR_STD'][:, :]
802 stdCat[
'npsfcand'][:, :] = stdStruct[
'NPSFCAND'][:, :]
805 md.set(
"BANDS", list(goodBands))
806 stdCat.setMetadata(md)
813 Compute the radius associated with a CircularApertureFlux field or
818 dataRef : `lsst.daf.persistence.ButlerDataRef` or
819 `lsst.daf.butler.DeferredDatasetHandle`
821 CircularApertureFlux or associated slot.
825 apertureRadius : `float`
826 Radius of the aperture field, in pixels.
830 RuntimeError: Raised if flux field is not a CircularApertureFlux, ApFlux,
831 apFlux, or associated slot.
834 if isinstance(dataRef, dafPersist.ButlerDataRef):
836 datasetType = dataRef.butlerSubset.datasetType
839 datasetType = dataRef.ref.datasetType.name
841 if datasetType ==
'src':
842 schema = dataRef.get(datasetType=
'src_schema').schema
844 fluxFieldName = schema[fluxField].asField().getName()
846 raise RuntimeError(
"Could not find %s or associated slot in schema." % (fluxField))
853 return apertureRadius
858 Compute the radius associated with a CircularApertureFlux or ApFlux field.
863 CircularApertureFlux or ApFlux
867 apertureRadius : `float`
868 Radius of the aperture field, in pixels.
872 RuntimeError: Raised if flux field is not a CircularApertureFlux,
876 m = re.search(
r'(CircularApertureFlux|ApFlux|apFlux)_(\d+)_(\d+)_', fluxField)
879 raise RuntimeError(f
"Flux field {fluxField} does not correspond to a CircularApertureFlux or ApFlux")
881 apertureRadius = float(m.groups()[1]) + float(m.groups()[2])/10.
883 return apertureRadius
888 Extract reference magnitudes from refStars for given bands and
889 associated filterMap.
893 refStars : `lsst.afw.table.BaseCatalog`
894 FGCM reference star catalog
896 List of bands for calibration
898 FGCM mapping of filter to band
902 refMag : `np.ndarray`
903 nstar x nband array of reference magnitudes
904 refMagErr : `np.ndarray`
905 nstar x nband array of reference magnitude errors
911 md = refStars.getMetadata()
912 if 'FILTERNAMES' in md:
913 filternames = md.getArray(
'FILTERNAMES')
917 refMag = np.zeros((len(refStars), len(bands)),
918 dtype=refStars[
'refMag'].dtype) + 99.0
919 refMagErr = np.zeros_like(refMag) + 99.0
920 for i, filtername
in enumerate(filternames):
924 band = filterMap[filtername]
928 ind = bands.index(band)
932 refMag[:, ind] = refStars[
'refMag'][:, i]
933 refMagErr[:, ind] = refStars[
'refMagErr'][:, i]
937 refMag = refStars[
'refMag'][:, :]
938 refMagErr = refStars[
'refMagErr'][:, :]
940 return refMag, refMagErr
944 instrument = Instrument.fromName(quantumDataId[
"instrument"], registry)
945 unboundedCollection = instrument.makeUnboundedCalibrationRunName()
947 return registry.queryDatasets(datasetType,
948 dataId=quantumDataId,
949 collections=[unboundedCollection])
950 def extractReferenceMags(refStars, bands, filterMap)
def lookupStaticCalibrations(datasetType, registry, quantumDataId, collections)
def computeApertureRadiusFromDataRef(dataRef, fluxField)
def makeStdSchema(nBands)
def makeAtmCat(atmSchema, atmStruct)
def makeConfigDict(config, log, camera, maxIter, resetFitParameters, outputZeropoints, lutFilterNames, tract=None)
def translateFgcmLut(lutCat, physicalFilterMap)
def computeReferencePixelScale(camera)
def makeZptCat(zptSchema, zpStruct)
def makeStdCat(stdSchema, stdStruct, goodBands)
def computeApertureRadiusFromName(fluxField)
def computeApproxPixelAreaFields(camera)
def makeZptSchema(superStarChebyshevSize, zptChebyshevSize)
def computeCcdOffsets(camera, defaultOrientation)
def translateVisitCatalog(visitCat)