23 """Utility functions for fgcmcal.
25 This file contains utility functions that are used by more than one task,
26 and do not need to be part of a task.
44 resetFitParameters, outputZeropoints, tract=None):
46 Make the FGCM fit cycle configuration dict
50 config: `lsst.fgcmcal.FgcmFitCycleConfig`
54 camera: `lsst.afw.cameraGeom.Camera`
55 Camera from the butler
57 Maximum number of iterations
58 resetFitParameters: `bool`
59 Reset fit parameters before fitting?
60 outputZeropoints: `bool`
61 Compute zeropoints for output?
62 tract: `int`, optional
63 Tract number for extending the output file name for debugging.
69 Configuration dictionary for fgcm
72 notFitBands = [b
for b
in config.bands
if b
not in config.fitBands]
76 for ccut
in config.starColorCuts:
77 parts = ccut.split(
',')
78 starColorCutList.append([parts[0], parts[1], float(parts[2]), float(parts[3])])
83 mirrorArea = np.pi*(camera.telescopeDiameter*100./2.)**2.
86 gains = [amp.getGain()
for detector
in camera
for amp
in detector.getAmplifiers()]
87 cameraGain = float(np.median(gains))
90 outfileBase = config.outfileBase
92 outfileBase =
'%s-%06d' % (config.outfileBase, tract)
95 configDict = {
'outfileBase': outfileBase,
101 'mirrorArea': mirrorArea,
102 'cameraGain': cameraGain,
103 'ccdStartIndex': camera[0].getId(),
106 'seeingField':
'DELTA_APER',
107 'fwhmField':
'PSFSIGMA',
108 'skyBrightnessField':
'SKYBACKGROUND',
109 'deepFlag':
'DEEPFLAG',
110 'bands':
list(config.bands),
111 'fitBands':
list(config.fitBands),
112 'notFitBands': notFitBands,
113 'requiredBands':
list(config.requiredBands),
114 'filterToBand': dict(config.filterMap),
116 'nCore': config.nCore,
117 'nStarPerRun': config.nStarPerRun,
118 'nExpPerRun': config.nExpPerRun,
119 'reserveFraction': config.reserveFraction,
120 'freezeStdAtmosphere': config.freezeStdAtmosphere,
121 'precomputeSuperStarInitialCycle': config.precomputeSuperStarInitialCycle,
122 'superStarSubCCDDict': dict(config.superStarSubCcdDict),
123 'superStarSubCCDChebyshevOrder': config.superStarSubCcdChebyshevOrder,
124 'superStarSubCCDTriangular': config.superStarSubCcdTriangular,
125 'superStarSigmaClip': config.superStarSigmaClip,
126 'ccdGraySubCCDDict': dict(config.ccdGraySubCcdDict),
127 'ccdGraySubCCDChebyshevOrder': config.ccdGraySubCcdChebyshevOrder,
128 'ccdGraySubCCDTriangular': config.ccdGraySubCcdTriangular,
129 'cycleNumber': config.cycleNumber,
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'),
412 if camera.getName() ==
'HSC' and np.isnan(defaultOrientation):
413 orientation = 270*geom.degrees
415 orientation = defaultOrientation*geom.degrees
420 boresightRotAngle=orientation,
421 rotType=afwImage.visitInfo.RotType.SKY)
423 for i, detector
in enumerate(camera):
424 ccdOffsets[
'CCDNUM'][i] = detector.getId()
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
457 orientation = 0.0*geom.degrees
462 boresightRotAngle=orientation,
463 rotType=afwImage.visitInfo.RotType.SKY)
465 pixelScales = np.zeros(len(camera))
466 for i, detector
in enumerate(camera):
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
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()
508 bbox = detector.getBBox()
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`
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 flag <= 4')
566 zptSchema.addField(
'fgcmZptVar', type=np.float64, doc=
'Variance of zeropoint over ccd')
567 zptSchema.addField(
'fgcmTilings', type=np.float64,
568 doc=
'Number of photometric tilings used for solution for ccd')
569 zptSchema.addField(
'fgcmFpGry', type=np.float64,
570 doc=
'Average gray extinction over the full focal plane '
571 '(same for all ccds in a visit)')
572 zptSchema.addField(
'fgcmFpGryBlue', type=np.float64,
573 doc=
'Average gray extinction over the full focal plane '
574 'for 25% bluest stars')
575 zptSchema.addField(
'fgcmFpGryBlueErr', type=np.float64,
576 doc=
'Error on Average gray extinction over the full focal plane '
577 'for 25% bluest stars')
578 zptSchema.addField(
'fgcmFpGryRed', type=np.float64,
579 doc=
'Average gray extinction over the full focal plane '
580 'for 25% reddest stars')
581 zptSchema.addField(
'fgcmFpGryRedErr', type=np.float64,
582 doc=
'Error on Average gray extinction over the full focal plane '
583 'for 25% reddest stars')
584 zptSchema.addField(
'fgcmFpVar', type=np.float64,
585 doc=
'Variance of gray extinction over the full focal plane '
586 '(same for all ccds in a visit)')
587 zptSchema.addField(
'fgcmDust', type=np.float64,
588 doc=
'Gray dust extinction from the primary/corrector'
589 'at the time of the exposure')
590 zptSchema.addField(
'fgcmFlat', type=np.float64, doc=
'Superstarflat illumination correction')
591 zptSchema.addField(
'fgcmAperCorr', type=np.float64, doc=
'Aperture correction estimated by fgcm')
592 zptSchema.addField(
'exptime', type=np.float32, doc=
'Exposure time')
593 zptSchema.addField(
'filtername', type=str, size=10, doc=
'Filter name')
600 Make the zeropoint catalog for persistence
604 zptSchema: `lsst.afw.table.Schema`
605 Zeropoint catalog schema
606 zpStruct: `numpy.ndarray`
607 Zeropoint structure from fgcm
611 zptCat: `afwTable.BaseCatalog`
612 Zeropoint catalog for persistence
616 zptCat.reserve(zpStruct.size)
618 for filterName
in zpStruct[
'FILTERNAME']:
619 rec = zptCat.addNew()
620 rec[
'filtername'] = filterName.decode(
'utf-8')
622 zptCat[
'visit'][:] = zpStruct[
'VISIT']
623 zptCat[
'ccd'][:] = zpStruct[
'CCD']
624 zptCat[
'fgcmFlag'][:] = zpStruct[
'FGCM_FLAG']
625 zptCat[
'fgcmZpt'][:] = zpStruct[
'FGCM_ZPT']
626 zptCat[
'fgcmZptErr'][:] = zpStruct[
'FGCM_ZPTERR']
627 zptCat[
'fgcmfZptChebXyMax'][:, :] = zpStruct[
'FGCM_FZPT_XYMAX']
628 zptCat[
'fgcmfZptCheb'][:, :] = zpStruct[
'FGCM_FZPT_CHEB']
629 zptCat[
'fgcmfZptSstarCheb'][:, :] = zpStruct[
'FGCM_FZPT_SSTAR_CHEB']
630 zptCat[
'fgcmI0'][:] = zpStruct[
'FGCM_I0']
631 zptCat[
'fgcmI10'][:] = zpStruct[
'FGCM_I10']
632 zptCat[
'fgcmR0'][:] = zpStruct[
'FGCM_R0']
633 zptCat[
'fgcmR10'][:] = zpStruct[
'FGCM_R10']
634 zptCat[
'fgcmGry'][:] = zpStruct[
'FGCM_GRY']
635 zptCat[
'fgcmZptVar'][:] = zpStruct[
'FGCM_ZPTVAR']
636 zptCat[
'fgcmTilings'][:] = zpStruct[
'FGCM_TILINGS']
637 zptCat[
'fgcmFpGry'][:] = zpStruct[
'FGCM_FPGRY']
638 zptCat[
'fgcmFpGryBlue'][:] = zpStruct[
'FGCM_FPGRY_CSPLIT'][:, 0]
639 zptCat[
'fgcmFpGryBlueErr'][:] = zpStruct[
'FGCM_FPGRY_CSPLITERR'][:, 0]
640 zptCat[
'fgcmFpGryRed'][:] = zpStruct[
'FGCM_FPGRY_CSPLIT'][:, 2]
641 zptCat[
'fgcmFpGryRedErr'][:] = zpStruct[
'FGCM_FPGRY_CSPLITERR'][:, 2]
642 zptCat[
'fgcmFpVar'][:] = zpStruct[
'FGCM_FPVAR']
643 zptCat[
'fgcmDust'][:] = zpStruct[
'FGCM_DUST']
644 zptCat[
'fgcmFlat'][:] = zpStruct[
'FGCM_FLAT']
645 zptCat[
'fgcmAperCorr'][:] = zpStruct[
'FGCM_APERCORR']
646 zptCat[
'exptime'][:] = zpStruct[
'EXPTIME']
653 Make the atmosphere schema
657 atmSchema: `lsst.afw.table.Schema`
662 atmSchema.addField(
'visit', type=np.int32, doc=
'Visit number')
663 atmSchema.addField(
'pmb', type=np.float64, doc=
'Barometric pressure (mb)')
664 atmSchema.addField(
'pwv', type=np.float64, doc=
'Water vapor (mm)')
665 atmSchema.addField(
'tau', type=np.float64, doc=
'Aerosol optical depth')
666 atmSchema.addField(
'alpha', type=np.float64, doc=
'Aerosol slope')
667 atmSchema.addField(
'o3', type=np.float64, doc=
'Ozone (dobson)')
668 atmSchema.addField(
'secZenith', type=np.float64, doc=
'Secant(zenith) (~ airmass)')
669 atmSchema.addField(
'cTrans', type=np.float64, doc=
'Transmission correction factor')
670 atmSchema.addField(
'lamStd', type=np.float64, doc=
'Wavelength for transmission correction')
677 Make the atmosphere catalog for persistence
681 atmSchema: `lsst.afw.table.Schema`
682 Atmosphere catalog schema
683 atmStruct: `numpy.ndarray`
684 Atmosphere structure from fgcm
688 atmCat: `lsst.afw.table.BaseCatalog`
689 Atmosphere catalog for persistence
693 atmCat.reserve(atmStruct.size)
694 for i
in range(atmStruct.size):
697 atmCat[
'visit'][:] = atmStruct[
'VISIT']
698 atmCat[
'pmb'][:] = atmStruct[
'PMB']
699 atmCat[
'pwv'][:] = atmStruct[
'PWV']
700 atmCat[
'tau'][:] = atmStruct[
'TAU']
701 atmCat[
'alpha'][:] = atmStruct[
'ALPHA']
702 atmCat[
'o3'][:] = atmStruct[
'O3']
703 atmCat[
'secZenith'][:] = atmStruct[
'SECZENITH']
704 atmCat[
'cTrans'][:] = atmStruct[
'CTRANS']
705 atmCat[
'lamStd'][:] = atmStruct[
'LAMSTD']
712 Make the standard star schema
717 Number of bands in standard star catalog
721 stdSchema: `lsst.afw.table.Schema`
724 stdSchema = afwTable.SimpleTable.makeMinimalSchema()
725 stdSchema.addField(
'ngood', type=
'ArrayI', doc=
'Number of good observations',
727 stdSchema.addField(
'ntotal', type=
'ArrayI', doc=
'Number of total observations',
729 stdSchema.addField(
'mag_std_noabs', type=
'ArrayF',
730 doc=
'Standard magnitude (no absolute calibration)',
732 stdSchema.addField(
'magErr_std', type=
'ArrayF',
733 doc=
'Standard magnitude error',
735 stdSchema.addField(
'npsfcand', type=
'ArrayI',
736 doc=
'Number of observations flagged as psf candidates',
744 Make the standard star catalog for persistence
748 stdSchema: `lsst.afw.table.Schema`
749 Standard star catalog schema
750 stdStruct: `numpy.ndarray`
751 Standard star structure in FGCM format
753 List of good band names used in stdStruct
757 stdCat: `lsst.afw.table.BaseCatalog`
758 Standard star catalog for persistence
763 stdCat.reserve(stdStruct.size)
764 for i
in range(stdStruct.size):
767 stdCat[
'id'][:] = stdStruct[
'FGCM_ID']
768 stdCat[
'coord_ra'][:] = stdStruct[
'RA'] * geom.degrees
769 stdCat[
'coord_dec'][:] = stdStruct[
'DEC'] * geom.degrees
770 stdCat[
'ngood'][:, :] = stdStruct[
'NGOOD'][:, :]
771 stdCat[
'ntotal'][:, :] = stdStruct[
'NTOTAL'][:, :]
772 stdCat[
'mag_std_noabs'][:, :] = stdStruct[
'MAG_STD'][:, :]
773 stdCat[
'magErr_std'][:, :] = stdStruct[
'MAGERR_STD'][:, :]
774 stdCat[
'npsfcand'][:, :] = stdStruct[
'NPSFCAND'][:, :]
777 md.set(
"BANDS",
list(goodBands))
778 stdCat.setMetadata(md)
785 Compute the radius associated with a CircularApertureFlux field or
790 schema : `lsst.afw.table.schema`
792 CircularApertureFlux field or associated slot.
796 apertureRadius: `float`
797 Radius of the aperture field, in pixels.
801 RuntimeError: Raised if flux field is not a CircularApertureFlux
804 fluxFieldName = schema[fluxField].asField().getName()
806 m = re.search(
r'CircularApertureFlux_(\d+)_(\d+)_', fluxFieldName)
809 raise RuntimeError(
"Flux field %s does not correspond to a circular aperture"
812 apertureRadius = float(m.groups()[0]) + float(m.groups()[1])/10.
814 return apertureRadius
819 Extract reference magnitudes from refStars for given bands and
820 associated filterMap.
824 refStars : `lsst.afw.table.BaseCatalog`
825 FGCM reference star catalog
827 List of bands for calibration
829 FGCM mapping of filter to band
833 refMag : `np.ndarray`
834 nstar x nband array of reference magnitudes
835 refMagErr : `np.ndarray`
836 nstar x nband array of reference magnitude errors
842 md = refStars.getMetadata()
843 if 'FILTERNAMES' in md:
844 filternames = md.getArray(
'FILTERNAMES')
848 refMag = np.zeros((len(refStars), len(bands)),
849 dtype=refStars[
'refMag'].dtype) + 99.0
850 refMagErr = np.zeros_like(refMag) + 99.0
851 for i, filtername
in enumerate(filternames):
855 band = filterMap[filtername]
859 ind = bands.index(band)
863 refMag[:, ind] = refStars[
'refMag'][:, i]
864 refMagErr[:, ind] = refStars[
'refMagErr'][:, i]
868 refMag = refStars[
'refMag'][:, :]
869 refMagErr = refStars[
'refMagErr'][:, :]
871 return refMag, refMagErr