21"""Utility functions for fgcmcal.
23This file contains utility functions that are used by more than one task,
24and do not need to be part of a task.
30from deprecated.sphinx
import deprecated
32from lsst.daf.base
import PropertyList
33import lsst.daf.persistence
as dafPersist
34import lsst.afw.cameraGeom
as afwCameraGeom
35import lsst.afw.table
as afwTable
36import lsst.afw.image
as afwImage
37import lsst.afw.math
as afwMath
38import lsst.geom
as geom
39from lsst.obs.base
import createInitialSkyWcs
40from lsst.pipe.base
import Instrument
45FGCM_EXP_FIELD =
'VISIT'
46FGCM_CCD_FIELD =
'DETECTOR'
47FGCM_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,
205 'deltaAperInnerRadiusArcsec': config.deltaAperInnerRadiusArcsec,
206 'deltaAperOuterRadiusArcsec': config.deltaAperOuterRadiusArcsec,
207 'deltaAperFitMinNgoodObs': config.deltaAperFitMinNgoodObs,
208 'deltaAperFitPerCcdNx': config.deltaAperFitPerCcdNx,
209 'deltaAperFitPerCcdNy': config.deltaAperFitPerCcdNy,
210 'deltaAperFitSpatialNside': config.deltaAperFitSpatialNside,
211 'doComputeDeltaAperExposures': config.doComputeDeltaAperPerVisit,
212 'doComputeDeltaAperStars': config.doComputeDeltaAperPerStar,
213 'doComputeDeltaAperMap': config.doComputeDeltaAperMap,
214 'doComputeDeltaAperPerCcd': config.doComputeDeltaAperPerCcd,
216 'quietMode': config.quietMode,
217 'randomSeed': config.randomSeed,
218 'outputStars':
False,
219 'outputPath': os.path.abspath(
'.'),
222 'resetParameters': resetFitParameters,
223 'doPlots': config.doPlots,
224 'outputFgcmcalZpts':
True,
225 'outputZeropoints': outputZeropoints}
232 Translate the FGCM look-up-table into an fgcm-compatible object
236 lutCat: `lsst.afw.table.BaseCatalog`
237 Catalog describing the FGCM look-up table
238 physicalFilterMap: `dict`
239 Physical filter to band mapping
243 fgcmLut: `lsst.fgcm.FgcmLut`
244 Lookup table for FGCM
245 lutIndexVals: `numpy.ndarray`
246 Numpy array
with LUT index information
for FGCM
247 lutStd: `numpy.ndarray`
248 Numpy array
with LUT standard throughput values
for FGCM
252 After running this code, it
is wise to `del lutCat` to clear the memory.
256 lutFilterNames = np.array(lutCat[0][
'physicalFilters'].split(
','), dtype=
'U')
257 lutStdFilterNames = np.array(lutCat[0][
'stdPhysicalFilters'].split(
','), dtype=
'U')
262 lutIndexVals = np.zeros(1, dtype=[(
'FILTERNAMES', lutFilterNames.dtype.str,
263 lutFilterNames.size),
264 (
'STDFILTERNAMES', lutStdFilterNames.dtype.str,
265 lutStdFilterNames.size),
266 (
'PMB',
'f8', lutCat[0][
'pmb'].size),
267 (
'PMBFACTOR',
'f8', lutCat[0][
'pmbFactor'].size),
268 (
'PMBELEVATION',
'f8'),
269 (
'LAMBDANORM',
'f8'),
270 (
'PWV',
'f8', lutCat[0][
'pwv'].size),
271 (
'O3',
'f8', lutCat[0][
'o3'].size),
272 (
'TAU',
'f8', lutCat[0][
'tau'].size),
273 (
'ALPHA',
'f8', lutCat[0][
'alpha'].size),
274 (
'ZENITH',
'f8', lutCat[0][
'zenith'].size),
277 lutIndexVals[
'FILTERNAMES'][:] = lutFilterNames
278 lutIndexVals[
'STDFILTERNAMES'][:] = lutStdFilterNames
279 lutIndexVals[
'PMB'][:] = lutCat[0][
'pmb']
280 lutIndexVals[
'PMBFACTOR'][:] = lutCat[0][
'pmbFactor']
281 lutIndexVals[
'PMBELEVATION'] = lutCat[0][
'pmbElevation']
282 lutIndexVals[
'LAMBDANORM'] = lutCat[0][
'lambdaNorm']
283 lutIndexVals[
'PWV'][:] = lutCat[0][
'pwv']
284 lutIndexVals[
'O3'][:] = lutCat[0][
'o3']
285 lutIndexVals[
'TAU'][:] = lutCat[0][
'tau']
286 lutIndexVals[
'ALPHA'][:] = lutCat[0][
'alpha']
287 lutIndexVals[
'ZENITH'][:] = lutCat[0][
'zenith']
288 lutIndexVals[
'NCCD'] = lutCat[0][
'nCcd']
291 lutStd = np.zeros(1, dtype=[(
'PMBSTD',
'f8'),
297 (
'LAMBDARANGE',
'f8', 2),
298 (
'LAMBDASTEP',
'f8'),
299 (
'LAMBDASTD',
'f8', lutFilterNames.size),
300 (
'LAMBDASTDFILTER',
'f8', lutStdFilterNames.size),
301 (
'I0STD',
'f8', lutFilterNames.size),
302 (
'I1STD',
'f8', lutFilterNames.size),
303 (
'I10STD',
'f8', lutFilterNames.size),
304 (
'I2STD',
'f8', lutFilterNames.size),
305 (
'LAMBDAB',
'f8', lutFilterNames.size),
306 (
'ATMLAMBDA',
'f8', lutCat[0][
'atmLambda'].size),
307 (
'ATMSTDTRANS',
'f8', lutCat[0][
'atmStdTrans'].size)])
308 lutStd[
'PMBSTD'] = lutCat[0][
'pmbStd']
309 lutStd[
'PWVSTD'] = lutCat[0][
'pwvStd']
310 lutStd[
'O3STD'] = lutCat[0][
'o3Std']
311 lutStd[
'TAUSTD'] = lutCat[0][
'tauStd']
312 lutStd[
'ALPHASTD'] = lutCat[0][
'alphaStd']
313 lutStd[
'ZENITHSTD'] = lutCat[0][
'zenithStd']
314 lutStd[
'LAMBDARANGE'][:] = lutCat[0][
'lambdaRange'][:]
315 lutStd[
'LAMBDASTEP'] = lutCat[0][
'lambdaStep']
316 lutStd[
'LAMBDASTD'][:] = lutCat[0][
'lambdaStd']
317 lutStd[
'LAMBDASTDFILTER'][:] = lutCat[0][
'lambdaStdFilter']
318 lutStd[
'I0STD'][:] = lutCat[0][
'i0Std']
319 lutStd[
'I1STD'][:] = lutCat[0][
'i1Std']
320 lutStd[
'I10STD'][:] = lutCat[0][
'i10Std']
321 lutStd[
'I2STD'][:] = lutCat[0][
'i2Std']
322 lutStd[
'LAMBDAB'][:] = lutCat[0][
'lambdaB']
323 lutStd[
'ATMLAMBDA'][:] = lutCat[0][
'atmLambda'][:]
324 lutStd[
'ATMSTDTRANS'][:] = lutCat[0][
'atmStdTrans'][:]
326 lutTypes = [row[
'luttype']
for row
in lutCat]
329 lutFlat = np.zeros(lutCat[0][
'lut'].size, dtype=[(
'I0',
'f4'),
332 lutFlat[
'I0'][:] = lutCat[lutTypes.index(
'I0')][
'lut'][:]
333 lutFlat[
'I1'][:] = lutCat[lutTypes.index(
'I1')][
'lut'][:]
335 lutDerivFlat = np.zeros(lutCat[0][
'lut'].size, dtype=[(
'D_LNPWV',
'f4'),
339 (
'D_SECZENITH',
'f4'),
340 (
'D_LNPWV_I1',
'f4'),
342 (
'D_LNTAU_I1',
'f4'),
343 (
'D_ALPHA_I1',
'f4'),
344 (
'D_SECZENITH_I1',
'f4')])
346 for name
in lutDerivFlat.dtype.names:
347 lutDerivFlat[name][:] = lutCat[lutTypes.index(name)][
'lut'][:]
354 fgcmLut = fgcm.FgcmLUT(lutIndexVals, lutFlat, lutDerivFlat, lutStd,
355 filterToBand=physicalFilterMap)
357 return fgcmLut, lutIndexVals, lutStd
362 Translate the FGCM visit catalog to an fgcm-compatible object
366 visitCat: `lsst.afw.table.BaseCatalog`
371 fgcmExpInfo: `numpy.ndarray`
372 Numpy array
for visit information
for FGCM
376 After running this code, it
is wise to `del visitCat` to clear the memory.
379 fgcmExpInfo = np.zeros(len(visitCat), dtype=[('VISIT',
'i8'),
383 (
'DELTA_APER',
'f8'),
384 (
'SKYBACKGROUND',
'f8'),
391 (
'FILTERNAME',
'a50')])
392 fgcmExpInfo[
'VISIT'][:] = visitCat[
'visit']
393 fgcmExpInfo[
'MJD'][:] = visitCat[
'mjd']
394 fgcmExpInfo[
'EXPTIME'][:] = visitCat[
'exptime']
395 fgcmExpInfo[
'DEEPFLAG'][:] = visitCat[
'deepFlag']
396 fgcmExpInfo[
'TELHA'][:] = visitCat[
'telha']
397 fgcmExpInfo[
'TELRA'][:] = visitCat[
'telra']
398 fgcmExpInfo[
'TELDEC'][:] = visitCat[
'teldec']
399 fgcmExpInfo[
'TELROT'][:] = visitCat[
'telrot']
400 fgcmExpInfo[
'PMB'][:] = visitCat[
'pmb']
401 fgcmExpInfo[
'PSFSIGMA'][:] = visitCat[
'psfSigma']
402 fgcmExpInfo[
'DELTA_APER'][:] = visitCat[
'deltaAper']
403 fgcmExpInfo[
'SKYBACKGROUND'][:] = visitCat[
'skyBackground']
406 fgcmExpInfo[
'FILTERNAME'][:] = visitCat.asAstropy()[
'physicalFilter']
411@deprecated(reason=
"This method is no longer used in fgcmcal. It will be removed after v23.",
412 version=
"v23.0", category=FutureWarning)
415 Compute the CCD offsets in ra/dec
and x/y space
419 camera: `lsst.afw.cameraGeom.Camera`
420 defaultOrientation: `float`
421 Default camera orientation (degrees)
425 ccdOffsets: `numpy.ndarray`
426 Numpy array
with ccd offset information
for input to FGCM.
427 Angular units are degrees,
and x/y units are pixels.
432 ccdOffsets = np.zeros(len(camera), dtype=[(
'CCDNUM',
'i4'),
442 boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees)
447 if camera.getName() ==
'HSC' and np.isnan(defaultOrientation):
448 orientation = 270*geom.degrees
450 orientation = defaultOrientation*geom.degrees
454 visitInfo = afwImage.VisitInfo(boresightRaDec=boresight,
455 boresightRotAngle=orientation,
456 rotType=afwImage.RotType.SKY)
458 for i, detector
in enumerate(camera):
459 ccdOffsets[
'CCDNUM'][i] = detector.getId()
461 wcs = createInitialSkyWcs(visitInfo, detector, flipX)
463 detCenter = wcs.pixelToSky(detector.getCenter(afwCameraGeom.PIXELS))
464 ccdOffsets[
'DELTA_RA'][i] = (detCenter.getRa() - boresight.getRa()).asDegrees()
465 ccdOffsets[
'DELTA_DEC'][i] = (detCenter.getDec() - boresight.getDec()).asDegrees()
467 bbox = detector.getBBox()
469 detCorner1 = wcs.pixelToSky(geom.Point2D(bbox.getMin()))
470 detCorner2 = wcs.pixelToSky(geom.Point2D(bbox.getMax()))
472 ccdOffsets[
'RA_SIZE'][i] = np.abs((detCorner2.getRa() - detCorner1.getRa()).asDegrees())
473 ccdOffsets[
'DEC_SIZE'][i] = np.abs((detCorner2.getDec() - detCorner1.getDec()).asDegrees())
475 ccdOffsets[
'X_SIZE'][i] = bbox.getMaxX()
476 ccdOffsets[
'Y_SIZE'][i] = bbox.getMaxY()
483 Compute the median pixel scale in the camera
488 Average pixel scale (arcsecond) over the camera
491 boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees)
492 orientation = 0.0*geom.degrees
496 visitInfo = afwImage.VisitInfo(boresightRaDec=boresight,
497 boresightRotAngle=orientation,
498 rotType=afwImage.RotType.SKY)
500 pixelScales = np.zeros(len(camera))
501 for i, detector
in enumerate(camera):
502 wcs = createInitialSkyWcs(visitInfo, detector, flipX)
503 pixelScales[i] = wcs.getPixelScale().asArcseconds()
505 ok, = np.where(pixelScales > 0.0)
506 return np.median(pixelScales[ok])
511 Compute the approximate pixel area bounded fields from the camera
516 camera: `lsst.afw.cameraGeom.Camera`
520 approxPixelAreaFields: `dict`
521 Dictionary of approximate area fields, keyed
with detector ID
528 boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees)
533 visitInfo = afwImage.VisitInfo(boresightRaDec=boresight,
534 boresightRotAngle=0.0*geom.degrees,
535 rotType=afwImage.RotType.SKY)
537 approxPixelAreaFields = {}
539 for i, detector
in enumerate(camera):
540 key = detector.getId()
542 wcs = createInitialSkyWcs(visitInfo, detector, flipX)
543 bbox = detector.getBBox()
545 areaField = afwMath.PixelAreaBoundedField(bbox, wcs,
546 unit=geom.arcseconds, scaling=areaScaling)
547 approxAreaField = afwMath.ChebyshevBoundedField.approximate(areaField)
549 approxPixelAreaFields[key] = approxAreaField
551 return approxPixelAreaFields
556 Make the zeropoint schema
560 superStarChebyshevSize: `int`
561 Length of the superstar chebyshev array
562 zptChebyshevSize: `int`
563 Length of the zeropoint chebyshev array
567 zptSchema: `lsst.afw.table.schema`
570 zptSchema = afwTable.Schema()
572 zptSchema.addField('visit', type=np.int32, doc=
'Visit number')
573 zptSchema.addField(
'detector', type=np.int32, doc=
'Detector ID number')
574 zptSchema.addField(
'fgcmFlag', type=np.int32, doc=(
'FGCM flag value: '
575 '1: Photometric, used in fit; '
576 '2: Photometric, not used in fit; '
577 '4: Non-photometric, on partly photometric night; '
578 '8: Non-photometric, on non-photometric night; '
579 '16: No zeropoint could be determined; '
580 '32: Too few stars for reliable gray computation'))
581 zptSchema.addField(
'fgcmZpt', type=np.float64, doc=
'FGCM zeropoint (center of CCD)')
582 zptSchema.addField(
'fgcmZptErr', type=np.float64,
583 doc=
'Error on zeropoint, estimated from repeatability + number of obs')
584 zptSchema.addField(
'fgcmfZptChebXyMax', type=
'ArrayD', size=2,
585 doc=
'maximum x/maximum y to scale to apply chebyshev parameters')
586 zptSchema.addField(
'fgcmfZptCheb', type=
'ArrayD',
587 size=zptChebyshevSize,
588 doc=
'Chebyshev parameters (flattened) for zeropoint')
589 zptSchema.addField(
'fgcmfZptSstarCheb', type=
'ArrayD',
590 size=superStarChebyshevSize,
591 doc=
'Chebyshev parameters (flattened) for superStarFlat')
592 zptSchema.addField(
'fgcmI0', type=np.float64, doc=
'Integral of the passband')
593 zptSchema.addField(
'fgcmI10', type=np.float64, doc=
'Normalized chromatic integral')
594 zptSchema.addField(
'fgcmR0', type=np.float64,
595 doc=
'Retrieved i0 integral, estimated from stars (only for flag 1)')
596 zptSchema.addField(
'fgcmR10', type=np.float64,
597 doc=
'Retrieved i10 integral, estimated from stars (only for flag 1)')
598 zptSchema.addField(
'fgcmGry', type=np.float64,
599 doc=
'Estimated gray extinction relative to atmospheric solution; '
600 'only for fgcmFlag <= 4 (see fgcmFlag) ')
601 zptSchema.addField(
'fgcmDeltaChrom', type=np.float64,
602 doc=
'Mean chromatic correction for stars in this ccd; '
603 'only for fgcmFlag <= 4 (see fgcmFlag)')
604 zptSchema.addField(
'fgcmZptVar', type=np.float64, doc=
'Variance of zeropoint over ccd')
605 zptSchema.addField(
'fgcmTilings', type=np.float64,
606 doc=
'Number of photometric tilings used for solution for ccd')
607 zptSchema.addField(
'fgcmFpGry', type=np.float64,
608 doc=
'Average gray extinction over the full focal plane '
609 '(same for all ccds in a visit)')
610 zptSchema.addField(
'fgcmFpGryBlue', type=np.float64,
611 doc=
'Average gray extinction over the full focal plane '
612 'for 25% bluest stars')
613 zptSchema.addField(
'fgcmFpGryBlueErr', type=np.float64,
614 doc=
'Error on Average gray extinction over the full focal plane '
615 'for 25% bluest stars')
616 zptSchema.addField(
'fgcmFpGryRed', type=np.float64,
617 doc=
'Average gray extinction over the full focal plane '
618 'for 25% reddest stars')
619 zptSchema.addField(
'fgcmFpGryRedErr', type=np.float64,
620 doc=
'Error on Average gray extinction over the full focal plane '
621 'for 25% reddest stars')
622 zptSchema.addField(
'fgcmFpVar', type=np.float64,
623 doc=
'Variance of gray extinction over the full focal plane '
624 '(same for all ccds in a visit)')
625 zptSchema.addField(
'fgcmDust', type=np.float64,
626 doc=
'Gray dust extinction from the primary/corrector'
627 'at the time of the exposure')
628 zptSchema.addField(
'fgcmFlat', type=np.float64, doc=
'Superstarflat illumination correction')
629 zptSchema.addField(
'fgcmAperCorr', type=np.float64, doc=
'Aperture correction estimated by fgcm')
630 zptSchema.addField(
'fgcmDeltaMagBkg', type=np.float64,
631 doc=(
'Local background correction from brightest percentile '
632 '(value set by deltaMagBkgOffsetPercentile) calibration '
634 zptSchema.addField(
'exptime', type=np.float32, doc=
'Exposure time')
635 zptSchema.addField(
'filtername', type=str, size=10, doc=
'Filter name')
642 Make the zeropoint catalog for persistence
646 zptSchema: `lsst.afw.table.Schema`
647 Zeropoint catalog schema
648 zpStruct: `numpy.ndarray`
649 Zeropoint structure
from fgcm
653 zptCat: `afwTable.BaseCatalog`
654 Zeropoint catalog
for persistence
657 zptCat = afwTable.BaseCatalog(zptSchema)
658 zptCat.reserve(zpStruct.size)
660 for filterName
in zpStruct[
'FILTERNAME']:
661 rec = zptCat.addNew()
662 rec[
'filtername'] = filterName.decode(
'utf-8')
664 zptCat[
'visit'][:] = zpStruct[FGCM_EXP_FIELD]
665 zptCat[
'detector'][:] = zpStruct[FGCM_CCD_FIELD]
666 zptCat[
'fgcmFlag'][:] = zpStruct[
'FGCM_FLAG']
667 zptCat[
'fgcmZpt'][:] = zpStruct[
'FGCM_ZPT']
668 zptCat[
'fgcmZptErr'][:] = zpStruct[
'FGCM_ZPTERR']
669 zptCat[
'fgcmfZptChebXyMax'][:, :] = zpStruct[
'FGCM_FZPT_XYMAX']
670 zptCat[
'fgcmfZptCheb'][:, :] = zpStruct[
'FGCM_FZPT_CHEB']
671 zptCat[
'fgcmfZptSstarCheb'][:, :] = zpStruct[
'FGCM_FZPT_SSTAR_CHEB']
672 zptCat[
'fgcmI0'][:] = zpStruct[
'FGCM_I0']
673 zptCat[
'fgcmI10'][:] = zpStruct[
'FGCM_I10']
674 zptCat[
'fgcmR0'][:] = zpStruct[
'FGCM_R0']
675 zptCat[
'fgcmR10'][:] = zpStruct[
'FGCM_R10']
676 zptCat[
'fgcmGry'][:] = zpStruct[
'FGCM_GRY']
677 zptCat[
'fgcmDeltaChrom'][:] = zpStruct[
'FGCM_DELTACHROM']
678 zptCat[
'fgcmZptVar'][:] = zpStruct[
'FGCM_ZPTVAR']
679 zptCat[
'fgcmTilings'][:] = zpStruct[
'FGCM_TILINGS']
680 zptCat[
'fgcmFpGry'][:] = zpStruct[
'FGCM_FPGRY']
681 zptCat[
'fgcmFpGryBlue'][:] = zpStruct[
'FGCM_FPGRY_CSPLIT'][:, 0]
682 zptCat[
'fgcmFpGryBlueErr'][:] = zpStruct[
'FGCM_FPGRY_CSPLITERR'][:, 0]
683 zptCat[
'fgcmFpGryRed'][:] = zpStruct[
'FGCM_FPGRY_CSPLIT'][:, 2]
684 zptCat[
'fgcmFpGryRedErr'][:] = zpStruct[
'FGCM_FPGRY_CSPLITERR'][:, 2]
685 zptCat[
'fgcmFpVar'][:] = zpStruct[
'FGCM_FPVAR']
686 zptCat[
'fgcmDust'][:] = zpStruct[
'FGCM_DUST']
687 zptCat[
'fgcmFlat'][:] = zpStruct[
'FGCM_FLAT']
688 zptCat[
'fgcmAperCorr'][:] = zpStruct[
'FGCM_APERCORR']
689 zptCat[
'fgcmDeltaMagBkg'][:] = zpStruct[
'FGCM_DELTAMAGBKG']
690 zptCat[
'exptime'][:] = zpStruct[
'EXPTIME']
697 Make the atmosphere schema
701 atmSchema: `lsst.afw.table.Schema`
704 atmSchema = afwTable.Schema()
706 atmSchema.addField('visit', type=np.int32, doc=
'Visit number')
707 atmSchema.addField(
'pmb', type=np.float64, doc=
'Barometric pressure (mb)')
708 atmSchema.addField(
'pwv', type=np.float64, doc=
'Water vapor (mm)')
709 atmSchema.addField(
'tau', type=np.float64, doc=
'Aerosol optical depth')
710 atmSchema.addField(
'alpha', type=np.float64, doc=
'Aerosol slope')
711 atmSchema.addField(
'o3', type=np.float64, doc=
'Ozone (dobson)')
712 atmSchema.addField(
'secZenith', type=np.float64, doc=
'Secant(zenith) (~ airmass)')
713 atmSchema.addField(
'cTrans', type=np.float64, doc=
'Transmission correction factor')
714 atmSchema.addField(
'lamStd', type=np.float64, doc=
'Wavelength for transmission correction')
721 Make the atmosphere catalog for persistence
725 atmSchema: `lsst.afw.table.Schema`
726 Atmosphere catalog schema
727 atmStruct: `numpy.ndarray`
728 Atmosphere structure
from fgcm
732 atmCat: `lsst.afw.table.BaseCatalog`
733 Atmosphere catalog
for persistence
736 atmCat = afwTable.BaseCatalog(atmSchema)
737 atmCat.resize(atmStruct.size)
739 atmCat['visit'][:] = atmStruct[
'VISIT']
740 atmCat[
'pmb'][:] = atmStruct[
'PMB']
741 atmCat[
'pwv'][:] = atmStruct[
'PWV']
742 atmCat[
'tau'][:] = atmStruct[
'TAU']
743 atmCat[
'alpha'][:] = atmStruct[
'ALPHA']
744 atmCat[
'o3'][:] = atmStruct[
'O3']
745 atmCat[
'secZenith'][:] = atmStruct[
'SECZENITH']
746 atmCat[
'cTrans'][:] = atmStruct[
'CTRANS']
747 atmCat[
'lamStd'][:] = atmStruct[
'LAMSTD']
754 Make the standard star schema
759 Number of bands in standard star catalog
763 stdSchema: `lsst.afw.table.Schema`
766 stdSchema = afwTable.SimpleTable.makeMinimalSchema()
767 stdSchema.addField('ngood', type=
'ArrayI', doc=
'Number of good observations',
769 stdSchema.addField(
'ntotal', type=
'ArrayI', doc=
'Number of total observations',
771 stdSchema.addField(
'mag_std_noabs', type=
'ArrayF',
772 doc=
'Standard magnitude (no absolute calibration)',
774 stdSchema.addField(
'magErr_std', type=
'ArrayF',
775 doc=
'Standard magnitude error',
777 stdSchema.addField(
'npsfcand', type=
'ArrayI',
778 doc=
'Number of observations flagged as psf candidates',
780 stdSchema.addField(
'delta_aper', type=
'ArrayF',
781 doc=
'Delta mag (small - large aperture)',
789 Make the standard star catalog for persistence
793 stdSchema: `lsst.afw.table.Schema`
794 Standard star catalog schema
795 stdStruct: `numpy.ndarray`
796 Standard star structure
in FGCM format
798 List of good band names used
in stdStruct
802 stdCat: `lsst.afw.table.BaseCatalog`
803 Standard star catalog
for persistence
806 stdCat = afwTable.SimpleCatalog(stdSchema)
807 stdCat.resize(stdStruct.size)
809 stdCat['id'][:] = stdStruct[
'FGCM_ID']
810 stdCat[
'coord_ra'][:] = stdStruct[
'RA'] * geom.degrees
811 stdCat[
'coord_dec'][:] = stdStruct[
'DEC'] * geom.degrees
812 stdCat[
'ngood'][:, :] = stdStruct[
'NGOOD'][:, :]
813 stdCat[
'ntotal'][:, :] = stdStruct[
'NTOTAL'][:, :]
814 stdCat[
'mag_std_noabs'][:, :] = stdStruct[
'MAG_STD'][:, :]
815 stdCat[
'magErr_std'][:, :] = stdStruct[
'MAGERR_STD'][:, :]
816 stdCat[
'npsfcand'][:, :] = stdStruct[
'NPSFCAND'][:, :]
817 stdCat[
'delta_aper'][:, :] = stdStruct[
'DELTA_APER'][:, :]
820 md.set(
"BANDS", list(goodBands))
821 stdCat.setMetadata(md)
828 Compute the radius associated with a CircularApertureFlux field
or
833 dataRef : `lsst.daf.persistence.ButlerDataRef`
or
834 `lsst.daf.butler.DeferredDatasetHandle`
836 CircularApertureFlux
or associated slot.
840 apertureRadius : `float`
841 Radius of the aperture field,
in pixels.
845 RuntimeError: Raised
if flux field
is not a CircularApertureFlux, ApFlux,
846 apFlux,
or associated slot.
849 if isinstance(dataRef, dafPersist.ButlerDataRef):
851 datasetType = dataRef.butlerSubset.datasetType
854 datasetType = dataRef.ref.datasetType.name
856 if datasetType ==
'src':
857 schema = dataRef.get(datasetType=
'src_schema').schema
859 fluxFieldName = schema[fluxField].asField().getName()
861 raise RuntimeError(
"Could not find %s or associated slot in schema." % (fluxField))
868 return apertureRadius
873 Compute the radius associated with a CircularApertureFlux
or ApFlux field.
878 CircularApertureFlux
or ApFlux
882 apertureRadius : `float`
883 Radius of the aperture field,
in pixels.
887 RuntimeError: Raised
if flux field
is not a CircularApertureFlux,
891 m = re.search(
r'(CircularApertureFlux|ApFlux|apFlux)_(\d+)_(\d+)_', fluxField)
894 raise RuntimeError(f
"Flux field {fluxField} does not correspond to a CircularApertureFlux or ApFlux")
896 apertureRadius = float(m.groups()[1]) + float(m.groups()[2])/10.
898 return apertureRadius
903 Extract reference magnitudes from refStars
for given bands
and
904 associated filterMap.
908 refStars : `lsst.afw.table.BaseCatalog`
909 FGCM reference star catalog
911 List of bands
for calibration
913 FGCM mapping of filter to band
917 refMag : `np.ndarray`
918 nstar x nband array of reference magnitudes
919 refMagErr : `np.ndarray`
920 nstar x nband array of reference magnitude errors
926 md = refStars.getMetadata()
927 if 'FILTERNAMES' in md:
928 filternames = md.getArray(
'FILTERNAMES')
932 refMag = np.zeros((len(refStars), len(bands)),
933 dtype=refStars[
'refMag'].dtype) + 99.0
934 refMagErr = np.zeros_like(refMag) + 99.0
935 for i, filtername
in enumerate(filternames):
939 band = filterMap[filtername]
943 ind = bands.index(band)
947 refMag[:, ind] = refStars[
'refMag'][:, i]
948 refMagErr[:, ind] = refStars[
'refMagErr'][:, i]
952 refMag = refStars[
'refMag'][:, :]
953 refMagErr = refStars[
'refMagErr'][:, :]
955 return refMag, refMagErr
959 instrument = Instrument.fromName(quantumDataId[
"instrument"], registry)
960 unboundedCollection = instrument.makeUnboundedCalibrationRunName()
962 return registry.queryDatasets(datasetType,
963 dataId=quantumDataId,
964 collections=[unboundedCollection])
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)