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 lsst.daf.base
import PropertyList
31 import lsst.afw.cameraGeom
as afwCameraGeom
32 import lsst.afw.table
as afwTable
33 import lsst.afw.image
as afwImage
34 import lsst.afw.math
as afwMath
35 import lsst.geom
as geom
36 from lsst.obs.base
import createInitialSkyWcs
42 resetFitParameters, outputZeropoints, tract=None):
44 Make the FGCM fit cycle configuration dict
48 config: `lsst.fgcmcal.FgcmFitCycleConfig`
52 camera: `lsst.afw.cameraGeom.Camera`
53 Camera from the butler
55 Maximum number of iterations
56 resetFitParameters: `bool`
57 Reset fit parameters before fitting?
58 outputZeropoints: `bool`
59 Compute zeropoints for output?
60 tract: `int`, optional
61 Tract number for extending the output file name for debugging.
67 Configuration dictionary for fgcm
70 notFitBands = [b
for b
in config.bands
if b
not in config.fitBands]
74 for ccut
in config.starColorCuts:
75 parts = ccut.split(
',')
76 starColorCutList.append([parts[0], parts[1], float(parts[2]), float(parts[3])])
81 mirrorArea = np.pi*(camera.telescopeDiameter*100./2.)**2.
84 gains = [amp.getGain()
for detector
in camera
for amp
in detector.getAmplifiers()]
85 cameraGain = float(np.median(gains))
88 outfileBase = config.outfileBase
90 outfileBase =
'%s-%06d' % (config.outfileBase, tract)
93 configDict = {
'outfileBase': outfileBase,
99 'mirrorArea': mirrorArea,
100 'cameraGain': cameraGain,
101 'ccdStartIndex': camera[0].getId(),
104 'seeingField':
'DELTA_APER',
105 'fwhmField':
'PSFSIGMA',
106 'skyBrightnessField':
'SKYBACKGROUND',
107 'deepFlag':
'DEEPFLAG',
108 'bands': list(config.bands),
109 'fitBands': list(config.fitBands),
110 'notFitBands': notFitBands,
111 'requiredBands': list(config.requiredBands),
112 'filterToBand': dict(config.filterMap),
114 'nCore': config.nCore,
115 'nStarPerRun': config.nStarPerRun,
116 'nExpPerRun': config.nExpPerRun,
117 'reserveFraction': config.reserveFraction,
118 'freezeStdAtmosphere': config.freezeStdAtmosphere,
119 'precomputeSuperStarInitialCycle': config.precomputeSuperStarInitialCycle,
120 'superStarSubCCDDict': dict(config.superStarSubCcdDict),
121 'superStarSubCCDChebyshevOrder': config.superStarSubCcdChebyshevOrder,
122 'superStarSubCCDTriangular': config.superStarSubCcdTriangular,
123 'superStarSigmaClip': config.superStarSigmaClip,
124 'ccdGraySubCCDDict': dict(config.ccdGraySubCcdDict),
125 'ccdGraySubCCDChebyshevOrder': config.ccdGraySubCcdChebyshevOrder,
126 'ccdGraySubCCDTriangular': config.ccdGraySubCcdTriangular,
127 'cycleNumber': config.cycleNumber,
129 'deltaMagBkgOffsetPercentile': config.deltaMagBkgOffsetPercentile,
130 'deltaMagBkgPerCcd': config.deltaMagBkgPerCcd,
131 'UTBoundary': config.utBoundary,
132 'washMJDs': config.washMjds,
133 'epochMJDs': config.epochMjds,
134 'coatingMJDs': config.coatingMjds,
135 'minObsPerBand': config.minObsPerBand,
136 'latitude': config.latitude,
137 'brightObsGrayMax': config.brightObsGrayMax,
138 'minStarPerCCD': config.minStarPerCcd,
139 'minCCDPerExp': config.minCcdPerExp,
140 'maxCCDGrayErr': config.maxCcdGrayErr,
141 'minStarPerExp': config.minStarPerExp,
142 'minExpPerNight': config.minExpPerNight,
143 'expGrayInitialCut': config.expGrayInitialCut,
144 'expGrayPhotometricCutDict': dict(config.expGrayPhotometricCutDict),
145 'expGrayHighCutDict': dict(config.expGrayHighCutDict),
146 'expGrayRecoverCut': config.expGrayRecoverCut,
147 'expVarGrayPhotometricCutDict': dict(config.expVarGrayPhotometricCutDict),
148 'expGrayErrRecoverCut': config.expGrayErrRecoverCut,
149 'refStarSnMin': config.refStarSnMin,
150 'refStarOutlierNSig': config.refStarOutlierNSig,
151 'applyRefStarColorCuts': config.applyRefStarColorCuts,
152 'illegalValue': -9999.0,
153 'starColorCuts': starColorCutList,
154 'aperCorrFitNBins': config.aperCorrFitNBins,
155 'aperCorrInputSlopeDict': dict(config.aperCorrInputSlopeDict),
156 'sedBoundaryTermDict': config.sedboundaryterms.toDict()[
'data'],
157 'sedTermDict': config.sedterms.toDict()[
'data'],
158 'colorSplitBands': list(config.colorSplitBands),
159 'sigFgcmMaxErr': config.sigFgcmMaxErr,
160 'sigFgcmMaxEGrayDict': dict(config.sigFgcmMaxEGrayDict),
161 'ccdGrayMaxStarErr': config.ccdGrayMaxStarErr,
162 'approxThroughputDict': dict(config.approxThroughputDict),
163 'sigmaCalRange': list(config.sigmaCalRange),
164 'sigmaCalFitPercentile': list(config.sigmaCalFitPercentile),
165 'sigmaCalPlotPercentile': list(config.sigmaCalPlotPercentile),
166 'sigma0Phot': config.sigma0Phot,
167 'mapLongitudeRef': config.mapLongitudeRef,
168 'mapNSide': config.mapNSide,
171 'useRetrievedPwv':
False,
172 'useNightlyRetrievedPwv':
False,
173 'pwvRetrievalSmoothBlock': 25,
174 'useQuadraticPwv': config.useQuadraticPwv,
175 'useRetrievedTauInit':
False,
176 'tauRetrievalMinCCDPerNight': 500,
177 'modelMagErrors': config.modelMagErrors,
178 'instrumentParsPerBand': config.instrumentParsPerBand,
179 'instrumentSlopeMinDeltaT': config.instrumentSlopeMinDeltaT,
180 'fitMirrorChromaticity': config.fitMirrorChromaticity,
181 'useRepeatabilityForExpGrayCutsDict': dict(config.useRepeatabilityForExpGrayCutsDict),
182 'autoPhotometricCutNSig': config.autoPhotometricCutNSig,
183 'autoHighCutNSig': config.autoHighCutNSig,
185 'quietMode': config.quietMode,
186 'outputStars':
False,
189 'resetParameters': resetFitParameters,
190 'outputFgcmcalZpts':
True,
191 'outputZeropoints': outputZeropoints}
198 Translate the FGCM look-up-table into an fgcm-compatible object
202 lutCat: `lsst.afw.table.BaseCatalog`
203 Catalog describing the FGCM look-up table
205 Filter to band mapping
209 fgcmLut: `lsst.fgcm.FgcmLut`
210 Lookup table for FGCM
211 lutIndexVals: `numpy.ndarray`
212 Numpy array with LUT index information for FGCM
213 lutStd: `numpy.ndarray`
214 Numpy array with LUT standard throughput values for FGCM
218 After running this code, it is wise to `del lutCat` to clear the memory.
223 lutFilterNames = np.array(lutCat[0][
'filterNames'].split(
','), dtype=
'a')
224 lutStdFilterNames = np.array(lutCat[0][
'stdFilterNames'].split(
','), dtype=
'a')
229 lutIndexVals = np.zeros(1, dtype=[(
'FILTERNAMES', lutFilterNames.dtype.str,
230 lutFilterNames.size),
231 (
'STDFILTERNAMES', lutStdFilterNames.dtype.str,
232 lutStdFilterNames.size),
233 (
'PMB',
'f8', lutCat[0][
'pmb'].size),
234 (
'PMBFACTOR',
'f8', lutCat[0][
'pmbFactor'].size),
235 (
'PMBELEVATION',
'f8'),
236 (
'LAMBDANORM',
'f8'),
237 (
'PWV',
'f8', lutCat[0][
'pwv'].size),
238 (
'O3',
'f8', lutCat[0][
'o3'].size),
239 (
'TAU',
'f8', lutCat[0][
'tau'].size),
240 (
'ALPHA',
'f8', lutCat[0][
'alpha'].size),
241 (
'ZENITH',
'f8', lutCat[0][
'zenith'].size),
244 lutIndexVals[
'FILTERNAMES'][:] = lutFilterNames
245 lutIndexVals[
'STDFILTERNAMES'][:] = lutStdFilterNames
246 lutIndexVals[
'PMB'][:] = lutCat[0][
'pmb']
247 lutIndexVals[
'PMBFACTOR'][:] = lutCat[0][
'pmbFactor']
248 lutIndexVals[
'PMBELEVATION'] = lutCat[0][
'pmbElevation']
249 lutIndexVals[
'LAMBDANORM'] = lutCat[0][
'lambdaNorm']
250 lutIndexVals[
'PWV'][:] = lutCat[0][
'pwv']
251 lutIndexVals[
'O3'][:] = lutCat[0][
'o3']
252 lutIndexVals[
'TAU'][:] = lutCat[0][
'tau']
253 lutIndexVals[
'ALPHA'][:] = lutCat[0][
'alpha']
254 lutIndexVals[
'ZENITH'][:] = lutCat[0][
'zenith']
255 lutIndexVals[
'NCCD'] = lutCat[0][
'nCcd']
258 lutStd = np.zeros(1, dtype=[(
'PMBSTD',
'f8'),
264 (
'LAMBDARANGE',
'f8', 2),
265 (
'LAMBDASTEP',
'f8'),
266 (
'LAMBDASTD',
'f8', lutFilterNames.size),
267 (
'LAMBDASTDFILTER',
'f8', lutStdFilterNames.size),
268 (
'I0STD',
'f8', lutFilterNames.size),
269 (
'I1STD',
'f8', lutFilterNames.size),
270 (
'I10STD',
'f8', lutFilterNames.size),
271 (
'I2STD',
'f8', lutFilterNames.size),
272 (
'LAMBDAB',
'f8', lutFilterNames.size),
273 (
'ATMLAMBDA',
'f8', lutCat[0][
'atmLambda'].size),
274 (
'ATMSTDTRANS',
'f8', lutCat[0][
'atmStdTrans'].size)])
275 lutStd[
'PMBSTD'] = lutCat[0][
'pmbStd']
276 lutStd[
'PWVSTD'] = lutCat[0][
'pwvStd']
277 lutStd[
'O3STD'] = lutCat[0][
'o3Std']
278 lutStd[
'TAUSTD'] = lutCat[0][
'tauStd']
279 lutStd[
'ALPHASTD'] = lutCat[0][
'alphaStd']
280 lutStd[
'ZENITHSTD'] = lutCat[0][
'zenithStd']
281 lutStd[
'LAMBDARANGE'][:] = lutCat[0][
'lambdaRange'][:]
282 lutStd[
'LAMBDASTEP'] = lutCat[0][
'lambdaStep']
283 lutStd[
'LAMBDASTD'][:] = lutCat[0][
'lambdaStd']
284 lutStd[
'LAMBDASTDFILTER'][:] = lutCat[0][
'lambdaStdFilter']
285 lutStd[
'I0STD'][:] = lutCat[0][
'i0Std']
286 lutStd[
'I1STD'][:] = lutCat[0][
'i1Std']
287 lutStd[
'I10STD'][:] = lutCat[0][
'i10Std']
288 lutStd[
'I2STD'][:] = lutCat[0][
'i2Std']
289 lutStd[
'LAMBDAB'][:] = lutCat[0][
'lambdaB']
290 lutStd[
'ATMLAMBDA'][:] = lutCat[0][
'atmLambda'][:]
291 lutStd[
'ATMSTDTRANS'][:] = lutCat[0][
'atmStdTrans'][:]
293 lutTypes = [row[
'luttype']
for row
in lutCat]
296 lutFlat = np.zeros(lutCat[0][
'lut'].size, dtype=[(
'I0',
'f4'),
299 lutFlat[
'I0'][:] = lutCat[lutTypes.index(
'I0')][
'lut'][:]
300 lutFlat[
'I1'][:] = lutCat[lutTypes.index(
'I1')][
'lut'][:]
302 lutDerivFlat = np.zeros(lutCat[0][
'lut'].size, dtype=[(
'D_LNPWV',
'f4'),
306 (
'D_SECZENITH',
'f4'),
307 (
'D_LNPWV_I1',
'f4'),
309 (
'D_LNTAU_I1',
'f4'),
310 (
'D_ALPHA_I1',
'f4'),
311 (
'D_SECZENITH_I1',
'f4')])
313 for name
in lutDerivFlat.dtype.names:
314 lutDerivFlat[name][:] = lutCat[lutTypes.index(name)][
'lut'][:]
321 fgcmLut = fgcm.FgcmLUT(lutIndexVals, lutFlat, lutDerivFlat, lutStd,
322 filterToBand=filterMap)
324 return fgcmLut, lutIndexVals, lutStd
329 Translate the FGCM visit catalog to an fgcm-compatible object
333 visitCat: `lsst.afw.table.BaseCatalog`
334 FGCM visitCat from `lsst.fgcmcal.FgcmBuildStarsTask`
338 fgcmExpInfo: `numpy.ndarray`
339 Numpy array for visit information for FGCM
343 After running this code, it is wise to `del visitCat` to clear the memory.
346 fgcmExpInfo = np.zeros(len(visitCat), dtype=[(
'VISIT',
'i8'),
350 (
'DELTA_APER',
'f8'),
351 (
'SKYBACKGROUND',
'f8'),
358 (
'FILTERNAME',
'a10')])
359 fgcmExpInfo[
'VISIT'][:] = visitCat[
'visit']
360 fgcmExpInfo[
'MJD'][:] = visitCat[
'mjd']
361 fgcmExpInfo[
'EXPTIME'][:] = visitCat[
'exptime']
362 fgcmExpInfo[
'DEEPFLAG'][:] = visitCat[
'deepFlag']
363 fgcmExpInfo[
'TELHA'][:] = visitCat[
'telha']
364 fgcmExpInfo[
'TELRA'][:] = visitCat[
'telra']
365 fgcmExpInfo[
'TELDEC'][:] = visitCat[
'teldec']
366 fgcmExpInfo[
'TELROT'][:] = visitCat[
'telrot']
367 fgcmExpInfo[
'PMB'][:] = visitCat[
'pmb']
368 fgcmExpInfo[
'PSFSIGMA'][:] = visitCat[
'psfSigma']
369 fgcmExpInfo[
'DELTA_APER'][:] = visitCat[
'deltaAper']
370 fgcmExpInfo[
'SKYBACKGROUND'][:] = visitCat[
'skyBackground']
373 fgcmExpInfo[
'FILTERNAME'][:] = visitCat.asAstropy()[
'filtername']
380 Compute the CCD offsets in ra/dec and x/y space
384 camera: `lsst.afw.cameraGeom.Camera`
385 defaultOrientation: `float`
386 Default camera orientation (degrees)
390 ccdOffsets: `numpy.ndarray`
391 Numpy array with ccd offset information for input to FGCM.
392 Angular units are degrees, and x/y units are pixels.
397 ccdOffsets = np.zeros(len(camera), dtype=[(
'CCDNUM',
'i4'),
407 boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees)
412 if camera.getName() ==
'HSC' and np.isnan(defaultOrientation):
413 orientation = 270*geom.degrees
415 orientation = defaultOrientation*geom.degrees
419 visitInfo = afwImage.VisitInfo(boresightRaDec=boresight,
420 boresightRotAngle=orientation,
421 rotType=afwImage.visitInfo.RotType.SKY)
423 for i, detector
in enumerate(camera):
424 ccdOffsets[
'CCDNUM'][i] = detector.getId()
426 wcs = createInitialSkyWcs(visitInfo, detector, flipX)
428 detCenter = wcs.pixelToSky(detector.getCenter(afwCameraGeom.PIXELS))
429 ccdOffsets[
'DELTA_RA'][i] = (detCenter.getRa() - boresight.getRa()).asDegrees()
430 ccdOffsets[
'DELTA_DEC'][i] = (detCenter.getDec() - boresight.getDec()).asDegrees()
432 bbox = detector.getBBox()
434 detCorner1 = wcs.pixelToSky(geom.Point2D(bbox.getMin()))
435 detCorner2 = wcs.pixelToSky(geom.Point2D(bbox.getMax()))
437 ccdOffsets[
'RA_SIZE'][i] = np.abs((detCorner2.getRa() - detCorner1.getRa()).asDegrees())
438 ccdOffsets[
'DEC_SIZE'][i] = np.abs((detCorner2.getDec() - detCorner1.getDec()).asDegrees())
440 ccdOffsets[
'X_SIZE'][i] = bbox.getMaxX()
441 ccdOffsets[
'Y_SIZE'][i] = bbox.getMaxY()
448 Compute the median pixel scale in the camera
453 Average pixel scale (arcsecond) over the camera
456 boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees)
457 orientation = 0.0*geom.degrees
461 visitInfo = afwImage.VisitInfo(boresightRaDec=boresight,
462 boresightRotAngle=orientation,
463 rotType=afwImage.visitInfo.RotType.SKY)
465 pixelScales = np.zeros(len(camera))
466 for i, detector
in enumerate(camera):
467 wcs = createInitialSkyWcs(visitInfo, detector, flipX)
468 pixelScales[i] = wcs.getPixelScale().asArcseconds()
470 ok, = np.where(pixelScales > 0.0)
471 return np.median(pixelScales[ok])
476 Compute the approximate pixel area bounded fields from the camera
481 camera: `lsst.afw.cameraGeom.Camera`
485 approxPixelAreaFields: `dict`
486 Dictionary of approximate area fields, keyed with detector ID
493 boresight = geom.SpherePoint(180.0*geom.degrees, 0.0*geom.degrees)
498 visitInfo = afwImage.VisitInfo(boresightRaDec=boresight,
499 boresightRotAngle=0.0*geom.degrees,
500 rotType=afwImage.visitInfo.RotType.SKY)
502 approxPixelAreaFields = {}
504 for i, detector
in enumerate(camera):
505 key = detector.getId()
507 wcs = createInitialSkyWcs(visitInfo, detector, flipX)
508 bbox = detector.getBBox()
510 areaField = afwMath.PixelAreaBoundedField(bbox, wcs,
511 unit=geom.arcseconds, scaling=areaScaling)
512 approxAreaField = afwMath.ChebyshevBoundedField.approximate(areaField)
514 approxPixelAreaFields[key] = approxAreaField
516 return approxPixelAreaFields
521 Make the zeropoint schema
525 superStarChebyshevSize: `int`
526 Length of the superstar chebyshev array
527 zptChebyshevSize: `int`
528 Length of the zeropoint chebyshev array
532 zptSchema: `lsst.afw.table.schema`
535 zptSchema = afwTable.Schema()
537 zptSchema.addField(
'visit', type=np.int32, doc=
'Visit number')
538 zptSchema.addField(
'ccd', type=np.int32, doc=
'CCD number')
539 zptSchema.addField(
'fgcmFlag', type=np.int32, doc=(
'FGCM flag value: '
540 '1: Photometric, used in fit; '
541 '2: Photometric, not used in fit; '
542 '4: Non-photometric, on partly photometric night; '
543 '8: Non-photometric, on non-photometric night; '
544 '16: No zeropoint could be determined; '
545 '32: Too few stars for reliable gray computation'))
546 zptSchema.addField(
'fgcmZpt', type=np.float64, doc=
'FGCM zeropoint (center of CCD)')
547 zptSchema.addField(
'fgcmZptErr', type=np.float64,
548 doc=
'Error on zeropoint, estimated from repeatability + number of obs')
549 zptSchema.addField(
'fgcmfZptChebXyMax', type=
'ArrayD', size=2,
550 doc=
'maximum x/maximum y to scale to apply chebyshev parameters')
551 zptSchema.addField(
'fgcmfZptCheb', type=
'ArrayD',
552 size=zptChebyshevSize,
553 doc=
'Chebyshev parameters (flattened) for zeropoint')
554 zptSchema.addField(
'fgcmfZptSstarCheb', type=
'ArrayD',
555 size=superStarChebyshevSize,
556 doc=
'Chebyshev parameters (flattened) for superStarFlat')
557 zptSchema.addField(
'fgcmI0', type=np.float64, doc=
'Integral of the passband')
558 zptSchema.addField(
'fgcmI10', type=np.float64, doc=
'Normalized chromatic integral')
559 zptSchema.addField(
'fgcmR0', type=np.float64,
560 doc=
'Retrieved i0 integral, estimated from stars (only for flag 1)')
561 zptSchema.addField(
'fgcmR10', type=np.float64,
562 doc=
'Retrieved i10 integral, estimated from stars (only for flag 1)')
563 zptSchema.addField(
'fgcmGry', type=np.float64,
564 doc=
'Estimated gray extinction relative to atmospheric solution; '
565 'only for fgcmFlag <= 4 (see fgcmFlag) ')
566 zptSchema.addField(
'fgcmDeltaChrom', type=np.float64,
567 doc=
'Mean chromatic correction for stars in this ccd; '
568 'only for fgcmFlag <= 4 (see fgcmFlag)')
569 zptSchema.addField(
'fgcmZptVar', type=np.float64, doc=
'Variance of zeropoint over ccd')
570 zptSchema.addField(
'fgcmTilings', type=np.float64,
571 doc=
'Number of photometric tilings used for solution for ccd')
572 zptSchema.addField(
'fgcmFpGry', type=np.float64,
573 doc=
'Average gray extinction over the full focal plane '
574 '(same for all ccds in a visit)')
575 zptSchema.addField(
'fgcmFpGryBlue', type=np.float64,
576 doc=
'Average gray extinction over the full focal plane '
577 'for 25% bluest stars')
578 zptSchema.addField(
'fgcmFpGryBlueErr', type=np.float64,
579 doc=
'Error on Average gray extinction over the full focal plane '
580 'for 25% bluest stars')
581 zptSchema.addField(
'fgcmFpGryRed', type=np.float64,
582 doc=
'Average gray extinction over the full focal plane '
583 'for 25% reddest stars')
584 zptSchema.addField(
'fgcmFpGryRedErr', type=np.float64,
585 doc=
'Error on Average gray extinction over the full focal plane '
586 'for 25% reddest stars')
587 zptSchema.addField(
'fgcmFpVar', type=np.float64,
588 doc=
'Variance of gray extinction over the full focal plane '
589 '(same for all ccds in a visit)')
590 zptSchema.addField(
'fgcmDust', type=np.float64,
591 doc=
'Gray dust extinction from the primary/corrector'
592 'at the time of the exposure')
593 zptSchema.addField(
'fgcmFlat', type=np.float64, doc=
'Superstarflat illumination correction')
594 zptSchema.addField(
'fgcmAperCorr', type=np.float64, doc=
'Aperture correction estimated by fgcm')
595 zptSchema.addField(
'fgcmDeltaMagBkg', type=np.float64,
596 doc=(
'Local background correction from brightest percentile '
597 '(value set by deltaMagBkgOffsetPercentile) calibration '
599 zptSchema.addField(
'exptime', type=np.float32, doc=
'Exposure time')
600 zptSchema.addField(
'filtername', type=str, size=10, doc=
'Filter name')
607 Make the zeropoint catalog for persistence
611 zptSchema: `lsst.afw.table.Schema`
612 Zeropoint catalog schema
613 zpStruct: `numpy.ndarray`
614 Zeropoint structure from fgcm
618 zptCat: `afwTable.BaseCatalog`
619 Zeropoint catalog for persistence
622 zptCat = afwTable.BaseCatalog(zptSchema)
623 zptCat.reserve(zpStruct.size)
625 for filterName
in zpStruct[
'FILTERNAME']:
626 rec = zptCat.addNew()
627 rec[
'filtername'] = filterName.decode(
'utf-8')
629 zptCat[
'visit'][:] = zpStruct[
'VISIT']
630 zptCat[
'ccd'][:] = zpStruct[
'CCD']
631 zptCat[
'fgcmFlag'][:] = zpStruct[
'FGCM_FLAG']
632 zptCat[
'fgcmZpt'][:] = zpStruct[
'FGCM_ZPT']
633 zptCat[
'fgcmZptErr'][:] = zpStruct[
'FGCM_ZPTERR']
634 zptCat[
'fgcmfZptChebXyMax'][:, :] = zpStruct[
'FGCM_FZPT_XYMAX']
635 zptCat[
'fgcmfZptCheb'][:, :] = zpStruct[
'FGCM_FZPT_CHEB']
636 zptCat[
'fgcmfZptSstarCheb'][:, :] = zpStruct[
'FGCM_FZPT_SSTAR_CHEB']
637 zptCat[
'fgcmI0'][:] = zpStruct[
'FGCM_I0']
638 zptCat[
'fgcmI10'][:] = zpStruct[
'FGCM_I10']
639 zptCat[
'fgcmR0'][:] = zpStruct[
'FGCM_R0']
640 zptCat[
'fgcmR10'][:] = zpStruct[
'FGCM_R10']
641 zptCat[
'fgcmGry'][:] = zpStruct[
'FGCM_GRY']
642 zptCat[
'fgcmDeltaChrom'][:] = zpStruct[
'FGCM_DELTACHROM']
643 zptCat[
'fgcmZptVar'][:] = zpStruct[
'FGCM_ZPTVAR']
644 zptCat[
'fgcmTilings'][:] = zpStruct[
'FGCM_TILINGS']
645 zptCat[
'fgcmFpGry'][:] = zpStruct[
'FGCM_FPGRY']
646 zptCat[
'fgcmFpGryBlue'][:] = zpStruct[
'FGCM_FPGRY_CSPLIT'][:, 0]
647 zptCat[
'fgcmFpGryBlueErr'][:] = zpStruct[
'FGCM_FPGRY_CSPLITERR'][:, 0]
648 zptCat[
'fgcmFpGryRed'][:] = zpStruct[
'FGCM_FPGRY_CSPLIT'][:, 2]
649 zptCat[
'fgcmFpGryRedErr'][:] = zpStruct[
'FGCM_FPGRY_CSPLITERR'][:, 2]
650 zptCat[
'fgcmFpVar'][:] = zpStruct[
'FGCM_FPVAR']
651 zptCat[
'fgcmDust'][:] = zpStruct[
'FGCM_DUST']
652 zptCat[
'fgcmFlat'][:] = zpStruct[
'FGCM_FLAT']
653 zptCat[
'fgcmAperCorr'][:] = zpStruct[
'FGCM_APERCORR']
654 zptCat[
'fgcmDeltaMagBkg'][:] = zpStruct[
'FGCM_DELTAMAGBKG']
655 zptCat[
'exptime'][:] = zpStruct[
'EXPTIME']
662 Make the atmosphere schema
666 atmSchema: `lsst.afw.table.Schema`
669 atmSchema = afwTable.Schema()
671 atmSchema.addField(
'visit', type=np.int32, doc=
'Visit number')
672 atmSchema.addField(
'pmb', type=np.float64, doc=
'Barometric pressure (mb)')
673 atmSchema.addField(
'pwv', type=np.float64, doc=
'Water vapor (mm)')
674 atmSchema.addField(
'tau', type=np.float64, doc=
'Aerosol optical depth')
675 atmSchema.addField(
'alpha', type=np.float64, doc=
'Aerosol slope')
676 atmSchema.addField(
'o3', type=np.float64, doc=
'Ozone (dobson)')
677 atmSchema.addField(
'secZenith', type=np.float64, doc=
'Secant(zenith) (~ airmass)')
678 atmSchema.addField(
'cTrans', type=np.float64, doc=
'Transmission correction factor')
679 atmSchema.addField(
'lamStd', type=np.float64, doc=
'Wavelength for transmission correction')
686 Make the atmosphere catalog for persistence
690 atmSchema: `lsst.afw.table.Schema`
691 Atmosphere catalog schema
692 atmStruct: `numpy.ndarray`
693 Atmosphere structure from fgcm
697 atmCat: `lsst.afw.table.BaseCatalog`
698 Atmosphere catalog for persistence
701 atmCat = afwTable.BaseCatalog(atmSchema)
702 atmCat.resize(atmStruct.size)
704 atmCat[
'visit'][:] = atmStruct[
'VISIT']
705 atmCat[
'pmb'][:] = atmStruct[
'PMB']
706 atmCat[
'pwv'][:] = atmStruct[
'PWV']
707 atmCat[
'tau'][:] = atmStruct[
'TAU']
708 atmCat[
'alpha'][:] = atmStruct[
'ALPHA']
709 atmCat[
'o3'][:] = atmStruct[
'O3']
710 atmCat[
'secZenith'][:] = atmStruct[
'SECZENITH']
711 atmCat[
'cTrans'][:] = atmStruct[
'CTRANS']
712 atmCat[
'lamStd'][:] = atmStruct[
'LAMSTD']
719 Make the standard star schema
724 Number of bands in standard star catalog
728 stdSchema: `lsst.afw.table.Schema`
731 stdSchema = afwTable.SimpleTable.makeMinimalSchema()
732 stdSchema.addField(
'ngood', type=
'ArrayI', doc=
'Number of good observations',
734 stdSchema.addField(
'ntotal', type=
'ArrayI', doc=
'Number of total observations',
736 stdSchema.addField(
'mag_std_noabs', type=
'ArrayF',
737 doc=
'Standard magnitude (no absolute calibration)',
739 stdSchema.addField(
'magErr_std', type=
'ArrayF',
740 doc=
'Standard magnitude error',
742 stdSchema.addField(
'npsfcand', type=
'ArrayI',
743 doc=
'Number of observations flagged as psf candidates',
751 Make the standard star catalog for persistence
755 stdSchema: `lsst.afw.table.Schema`
756 Standard star catalog schema
757 stdStruct: `numpy.ndarray`
758 Standard star structure in FGCM format
760 List of good band names used in stdStruct
764 stdCat: `lsst.afw.table.BaseCatalog`
765 Standard star catalog for persistence
768 stdCat = afwTable.SimpleCatalog(stdSchema)
769 stdCat.resize(stdStruct.size)
771 stdCat[
'id'][:] = stdStruct[
'FGCM_ID']
772 stdCat[
'coord_ra'][:] = stdStruct[
'RA'] * geom.degrees
773 stdCat[
'coord_dec'][:] = stdStruct[
'DEC'] * geom.degrees
774 stdCat[
'ngood'][:, :] = stdStruct[
'NGOOD'][:, :]
775 stdCat[
'ntotal'][:, :] = stdStruct[
'NTOTAL'][:, :]
776 stdCat[
'mag_std_noabs'][:, :] = stdStruct[
'MAG_STD'][:, :]
777 stdCat[
'magErr_std'][:, :] = stdStruct[
'MAGERR_STD'][:, :]
778 stdCat[
'npsfcand'][:, :] = stdStruct[
'NPSFCAND'][:, :]
781 md.set(
"BANDS", list(goodBands))
782 stdCat.setMetadata(md)
789 Compute the radius associated with a CircularApertureFlux field or
794 dataRef : `lsst.daf.persistence.ButlerDataRef`
796 CircularApertureFlux or associated slot.
800 apertureRadius : `float`
801 Radius of the aperture field, in pixels.
805 RuntimeError: Raised if flux field is not a CircularApertureFlux, ApFlux,
809 datasetType = dataRef.butlerSubset.datasetType
811 if datasetType ==
'src':
812 schema = dataRef.get(datasetType=
'src_schema').schema
814 fluxFieldName = schema[fluxField].asField().getName()
816 raise RuntimeError(
"Could not find %s or associated slot in schema." % (fluxField))
823 return apertureRadius
828 Compute the radius associated with a CircularApertureFlux or ApFlux field.
833 CircularApertureFlux or ApFlux
837 apertureRadius : `float`
838 Radius of the aperture field, in pixels.
842 RuntimeError: Raised if flux field is not a CircularApertureFlux
846 m = re.search(
r'(CircularApertureFlux|ApFlux)_(\d+)_(\d+)_', fluxField)
849 raise RuntimeError(f
"Flux field {fluxField} does not correspond to a CircularApertureFlux or ApFlux")
851 apertureRadius = float(m.groups()[1]) + float(m.groups()[2])/10.
853 return apertureRadius
858 Extract reference magnitudes from refStars for given bands and
859 associated filterMap.
863 refStars : `lsst.afw.table.BaseCatalog`
864 FGCM reference star catalog
866 List of bands for calibration
868 FGCM mapping of filter to band
872 refMag : `np.ndarray`
873 nstar x nband array of reference magnitudes
874 refMagErr : `np.ndarray`
875 nstar x nband array of reference magnitude errors
881 md = refStars.getMetadata()
882 if 'FILTERNAMES' in md:
883 filternames = md.getArray(
'FILTERNAMES')
887 refMag = np.zeros((len(refStars), len(bands)),
888 dtype=refStars[
'refMag'].dtype) + 99.0
889 refMagErr = np.zeros_like(refMag) + 99.0
890 for i, filtername
in enumerate(filternames):
894 band = filterMap[filtername]
898 ind = bands.index(band)
902 refMag[:, ind] = refStars[
'refMag'][:, i]
903 refMagErr[:, ind] = refStars[
'refMagErr'][:, i]
907 refMag = refStars[
'refMag'][:, :]
908 refMagErr = refStars[
'refMagErr'][:, :]
910 return refMag, refMagErr