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.
32 from lsst.daf.base
import PropertyList
33 import lsst.afw.cameraGeom
as afwCameraGeom
34 import lsst.afw.table
as afwTable
35 import lsst.afw.image
as afwImage
36 import lsst.afw.math
as afwMath
37 import lsst.geom
as geom
38 from lsst.obs.base
import createInitialSkyWcs
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'),
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 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
615 zptCat = afwTable.BaseCatalog(zptSchema)
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`
660 atmSchema = afwTable.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
692 atmCat = afwTable.BaseCatalog(atmSchema)
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
761 stdCat = afwTable.SimpleCatalog(stdSchema)
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