23 """Perform a single fit cycle of FGCM.
25 This task runs a single "fit cycle" of fgcm. Prior to running this task
26 one must run both fgcmMakeLut (to construct the atmosphere and instrumental
27 look-up-table) and fgcmBuildStars (to extract visits and star observations
30 The fgcmFitCycle is meant to be run multiple times, and is tracked by the
31 'cycleNumber'. After each run of the fit cycle, diagnostic plots should
32 be inspected to set parameters for outlier rejection on the following
33 cycle. Please see the fgcmcal Cookbook for details.
42 import lsst.pex.config
as pexConfig
43 import lsst.pipe.base
as pipeBase
44 import lsst.afw.table
as afwTable
46 from .utilities
import makeConfigDict, translateFgcmLut, translateVisitCatalog
47 from .utilities
import extractReferenceMags
48 from .utilities
import computeCcdOffsets, makeZptSchema, makeZptCat
49 from .utilities
import makeAtmSchema, makeAtmCat, makeStdSchema, makeStdCat
50 from .sedterms
import SedboundarytermDict, SedtermDict
54 __all__ = [
'FgcmFitCycleConfig',
'FgcmFitCycleTask',
'FgcmFitCycleRunner']
58 """Config for FgcmFitCycle"""
60 bands = pexConfig.ListField(
61 doc=
"Bands to run calibration",
65 fitFlag = pexConfig.ListField(
66 doc=(
"Flag for which bands are directly constrained in the FGCM fit. "
67 "Bands set to 0 will have the atmosphere constrained from observations "
68 "in other bands on the same night. Must be same length as config.bands, "
69 "and matched band-by-band."),
73 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
74 "It will be removed after v20. Use fitBands instead."),
76 fitBands = pexConfig.ListField(
77 doc=(
"Bands to use in atmospheric fit. The bands not listed here will have "
78 "the atmosphere constrained from the 'fitBands' on the same night. "
79 "Must be a subset of `config.bands`"),
83 requiredFlag = pexConfig.ListField(
84 doc=(
"Flag for which bands are required for a star to be considered a calibration "
85 "star in the FGCM fit. Typically this should be the same as fitFlag. Must "
86 "be same length as config.bands, and matched band-by-band."),
90 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
91 "It will be removed after v20. Use requiredBands instead."),
93 requiredBands = pexConfig.ListField(
94 doc=(
"Bands that are required for a star to be considered a calibration star. "
95 "Must be a subset of `config.bands`"),
99 filterMap = pexConfig.DictField(
100 doc=
"Mapping from 'filterName' to band.",
105 doReferenceCalibration = pexConfig.Field(
106 doc=
"Use reference catalog as additional constraint on calibration",
110 refStarSnMin = pexConfig.Field(
111 doc=
"Reference star signal-to-noise minimum to use in calibration. Set to <=0 for no cut.",
115 refStarOutlierNSig = pexConfig.Field(
116 doc=(
"Number of sigma compared to average mag for reference star to be considered an outlier. "
117 "Computed per-band, and if it is an outlier in any band it is rejected from fits."),
121 applyRefStarColorCuts = pexConfig.Field(
122 doc=
"Apply color cuts to reference stars?",
126 nCore = pexConfig.Field(
127 doc=
"Number of cores to use",
131 nStarPerRun = pexConfig.Field(
132 doc=
"Number of stars to run in each chunk",
136 nExpPerRun = pexConfig.Field(
137 doc=
"Number of exposures to run in each chunk",
141 reserveFraction = pexConfig.Field(
142 doc=
"Fraction of stars to reserve for testing",
146 freezeStdAtmosphere = pexConfig.Field(
147 doc=
"Freeze atmosphere parameters to standard (for testing)",
151 precomputeSuperStarInitialCycle = pexConfig.Field(
152 doc=
"Precompute superstar flat for initial cycle",
156 superStarSubCcd = pexConfig.Field(
157 doc=
"Compute superstar flat on sub-ccd scale",
161 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
162 "It will be removed after v20. Use superStarSubCcdDict instead."),
164 superStarSubCcdDict = pexConfig.DictField(
165 doc=(
"Per-band specification on whether to compute superstar flat on sub-ccd scale. "
166 "Must have one entry per band."),
171 superStarSubCcdChebyshevOrder = pexConfig.Field(
172 doc=(
"Order of the 2D chebyshev polynomials for sub-ccd superstar fit. "
173 "Global default is first-order polynomials, and should be overridden "
174 "on a camera-by-camera basis depending on the ISR."),
178 superStarSubCcdTriangular = pexConfig.Field(
179 doc=(
"Should the sub-ccd superstar chebyshev matrix be triangular to "
180 "suppress high-order cross terms?"),
184 superStarSigmaClip = pexConfig.Field(
185 doc=
"Number of sigma to clip outliers when selecting for superstar flats",
189 ccdGraySubCcd = pexConfig.Field(
190 doc=
"Compute CCD gray terms on sub-ccd scale",
194 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
195 "It will be removed after v20. Use ccdGraySubCcdDict instead."),
197 ccdGraySubCcdDict = pexConfig.DictField(
198 doc=(
"Per-band specification on whether to compute achromatic per-ccd residual "
199 "('ccd gray') on a sub-ccd scale."),
204 ccdGraySubCcdChebyshevOrder = pexConfig.Field(
205 doc=
"Order of the 2D chebyshev polynomials for sub-ccd gray fit.",
209 ccdGraySubCcdTriangular = pexConfig.Field(
210 doc=(
"Should the sub-ccd gray chebyshev matrix be triangular to "
211 "suppress high-order cross terms?"),
215 cycleNumber = pexConfig.Field(
216 doc=(
"FGCM fit cycle number. This is automatically incremented after each run "
217 "and stage of outlier rejection. See cookbook for details."),
221 isFinalCycle = pexConfig.Field(
222 doc=(
"Is this the final cycle of the fitting? Will automatically compute final "
223 "selection of stars and photometric exposures, and will output zeropoints "
224 "and standard stars for use in fgcmOutputProducts"),
228 maxIterBeforeFinalCycle = pexConfig.Field(
229 doc=(
"Maximum fit iterations, prior to final cycle. The number of iterations "
230 "will always be 0 in the final cycle for cleanup and final selection."),
234 utBoundary = pexConfig.Field(
235 doc=
"Boundary (in UTC) from day-to-day",
239 washMjds = pexConfig.ListField(
240 doc=
"Mirror wash MJDs",
244 epochMjds = pexConfig.ListField(
245 doc=
"Epoch boundaries in MJD",
249 minObsPerBand = pexConfig.Field(
250 doc=
"Minimum good observations per band",
256 latitude = pexConfig.Field(
257 doc=
"Observatory latitude",
261 brightObsGrayMax = pexConfig.Field(
262 doc=
"Maximum gray extinction to be considered bright observation",
266 minStarPerCcd = pexConfig.Field(
267 doc=(
"Minimum number of good stars per CCD to be used in calibration fit. "
268 "CCDs with fewer stars will have their calibration estimated from other "
269 "CCDs in the same visit, with zeropoint error increased accordingly."),
273 minCcdPerExp = pexConfig.Field(
274 doc=(
"Minimum number of good CCDs per exposure/visit to be used in calibration fit. "
275 "Visits with fewer good CCDs will have CCD zeropoints estimated where possible."),
279 maxCcdGrayErr = pexConfig.Field(
280 doc=
"Maximum error on CCD gray offset to be considered photometric",
284 minStarPerExp = pexConfig.Field(
285 doc=(
"Minimum number of good stars per exposure/visit to be used in calibration fit. "
286 "Visits with fewer good stars will have CCD zeropoints estimated where possible."),
290 minExpPerNight = pexConfig.Field(
291 doc=
"Minimum number of good exposures/visits to consider a partly photometric night",
295 expGrayInitialCut = pexConfig.Field(
296 doc=(
"Maximum exposure/visit gray value for initial selection of possible photometric "
301 expGrayPhotometricCut = pexConfig.ListField(
302 doc=(
"Maximum (negative) exposure gray for a visit to be considered photometric. "
303 "Must be same length as config.bands, and matched band-by-band."),
307 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
308 "It will be removed after v20. Use expGrayPhotometricCutDict instead."),
310 expGrayPhotometricCutDict = pexConfig.DictField(
311 doc=(
"Per-band specification on maximum (negative) achromatic exposure residual "
312 "('gray term') for a visit to be considered photometric. Must have one "
313 "entry per band. Broad-band filters should be -0.05."),
318 expGrayHighCut = pexConfig.ListField(
319 doc=(
"Maximum (positive) exposure gray for a visit to be considered photometric. "
320 "Must be same length as config.bands, and matched band-by-band."),
324 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
325 "It will be removed after v20. Use expGrayHighCutDict instead."),
327 expGrayHighCutDict = pexConfig.DictField(
328 doc=(
"Per-band specification on maximum (positive) achromatic exposure residual "
329 "('gray term') for a visit to be considered photometric. Must have one "
330 "entry per band. Broad-band filters should be 0.2."),
335 expGrayRecoverCut = pexConfig.Field(
336 doc=(
"Maximum (negative) exposure gray to be able to recover bad ccds via interpolation. "
337 "Visits with more gray extinction will only get CCD zeropoints if there are "
338 "sufficient star observations (minStarPerCcd) on that CCD."),
342 expVarGrayPhotometricCut = pexConfig.Field(
343 doc=
"Maximum exposure variance to be considered possibly photometric",
347 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
348 "It will be removed after v20. Use expVarGrayPhotometricCutDict instead."),
350 expVarGrayPhotometricCutDict = pexConfig.DictField(
351 doc=(
"Per-band specification on maximum exposure variance to be considered possibly "
352 "photometric. Must have one entry per band. Broad-band filters should be "
358 expGrayErrRecoverCut = pexConfig.Field(
359 doc=(
"Maximum exposure gray error to be able to recover bad ccds via interpolation. "
360 "Visits with more gray variance will only get CCD zeropoints if there are "
361 "sufficient star observations (minStarPerCcd) on that CCD."),
365 aperCorrFitNBins = pexConfig.Field(
366 doc=(
"Number of aperture bins used in aperture correction fit. When set to 0"
367 "no fit will be performed, and the config.aperCorrInputSlopes will be "
368 "used if available."),
372 aperCorrInputSlopes = pexConfig.ListField(
373 doc=(
"Aperture correction input slope parameters. These are used on the first "
374 "fit iteration, and aperture correction parameters will be updated from "
375 "the data if config.aperCorrFitNBins > 0. It is recommended to set this"
376 "when there is insufficient data to fit the parameters (e.g. tract mode). "
377 "If set, must be same length as config.bands, and matched band-by-band."),
381 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
382 "It will be removed after v20. Use aperCorrInputSlopeDict instead."),
384 aperCorrInputSlopeDict = pexConfig.DictField(
385 doc=(
"Per-band specification of aperture correction input slope parameters. These "
386 "are used on the first fit iteration, and aperture correction parameters will "
387 "be updated from the data if config.aperCorrFitNBins > 0. It is recommended "
388 "to set this when there is insufficient data to fit the parameters (e.g. "
394 sedFudgeFactors = pexConfig.ListField(
395 doc=(
"Fudge factors for computing linear SED from colors. Must be same length as "
396 "config.bands, and matched band-by-band."),
400 deprecated=(
"This field has been deprecated and will be removed after v20. "
401 "Please use sedSlopeTermMap and sedSlopeMap."),
403 sedboundaryterms = pexConfig.ConfigField(
404 doc=
"Mapping from bands to SED boundary term names used is sedterms.",
405 dtype=SedboundarytermDict,
407 sedterms = pexConfig.ConfigField(
408 doc=
"Mapping from terms to bands for fgcm linear SED approximations.",
411 sigFgcmMaxErr = pexConfig.Field(
412 doc=
"Maximum mag error for fitting sigma_FGCM",
416 sigFgcmMaxEGray = pexConfig.ListField(
417 doc=(
"Maximum (absolute) gray value for observation in sigma_FGCM. "
418 "May be 1 element (same for all bands) or the same length as config.bands."),
422 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
423 "It will be removed after v20. Use sigFgcmMaxEGrayDict instead."),
425 sigFgcmMaxEGrayDict = pexConfig.DictField(
426 doc=(
"Per-band specification for maximum (absolute) achromatic residual (gray value) "
427 "for observations in sigma_fgcm (raw repeatability). Broad-band filters "
433 ccdGrayMaxStarErr = pexConfig.Field(
434 doc=(
"Maximum error on a star observation to use in ccd gray (achromatic residual) "
439 approxThroughput = pexConfig.ListField(
440 doc=(
"Approximate overall throughput at start of calibration observations. "
441 "May be 1 element (same for all bands) or the same length as config.bands."),
445 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
446 "It will be removed after v20. Use approxThroughputDict instead."),
448 approxThroughputDict = pexConfig.DictField(
449 doc=(
"Per-band specification of the approximate overall throughput at the start of "
450 "calibration observations. Must have one entry per band. Typically should "
456 sigmaCalRange = pexConfig.ListField(
457 doc=
"Allowed range for systematic error floor estimation",
459 default=(0.001, 0.003),
461 sigmaCalFitPercentile = pexConfig.ListField(
462 doc=
"Magnitude percentile range to fit systematic error floor",
464 default=(0.05, 0.15),
466 sigmaCalPlotPercentile = pexConfig.ListField(
467 doc=
"Magnitude percentile range to plot systematic error floor",
469 default=(0.05, 0.95),
471 sigma0Phot = pexConfig.Field(
472 doc=
"Systematic error floor for all zeropoints",
476 mapLongitudeRef = pexConfig.Field(
477 doc=
"Reference longitude for plotting maps",
481 mapNSide = pexConfig.Field(
482 doc=
"Healpix nside for plotting maps",
486 outfileBase = pexConfig.Field(
487 doc=
"Filename start for plot output files",
491 starColorCuts = pexConfig.ListField(
492 doc=
"Encoded star-color cuts (to be cleaned up)",
494 default=(
"NO_DATA",),
496 colorSplitIndices = pexConfig.ListField(
497 doc=
"Band indices to use to split stars by color",
501 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
502 "It will be removed after v20. Use colorSplitBands instead."),
504 colorSplitBands = pexConfig.ListField(
505 doc=
"Band names to use to split stars by color. Must have 2 entries.",
510 modelMagErrors = pexConfig.Field(
511 doc=
"Should FGCM model the magnitude errors from sky/fwhm? (False means trust inputs)",
515 useQuadraticPwv = pexConfig.Field(
516 doc=
"Model PWV with a quadratic term for variation through the night?",
520 instrumentParsPerBand = pexConfig.Field(
521 doc=(
"Model instrumental parameters per band? "
522 "Otherwise, instrumental parameters (QE changes with time) are "
523 "shared among all bands."),
527 instrumentSlopeMinDeltaT = pexConfig.Field(
528 doc=(
"Minimum time change (in days) between observations to use in constraining "
529 "instrument slope."),
533 fitMirrorChromaticity = pexConfig.Field(
534 doc=
"Fit (intraband) mirror chromatic term?",
538 coatingMjds = pexConfig.ListField(
539 doc=
"Mirror coating dates in MJD",
543 outputStandardsBeforeFinalCycle = pexConfig.Field(
544 doc=
"Output standard stars prior to final cycle? Used in debugging.",
548 outputZeropointsBeforeFinalCycle = pexConfig.Field(
549 doc=
"Output standard stars prior to final cycle? Used in debugging.",
553 useRepeatabilityForExpGrayCuts = pexConfig.ListField(
554 doc=(
"Use star repeatability (instead of exposures) for computing photometric "
555 "cuts? Recommended for tract mode or bands with few exposures. "
556 "May be 1 element (same for all bands) or the same length as config.bands."),
560 deprecated=(
"This field is no longer used, and has been deprecated by DM-23699. "
561 "It will be removed after v20. Use useRepeatabilityForExpGrayCutsDict instead."),
563 useRepeatabilityForExpGrayCutsDict = pexConfig.DictField(
564 doc=(
"Per-band specification on whether to use star repeatability (instead of exposures) "
565 "for computing photometric cuts. Recommended for tract mode or bands with few visits."),
570 autoPhotometricCutNSig = pexConfig.Field(
571 doc=(
"Number of sigma for automatic computation of (low) photometric cut. "
572 "Cut is based on exposure gray width (per band), unless "
573 "useRepeatabilityForExpGrayCuts is set, in which case the star "
574 "repeatability is used (also per band)."),
578 autoHighCutNSig = pexConfig.Field(
579 doc=(
"Number of sigma for automatic computation of (high) outlier cut. "
580 "Cut is based on exposure gray width (per band), unless "
581 "useRepeatabilityForExpGrayCuts is set, in which case the star "
582 "repeatability is used (also per band)."),
586 quietMode = pexConfig.Field(
587 doc=
"Be less verbose with logging.",
598 for band
in self.fitBands:
599 if band
not in self.bands:
600 msg =
'fitBand %s not in bands' % (band)
601 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.fitBands, self, msg)
602 for band
in self.requiredBands:
603 if band
not in self.bands:
604 msg =
'requiredBand %s not in bands' % (band)
605 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.requiredBands, self, msg)
606 for band
in self.colorSplitBands:
607 if band
not in self.bands:
608 msg =
'colorSplitBand %s not in bands' % (band)
609 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.colorSplitBands, self, msg)
610 for band
in self.bands:
611 if band
not in self.superStarSubCcdDict:
612 msg =
'band %s not in superStarSubCcdDict' % (band)
613 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.superStarSubCcdDict,
615 if band
not in self.ccdGraySubCcdDict:
616 msg =
'band %s not in ccdGraySubCcdDict' % (band)
617 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.ccdGraySubCcdDict,
619 if band
not in self.expGrayPhotometricCutDict:
620 msg =
'band %s not in expGrayPhotometricCutDict' % (band)
621 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.expGrayPhotometricCutDict,
623 if band
not in self.expGrayHighCutDict:
624 msg =
'band %s not in expGrayHighCutDict' % (band)
625 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.expGrayHighCutDict,
627 if band
not in self.expVarGrayPhotometricCutDict:
628 msg =
'band %s not in expVarGrayPhotometricCutDict' % (band)
629 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.expVarGrayPhotometricCutDict,
631 if band
not in self.sigFgcmMaxEGrayDict:
632 msg =
'band %s not in sigFgcmMaxEGrayDict' % (band)
633 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.sigFgcmMaxEGrayDict,
635 if band
not in self.approxThroughputDict:
636 msg =
'band %s not in approxThroughputDict' % (band)
637 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.approxThroughputDict,
639 if band
not in self.useRepeatabilityForExpGrayCutsDict:
640 msg =
'band %s not in useRepeatabilityForExpGrayCutsDict' % (band)
641 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.useRepeatabilityForExpGrayCutsDict,
646 """Subclass of TaskRunner for fgcmFitCycleTask
648 fgcmFitCycleTask.run() takes one argument, the butler, and uses
649 stars and visits previously extracted from dataRefs by
651 This Runner does not perform any dataRef parallelization, but the FGCM
652 code called by the Task uses python multiprocessing (see the "ncores"
659 Return a list with one element, the butler.
661 return [parsedCmd.butler]
667 butler: `lsst.daf.persistence.Butler`
671 exitStatus: `list` with `pipeBase.Struct`
672 exitStatus (0: success; 1: failure)
675 task = self.TaskClass(config=self.config, log=self.log)
679 task.runDataRef(butler)
682 task.runDataRef(butler)
683 except Exception
as e:
685 task.log.fatal(
"Failed: %s" % e)
686 if not isinstance(e, pipeBase.TaskError):
687 traceback.print_exc(file=sys.stderr)
689 task.writeMetadata(butler)
692 return [pipeBase.Struct(exitStatus=exitStatus)]
696 Run the task, with no multiprocessing
700 parsedCmd: ArgumentParser parsed command line
705 if self.precall(parsedCmd):
708 resultList = self(targetList[0])
715 Run Single fit cycle for FGCM global calibration
718 ConfigClass = FgcmFitCycleConfig
719 RunnerClass = FgcmFitCycleRunner
720 _DefaultName =
"fgcmFitCycle"
724 Instantiate an fgcmFitCycle.
728 butler : `lsst.daf.persistence.Butler`
731 pipeBase.CmdLineTask.__init__(self, **kwargs)
734 def _getMetadataName(self):
740 Run a single fit cycle for FGCM
744 butler: `lsst.daf.persistence.Butler`
750 """Write the configuration used for processing the data, or check that an existing
751 one is equal to the new one if present. This is an override of the regular
752 version from pipe_base that knows about fgcmcycle.
756 butler : `lsst.daf.persistence.Butler`
757 Data butler used to write the config. The config is written to dataset type
758 `CmdLineTask._getConfigName`.
759 clobber : `bool`, optional
760 A boolean flag that controls what happens if a config already has been saved:
761 - `True`: overwrite or rename the existing config, depending on ``doBackup``.
762 - `False`: raise `TaskError` if this config does not match the existing config.
763 doBackup : `bool`, optional
764 Set to `True` to backup the config files if clobbering.
766 configName = self._getConfigName()
767 if configName
is None:
770 butler.put(self.config, configName, doBackup=doBackup, fgcmcycle=self.config.cycleNumber)
771 elif butler.datasetExists(configName, write=
True, fgcmcycle=self.config.cycleNumber):
774 oldConfig = butler.get(configName, immediate=
True, fgcmcycle=self.config.cycleNumber)
775 except Exception
as exc:
776 raise type(exc)(
"Unable to read stored config file %s (%s); consider using --clobber-config" %
779 def logConfigMismatch(msg):
780 self.log.fatal(
"Comparing configuration: %s", msg)
782 if not self.config.compare(oldConfig, shortcut=
False, output=logConfigMismatch):
783 raise pipeBase.TaskError(
784 (
"Config does not match existing task config %r on disk; tasks configurations " +
785 "must be consistent within the same output repo (override with --clobber-config)") %
788 butler.put(self.config, configName, fgcmcycle=self.config.cycleNumber)
790 def _fgcmFitCycle(self, butler):
796 butler: `lsst.daf.persistence.Butler`
802 self.
maxIter = self.config.maxIterBeforeFinalCycle
807 if self.config.isFinalCycle:
816 camera = butler.get(
'camera')
821 lutCat = butler.get(
'fgcmLookUpTable')
822 fgcmLut, lutIndexVals, lutStd =
translateFgcmLut(lutCat, dict(self.config.filterMap))
828 visitCat = butler.get(
'fgcmVisitCatalog')
836 noFitsDict = {
'lutIndex': lutIndexVals,
838 'expInfo': fgcmExpInfo,
839 'ccdOffsets': ccdOffsets}
842 fgcmFitCycle = fgcm.FgcmFitCycle(configDict, useFits=
False,
843 noFitsDict=noFitsDict, noOutput=
True)
846 if (fgcmFitCycle.initialCycle):
848 fgcmPars = fgcm.FgcmParameters.newParsWithArrays(fgcmFitCycle.fgcmConfig,
853 fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(fgcmFitCycle.fgcmConfig,
859 lastCycle = configDict[
'cycleNumber'] - 1
862 fgcmStars = fgcm.FgcmStars(fgcmFitCycle.fgcmConfig)
864 starObs = butler.get(
'fgcmStarObservations')
865 starIds = butler.get(
'fgcmStarIds')
866 starIndices = butler.get(
'fgcmStarIndices')
869 if butler.datasetExists(
'fgcmFlaggedStars', fgcmcycle=lastCycle):
870 flaggedStars = butler.get(
'fgcmFlaggedStars', fgcmcycle=lastCycle)
871 flagId = flaggedStars[
'objId'][:]
872 flagFlag = flaggedStars[
'objFlag'][:]
878 if self.config.doReferenceCalibration:
879 refStars = butler.get(
'fgcmReferenceStars')
883 self.config.filterMap)
884 refId = refStars[
'fgcm_id'][:]
894 visitIndex = np.searchsorted(fgcmExpInfo[
'VISIT'], starObs[
'visit'][starIndices[
'obsIndex']])
906 conv = starObs[0][
'ra'].asDegrees() / float(starObs[0][
'ra'])
908 fgcmStars.loadStars(fgcmPars,
909 starObs[
'visit'][starIndices[
'obsIndex']],
910 starObs[
'ccd'][starIndices[
'obsIndex']],
911 starObs[
'ra'][starIndices[
'obsIndex']] * conv,
912 starObs[
'dec'][starIndices[
'obsIndex']] * conv,
913 starObs[
'instMag'][starIndices[
'obsIndex']],
914 starObs[
'instMagErr'][starIndices[
'obsIndex']],
915 fgcmExpInfo[
'FILTERNAME'][visitIndex],
916 starIds[
'fgcm_id'][:],
919 starIds[
'obsArrIndex'][:],
921 obsX=starObs[
'x'][starIndices[
'obsIndex']],
922 obsY=starObs[
'y'][starIndices[
'obsIndex']],
923 psfCandidate=starObs[
'psf_candidate'][starIndices[
'obsIndex']],
944 fgcmFitCycle.setLUT(fgcmLut)
945 fgcmFitCycle.setStars(fgcmStars, fgcmPars)
946 fgcmFitCycle.setPars(fgcmPars)
949 fgcmFitCycle.finishSetup()
963 updatedPhotometricCutDict = {b: float(fgcmFitCycle.updatedPhotometricCut[i])
for
964 i, b
in enumerate(self.config.bands)}
965 updatedHighCutDict = {band: float(fgcmFitCycle.updatedHighCut[i])
for
966 i, band
in enumerate(self.config.bands)}
968 outConfig = copy.copy(self.config)
969 outConfig.update(cycleNumber=(self.config.cycleNumber + 1),
970 precomputeSuperStarInitialCycle=
False,
971 freezeStdAtmosphere=
False,
972 expGrayPhotometricCutDict=updatedPhotometricCutDict,
973 expGrayHighCutDict=updatedHighCutDict)
974 configFileName =
'%s_cycle%02d_config.py' % (outConfig.outfileBase,
975 outConfig.cycleNumber)
976 outConfig.save(configFileName)
978 if self.config.isFinalCycle == 1:
980 self.log.info(
"Everything is in place to run fgcmOutputProducts.py")
982 self.log.info(
"Saved config for next cycle to %s" % (configFileName))
983 self.log.info(
"Be sure to look at:")
984 self.log.info(
" config.expGrayPhotometricCut")
985 self.log.info(
" config.expGrayHighCut")
986 self.log.info(
"If you are satisfied with the fit, please set:")
987 self.log.info(
" config.isFinalCycle = True")
989 def _checkDatasetsExist(self, butler):
991 Check if necessary datasets exist to run fgcmFitCycle
995 butler: `lsst.daf.persistence.Butler`
1000 If any of fgcmVisitCatalog, fgcmStarObservations, fgcmStarIds,
1001 fgcmStarIndices, fgcmLookUpTable datasets do not exist.
1002 If cycleNumber > 0, then also checks for fgcmFitParameters,
1006 if not butler.datasetExists(
'fgcmVisitCatalog'):
1007 raise RuntimeError(
"Could not find fgcmVisitCatalog in repo!")
1008 if not butler.datasetExists(
'fgcmStarObservations'):
1009 raise RuntimeError(
"Could not find fgcmStarObservations in repo!")
1010 if not butler.datasetExists(
'fgcmStarIds'):
1011 raise RuntimeError(
"Could not find fgcmStarIds in repo!")
1012 if not butler.datasetExists(
'fgcmStarIndices'):
1013 raise RuntimeError(
"Could not find fgcmStarIndices in repo!")
1014 if not butler.datasetExists(
'fgcmLookUpTable'):
1015 raise RuntimeError(
"Could not find fgcmLookUpTable in repo!")
1018 if (self.config.cycleNumber > 0):
1019 if not butler.datasetExists(
'fgcmFitParameters',
1020 fgcmcycle=self.config.cycleNumber-1):
1021 raise RuntimeError(
"Could not find fgcmFitParameters for previous cycle (%d) in repo!" %
1022 (self.config.cycleNumber-1))
1023 if not butler.datasetExists(
'fgcmFlaggedStars',
1024 fgcmcycle=self.config.cycleNumber-1):
1025 raise RuntimeError(
"Could not find fgcmFlaggedStars for previous cycle (%d) in repo!" %
1026 (self.config.cycleNumber-1))
1029 if self.config.doReferenceCalibration:
1030 if not butler.datasetExists(
'fgcmReferenceStars'):
1031 raise RuntimeError(
"Could not find fgcmReferenceStars in repo, and "
1032 "doReferenceCalibration is True.")
1034 def _loadParameters(self, butler):
1036 Load FGCM parameters from a previous fit cycle
1040 butler: `lsst.daf.persistence.Butler`
1044 inParInfo: `numpy.ndarray`
1045 Numpy array parameter information formatted for input to fgcm
1046 inParameters: `numpy.ndarray`
1047 Numpy array parameter values formatted for input to fgcm
1048 inSuperStar: `numpy.array`
1049 Superstar flat formatted for input to fgcm
1053 parCat = butler.get(
'fgcmFitParameters', fgcmcycle=self.config.cycleNumber-1)
1055 parLutFilterNames = np.array(parCat[0][
'lutFilterNames'].split(
','))
1056 parFitBands = np.array(parCat[0][
'fitBands'].split(
','))
1058 inParInfo = np.zeros(1, dtype=[(
'NCCD',
'i4'),
1059 (
'LUTFILTERNAMES', parLutFilterNames.dtype.str,
1060 (parLutFilterNames.size, )),
1061 (
'FITBANDS', parFitBands.dtype.str, (parFitBands.size, )),
1062 (
'LNTAUUNIT',
'f8'),
1063 (
'LNTAUSLOPEUNIT',
'f8'),
1064 (
'ALPHAUNIT',
'f8'),
1065 (
'LNPWVUNIT',
'f8'),
1066 (
'LNPWVSLOPEUNIT',
'f8'),
1067 (
'LNPWVQUADRATICUNIT',
'f8'),
1068 (
'LNPWVGLOBALUNIT',
'f8'),
1070 (
'QESYSUNIT',
'f8'),
1071 (
'FILTEROFFSETUNIT',
'f8'),
1072 (
'HASEXTERNALPWV',
'i2'),
1073 (
'HASEXTERNALTAU',
'i2')])
1074 inParInfo[
'NCCD'] = parCat[
'nCcd']
1075 inParInfo[
'LUTFILTERNAMES'][:] = parLutFilterNames
1076 inParInfo[
'FITBANDS'][:] = parFitBands
1077 inParInfo[
'HASEXTERNALPWV'] = parCat[
'hasExternalPwv']
1078 inParInfo[
'HASEXTERNALTAU'] = parCat[
'hasExternalTau']
1080 inParams = np.zeros(1, dtype=[(
'PARALPHA',
'f8', (parCat[
'parAlpha'].size, )),
1081 (
'PARO3',
'f8', (parCat[
'parO3'].size, )),
1082 (
'PARLNTAUINTERCEPT',
'f8',
1083 (parCat[
'parLnTauIntercept'].size, )),
1084 (
'PARLNTAUSLOPE',
'f8',
1085 (parCat[
'parLnTauSlope'].size, )),
1086 (
'PARLNPWVINTERCEPT',
'f8',
1087 (parCat[
'parLnPwvIntercept'].size, )),
1088 (
'PARLNPWVSLOPE',
'f8',
1089 (parCat[
'parLnPwvSlope'].size, )),
1090 (
'PARLNPWVQUADRATIC',
'f8',
1091 (parCat[
'parLnPwvQuadratic'].size, )),
1092 (
'PARQESYSINTERCEPT',
'f8',
1093 (parCat[
'parQeSysIntercept'].size, )),
1094 (
'COMPQESYSSLOPE',
'f8',
1095 (parCat[
'compQeSysSlope'].size, )),
1096 (
'PARFILTEROFFSET',
'f8',
1097 (parCat[
'parFilterOffset'].size, )),
1098 (
'PARFILTEROFFSETFITFLAG',
'i2',
1099 (parCat[
'parFilterOffsetFitFlag'].size, )),
1100 (
'PARRETRIEVEDLNPWVSCALE',
'f8'),
1101 (
'PARRETRIEVEDLNPWVOFFSET',
'f8'),
1102 (
'PARRETRIEVEDLNPWVNIGHTLYOFFSET',
'f8',
1103 (parCat[
'parRetrievedLnPwvNightlyOffset'].size, )),
1104 (
'COMPABSTHROUGHPUT',
'f8',
1105 (parCat[
'compAbsThroughput'].size, )),
1106 (
'COMPREFOFFSET',
'f8',
1107 (parCat[
'compRefOffset'].size, )),
1108 (
'COMPREFSIGMA',
'f8',
1109 (parCat[
'compRefSigma'].size, )),
1110 (
'COMPMIRRORCHROMATICITY',
'f8',
1111 (parCat[
'compMirrorChromaticity'].size, )),
1112 (
'MIRRORCHROMATICITYPIVOT',
'f8',
1113 (parCat[
'mirrorChromaticityPivot'].size, )),
1114 (
'COMPAPERCORRPIVOT',
'f8',
1115 (parCat[
'compAperCorrPivot'].size, )),
1116 (
'COMPAPERCORRSLOPE',
'f8',
1117 (parCat[
'compAperCorrSlope'].size, )),
1118 (
'COMPAPERCORRSLOPEERR',
'f8',
1119 (parCat[
'compAperCorrSlopeErr'].size, )),
1120 (
'COMPAPERCORRRANGE',
'f8',
1121 (parCat[
'compAperCorrRange'].size, )),
1122 (
'COMPMODELERREXPTIMEPIVOT',
'f8',
1123 (parCat[
'compModelErrExptimePivot'].size, )),
1124 (
'COMPMODELERRFWHMPIVOT',
'f8',
1125 (parCat[
'compModelErrFwhmPivot'].size, )),
1126 (
'COMPMODELERRSKYPIVOT',
'f8',
1127 (parCat[
'compModelErrSkyPivot'].size, )),
1128 (
'COMPMODELERRPARS',
'f8',
1129 (parCat[
'compModelErrPars'].size, )),
1130 (
'COMPEXPGRAY',
'f8',
1131 (parCat[
'compExpGray'].size, )),
1132 (
'COMPVARGRAY',
'f8',
1133 (parCat[
'compVarGray'].size, )),
1134 (
'COMPNGOODSTARPEREXP',
'i4',
1135 (parCat[
'compNGoodStarPerExp'].size, )),
1136 (
'COMPSIGFGCM',
'f8',
1137 (parCat[
'compSigFgcm'].size, )),
1138 (
'COMPSIGMACAL',
'f8',
1139 (parCat[
'compSigmaCal'].size, )),
1140 (
'COMPRETRIEVEDLNPWV',
'f8',
1141 (parCat[
'compRetrievedLnPwv'].size, )),
1142 (
'COMPRETRIEVEDLNPWVRAW',
'f8',
1143 (parCat[
'compRetrievedLnPwvRaw'].size, )),
1144 (
'COMPRETRIEVEDLNPWVFLAG',
'i2',
1145 (parCat[
'compRetrievedLnPwvFlag'].size, )),
1146 (
'COMPRETRIEVEDTAUNIGHT',
'f8',
1147 (parCat[
'compRetrievedTauNight'].size, ))])
1149 inParams[
'PARALPHA'][:] = parCat[
'parAlpha'][0, :]
1150 inParams[
'PARO3'][:] = parCat[
'parO3'][0, :]
1151 inParams[
'PARLNTAUINTERCEPT'][:] = parCat[
'parLnTauIntercept'][0, :]
1152 inParams[
'PARLNTAUSLOPE'][:] = parCat[
'parLnTauSlope'][0, :]
1153 inParams[
'PARLNPWVINTERCEPT'][:] = parCat[
'parLnPwvIntercept'][0, :]
1154 inParams[
'PARLNPWVSLOPE'][:] = parCat[
'parLnPwvSlope'][0, :]
1155 inParams[
'PARLNPWVQUADRATIC'][:] = parCat[
'parLnPwvQuadratic'][0, :]
1156 inParams[
'PARQESYSINTERCEPT'][:] = parCat[
'parQeSysIntercept'][0, :]
1157 inParams[
'COMPQESYSSLOPE'][:] = parCat[
'compQeSysSlope'][0, :]
1158 inParams[
'PARFILTEROFFSET'][:] = parCat[
'parFilterOffset'][0, :]
1159 inParams[
'PARFILTEROFFSETFITFLAG'][:] = parCat[
'parFilterOffsetFitFlag'][0, :]
1160 inParams[
'PARRETRIEVEDLNPWVSCALE'] = parCat[
'parRetrievedLnPwvScale']
1161 inParams[
'PARRETRIEVEDLNPWVOFFSET'] = parCat[
'parRetrievedLnPwvOffset']
1162 inParams[
'PARRETRIEVEDLNPWVNIGHTLYOFFSET'][:] = parCat[
'parRetrievedLnPwvNightlyOffset'][0, :]
1163 inParams[
'COMPABSTHROUGHPUT'][:] = parCat[
'compAbsThroughput'][0, :]
1164 inParams[
'COMPREFOFFSET'][:] = parCat[
'compRefOffset'][0, :]
1165 inParams[
'COMPREFSIGMA'][:] = parCat[
'compRefSigma'][0, :]
1166 inParams[
'COMPMIRRORCHROMATICITY'][:] = parCat[
'compMirrorChromaticity'][0, :]
1167 inParams[
'MIRRORCHROMATICITYPIVOT'][:] = parCat[
'mirrorChromaticityPivot'][0, :]
1168 inParams[
'COMPAPERCORRPIVOT'][:] = parCat[
'compAperCorrPivot'][0, :]
1169 inParams[
'COMPAPERCORRSLOPE'][:] = parCat[
'compAperCorrSlope'][0, :]
1170 inParams[
'COMPAPERCORRSLOPEERR'][:] = parCat[
'compAperCorrSlopeErr'][0, :]
1171 inParams[
'COMPAPERCORRRANGE'][:] = parCat[
'compAperCorrRange'][0, :]
1172 inParams[
'COMPMODELERREXPTIMEPIVOT'][:] = parCat[
'compModelErrExptimePivot'][0, :]
1173 inParams[
'COMPMODELERRFWHMPIVOT'][:] = parCat[
'compModelErrFwhmPivot'][0, :]
1174 inParams[
'COMPMODELERRSKYPIVOT'][:] = parCat[
'compModelErrSkyPivot'][0, :]
1175 inParams[
'COMPMODELERRPARS'][:] = parCat[
'compModelErrPars'][0, :]
1176 inParams[
'COMPEXPGRAY'][:] = parCat[
'compExpGray'][0, :]
1177 inParams[
'COMPVARGRAY'][:] = parCat[
'compVarGray'][0, :]
1178 inParams[
'COMPNGOODSTARPEREXP'][:] = parCat[
'compNGoodStarPerExp'][0, :]
1179 inParams[
'COMPSIGFGCM'][:] = parCat[
'compSigFgcm'][0, :]
1180 inParams[
'COMPSIGMACAL'][:] = parCat[
'compSigmaCal'][0, :]
1181 inParams[
'COMPRETRIEVEDLNPWV'][:] = parCat[
'compRetrievedLnPwv'][0, :]
1182 inParams[
'COMPRETRIEVEDLNPWVRAW'][:] = parCat[
'compRetrievedLnPwvRaw'][0, :]
1183 inParams[
'COMPRETRIEVEDLNPWVFLAG'][:] = parCat[
'compRetrievedLnPwvFlag'][0, :]
1184 inParams[
'COMPRETRIEVEDTAUNIGHT'][:] = parCat[
'compRetrievedTauNight'][0, :]
1186 inSuperStar = np.zeros(parCat[
'superstarSize'][0, :], dtype=
'f8')
1187 inSuperStar[:, :, :, :] = parCat[
'superstar'][0, :].reshape(inSuperStar.shape)
1189 return (inParInfo, inParams, inSuperStar)
1191 def _persistFgcmDatasets(self, butler, fgcmFitCycle):
1193 Persist FGCM datasets through the butler.
1197 butler: `lsst.daf.persistence.Butler`
1198 fgcmFitCycle: `lsst.fgcm.FgcmFitCycle`
1199 Fgcm Fit cycle object
1203 parInfo, pars = fgcmFitCycle.fgcmPars.parsToArrays()
1205 parSchema = afwTable.Schema()
1208 lutFilterNameString = comma.join([n.decode(
'utf-8')
1209 for n
in parInfo[
'LUTFILTERNAMES'][0]])
1210 fitBandString = comma.join([n.decode(
'utf-8')
1211 for n
in parInfo[
'FITBANDS'][0]])
1213 parSchema = self.
_makeParSchema(parInfo, pars, fgcmFitCycle.fgcmPars.parSuperStarFlat,
1214 lutFilterNameString, fitBandString)
1216 fgcmFitCycle.fgcmPars.parSuperStarFlat,
1217 lutFilterNameString, fitBandString)
1219 butler.put(parCat,
'fgcmFitParameters', fgcmcycle=self.config.cycleNumber)
1225 flagStarStruct = fgcmFitCycle.fgcmStars.getFlagStarIndices()
1228 butler.put(flagStarCat,
'fgcmFlaggedStars', fgcmcycle=self.config.cycleNumber)
1232 superStarChebSize = fgcmFitCycle.fgcmZpts.zpStruct[
'FGCM_FZPT_SSTAR_CHEB'].shape[1]
1233 zptChebSize = fgcmFitCycle.fgcmZpts.zpStruct[
'FGCM_FZPT_CHEB'].shape[1]
1236 zptCat =
makeZptCat(zptSchema, fgcmFitCycle.fgcmZpts.zpStruct)
1238 butler.put(zptCat,
'fgcmZeropoints', fgcmcycle=self.config.cycleNumber)
1243 atmCat =
makeAtmCat(atmSchema, fgcmFitCycle.fgcmZpts.atmStruct)
1245 butler.put(atmCat,
'fgcmAtmosphereParameters', fgcmcycle=self.config.cycleNumber)
1249 stdStruct, goodBands = fgcmFitCycle.fgcmStars.retrieveStdStarCatalog(fgcmFitCycle.fgcmPars)
1251 stdCat =
makeStdCat(stdSchema, stdStruct, goodBands)
1253 butler.put(stdCat,
'fgcmStandardStars', fgcmcycle=self.config.cycleNumber)
1255 def _makeParSchema(self, parInfo, pars, parSuperStarFlat,
1256 lutFilterNameString, fitBandString):
1258 Make the parameter persistence schema
1262 parInfo: `numpy.ndarray`
1263 Parameter information returned by fgcm
1264 pars: `numpy.ndarray`
1265 Parameter values returned by fgcm
1266 parSuperStarFlat: `numpy.array`
1267 Superstar flat values returned by fgcm
1268 lutFilterNameString: `str`
1269 Combined string of all the lutFilterNames
1270 fitBandString: `str`
1271 Combined string of all the fitBands
1275 parSchema: `afwTable.schema`
1278 parSchema = afwTable.Schema()
1281 parSchema.addField(
'nCcd', type=np.int32, doc=
'Number of CCDs')
1282 parSchema.addField(
'lutFilterNames', type=str, doc=
'LUT Filter names in parameter file',
1283 size=len(lutFilterNameString))
1284 parSchema.addField(
'fitBands', type=str, doc=
'Bands that were fit',
1285 size=len(fitBandString))
1286 parSchema.addField(
'lnTauUnit', type=np.float64, doc=
'Step units for ln(AOD)')
1287 parSchema.addField(
'lnTauSlopeUnit', type=np.float64,
1288 doc=
'Step units for ln(AOD) slope')
1289 parSchema.addField(
'alphaUnit', type=np.float64, doc=
'Step units for alpha')
1290 parSchema.addField(
'lnPwvUnit', type=np.float64, doc=
'Step units for ln(pwv)')
1291 parSchema.addField(
'lnPwvSlopeUnit', type=np.float64,
1292 doc=
'Step units for ln(pwv) slope')
1293 parSchema.addField(
'lnPwvQuadraticUnit', type=np.float64,
1294 doc=
'Step units for ln(pwv) quadratic term')
1295 parSchema.addField(
'lnPwvGlobalUnit', type=np.float64,
1296 doc=
'Step units for global ln(pwv) parameters')
1297 parSchema.addField(
'o3Unit', type=np.float64, doc=
'Step units for O3')
1298 parSchema.addField(
'qeSysUnit', type=np.float64, doc=
'Step units for mirror gray')
1299 parSchema.addField(
'filterOffsetUnit', type=np.float64, doc=
'Step units for filter offset')
1300 parSchema.addField(
'hasExternalPwv', type=np.int32, doc=
'Parameters fit using external pwv')
1301 parSchema.addField(
'hasExternalTau', type=np.int32, doc=
'Parameters fit using external tau')
1304 parSchema.addField(
'parAlpha', type=
'ArrayD', doc=
'Alpha parameter vector',
1305 size=pars[
'PARALPHA'].size)
1306 parSchema.addField(
'parO3', type=
'ArrayD', doc=
'O3 parameter vector',
1307 size=pars[
'PARO3'].size)
1308 parSchema.addField(
'parLnTauIntercept', type=
'ArrayD',
1309 doc=
'ln(Tau) intercept parameter vector',
1310 size=pars[
'PARLNTAUINTERCEPT'].size)
1311 parSchema.addField(
'parLnTauSlope', type=
'ArrayD',
1312 doc=
'ln(Tau) slope parameter vector',
1313 size=pars[
'PARLNTAUSLOPE'].size)
1314 parSchema.addField(
'parLnPwvIntercept', type=
'ArrayD', doc=
'ln(pwv) intercept parameter vector',
1315 size=pars[
'PARLNPWVINTERCEPT'].size)
1316 parSchema.addField(
'parLnPwvSlope', type=
'ArrayD', doc=
'ln(pwv) slope parameter vector',
1317 size=pars[
'PARLNPWVSLOPE'].size)
1318 parSchema.addField(
'parLnPwvQuadratic', type=
'ArrayD', doc=
'ln(pwv) quadratic parameter vector',
1319 size=pars[
'PARLNPWVQUADRATIC'].size)
1320 parSchema.addField(
'parQeSysIntercept', type=
'ArrayD', doc=
'Mirror gray intercept parameter vector',
1321 size=pars[
'PARQESYSINTERCEPT'].size)
1322 parSchema.addField(
'compQeSysSlope', type=
'ArrayD', doc=
'Mirror gray slope parameter vector',
1323 size=pars[0][
'COMPQESYSSLOPE'].size)
1324 parSchema.addField(
'parFilterOffset', type=
'ArrayD', doc=
'Filter offset parameter vector',
1325 size=pars[
'PARFILTEROFFSET'].size)
1326 parSchema.addField(
'parFilterOffsetFitFlag', type=
'ArrayI', doc=
'Filter offset parameter fit flag',
1327 size=pars[
'PARFILTEROFFSETFITFLAG'].size)
1328 parSchema.addField(
'parRetrievedLnPwvScale', type=np.float64,
1329 doc=
'Global scale for retrieved ln(pwv)')
1330 parSchema.addField(
'parRetrievedLnPwvOffset', type=np.float64,
1331 doc=
'Global offset for retrieved ln(pwv)')
1332 parSchema.addField(
'parRetrievedLnPwvNightlyOffset', type=
'ArrayD',
1333 doc=
'Nightly offset for retrieved ln(pwv)',
1334 size=pars[
'PARRETRIEVEDLNPWVNIGHTLYOFFSET'].size)
1335 parSchema.addField(
'compAbsThroughput', type=
'ArrayD',
1336 doc=
'Absolute throughput (relative to transmission curves)',
1337 size=pars[
'COMPABSTHROUGHPUT'].size)
1338 parSchema.addField(
'compRefOffset', type=
'ArrayD',
1339 doc=
'Offset between reference stars and calibrated stars',
1340 size=pars[
'COMPREFOFFSET'].size)
1341 parSchema.addField(
'compRefSigma', type=
'ArrayD',
1342 doc=
'Width of reference star/calibrated star distribution',
1343 size=pars[
'COMPREFSIGMA'].size)
1344 parSchema.addField(
'compMirrorChromaticity', type=
'ArrayD',
1345 doc=
'Computed mirror chromaticity terms',
1346 size=pars[
'COMPMIRRORCHROMATICITY'].size)
1347 parSchema.addField(
'mirrorChromaticityPivot', type=
'ArrayD',
1348 doc=
'Mirror chromaticity pivot mjd',
1349 size=pars[
'MIRRORCHROMATICITYPIVOT'].size)
1350 parSchema.addField(
'compAperCorrPivot', type=
'ArrayD', doc=
'Aperture correction pivot',
1351 size=pars[
'COMPAPERCORRPIVOT'].size)
1352 parSchema.addField(
'compAperCorrSlope', type=
'ArrayD', doc=
'Aperture correction slope',
1353 size=pars[
'COMPAPERCORRSLOPE'].size)
1354 parSchema.addField(
'compAperCorrSlopeErr', type=
'ArrayD', doc=
'Aperture correction slope error',
1355 size=pars[
'COMPAPERCORRSLOPEERR'].size)
1356 parSchema.addField(
'compAperCorrRange', type=
'ArrayD', doc=
'Aperture correction range',
1357 size=pars[
'COMPAPERCORRRANGE'].size)
1358 parSchema.addField(
'compModelErrExptimePivot', type=
'ArrayD', doc=
'Model error exptime pivot',
1359 size=pars[
'COMPMODELERREXPTIMEPIVOT'].size)
1360 parSchema.addField(
'compModelErrFwhmPivot', type=
'ArrayD', doc=
'Model error fwhm pivot',
1361 size=pars[
'COMPMODELERRFWHMPIVOT'].size)
1362 parSchema.addField(
'compModelErrSkyPivot', type=
'ArrayD', doc=
'Model error sky pivot',
1363 size=pars[
'COMPMODELERRSKYPIVOT'].size)
1364 parSchema.addField(
'compModelErrPars', type=
'ArrayD', doc=
'Model error parameters',
1365 size=pars[
'COMPMODELERRPARS'].size)
1366 parSchema.addField(
'compExpGray', type=
'ArrayD', doc=
'Computed exposure gray',
1367 size=pars[
'COMPEXPGRAY'].size)
1368 parSchema.addField(
'compVarGray', type=
'ArrayD', doc=
'Computed exposure variance',
1369 size=pars[
'COMPVARGRAY'].size)
1370 parSchema.addField(
'compNGoodStarPerExp', type=
'ArrayI',
1371 doc=
'Computed number of good stars per exposure',
1372 size=pars[
'COMPNGOODSTARPEREXP'].size)
1373 parSchema.addField(
'compSigFgcm', type=
'ArrayD', doc=
'Computed sigma_fgcm (intrinsic repeatability)',
1374 size=pars[
'COMPSIGFGCM'].size)
1375 parSchema.addField(
'compSigmaCal', type=
'ArrayD', doc=
'Computed sigma_cal (systematic error floor)',
1376 size=pars[
'COMPSIGMACAL'].size)
1377 parSchema.addField(
'compRetrievedLnPwv', type=
'ArrayD', doc=
'Retrieved ln(pwv) (smoothed)',
1378 size=pars[
'COMPRETRIEVEDLNPWV'].size)
1379 parSchema.addField(
'compRetrievedLnPwvRaw', type=
'ArrayD', doc=
'Retrieved ln(pwv) (raw)',
1380 size=pars[
'COMPRETRIEVEDLNPWVRAW'].size)
1381 parSchema.addField(
'compRetrievedLnPwvFlag', type=
'ArrayI', doc=
'Retrieved ln(pwv) Flag',
1382 size=pars[
'COMPRETRIEVEDLNPWVFLAG'].size)
1383 parSchema.addField(
'compRetrievedTauNight', type=
'ArrayD', doc=
'Retrieved tau (per night)',
1384 size=pars[
'COMPRETRIEVEDTAUNIGHT'].size)
1386 parSchema.addField(
'superstarSize', type=
'ArrayI', doc=
'Superstar matrix size',
1388 parSchema.addField(
'superstar', type=
'ArrayD', doc=
'Superstar matrix (flattened)',
1389 size=parSuperStarFlat.size)
1393 def _makeParCatalog(self, parSchema, parInfo, pars, parSuperStarFlat,
1394 lutFilterNameString, fitBandString):
1396 Make the FGCM parameter catalog for persistence
1400 parSchema: `lsst.afw.table.Schema`
1401 Parameter catalog schema
1402 pars: `numpy.ndarray`
1403 FGCM parameters to put into parCat
1404 parSuperStarFlat: `numpy.array`
1405 FGCM superstar flat array to put into parCat
1406 lutFilterNameString: `str`
1407 Combined string of all the lutFilterNames
1408 fitBandString: `str`
1409 Combined string of all the fitBands
1413 parCat: `afwTable.BasicCatalog`
1414 Atmosphere and instrumental model parameter catalog for persistence
1417 parCat = afwTable.BaseCatalog(parSchema)
1422 rec = parCat.addNew()
1425 rec[
'nCcd'] = parInfo[
'NCCD']
1426 rec[
'lutFilterNames'] = lutFilterNameString
1427 rec[
'fitBands'] = fitBandString
1429 rec[
'hasExternalPwv'] = 0
1430 rec[
'hasExternalTau'] = 0
1434 scalarNames = [
'parRetrievedLnPwvScale',
'parRetrievedLnPwvOffset']
1436 arrNames = [
'parAlpha',
'parO3',
'parLnTauIntercept',
'parLnTauSlope',
1437 'parLnPwvIntercept',
'parLnPwvSlope',
'parLnPwvQuadratic',
1438 'parQeSysIntercept',
'compQeSysSlope',
1439 'parRetrievedLnPwvNightlyOffset',
'compAperCorrPivot',
1440 'parFilterOffset',
'parFilterOffsetFitFlag',
1441 'compAbsThroughput',
'compRefOffset',
'compRefSigma',
1442 'compMirrorChromaticity',
'mirrorChromaticityPivot',
1443 'compAperCorrSlope',
'compAperCorrSlopeErr',
'compAperCorrRange',
1444 'compModelErrExptimePivot',
'compModelErrFwhmPivot',
1445 'compModelErrSkyPivot',
'compModelErrPars',
1446 'compExpGray',
'compVarGray',
'compNGoodStarPerExp',
'compSigFgcm',
1448 'compRetrievedLnPwv',
'compRetrievedLnPwvRaw',
'compRetrievedLnPwvFlag',
1449 'compRetrievedTauNight']
1451 for scalarName
in scalarNames:
1452 rec[scalarName] = pars[scalarName.upper()]
1454 for arrName
in arrNames:
1455 rec[arrName][:] = np.atleast_1d(pars[0][arrName.upper()])[:]
1458 rec[
'superstarSize'][:] = parSuperStarFlat.shape
1459 rec[
'superstar'][:] = parSuperStarFlat.flatten()
1463 def _makeFlagStarSchema(self):
1465 Make the flagged-stars schema
1469 flagStarSchema: `lsst.afw.table.Schema`
1472 flagStarSchema = afwTable.Schema()
1474 flagStarSchema.addField(
'objId', type=np.int32, doc=
'FGCM object id')
1475 flagStarSchema.addField(
'objFlag', type=np.int32, doc=
'FGCM object flag')
1477 return flagStarSchema
1479 def _makeFlagStarCat(self, flagStarSchema, flagStarStruct):
1481 Make the flagged star catalog for persistence
1485 flagStarSchema: `lsst.afw.table.Schema`
1487 flagStarStruct: `numpy.ndarray`
1488 Flagged star structure from fgcm
1492 flagStarCat: `lsst.afw.table.BaseCatalog`
1493 Flagged star catalog for persistence
1496 flagStarCat = afwTable.BaseCatalog(flagStarSchema)
1497 flagStarCat.resize(flagStarStruct.size)
1499 flagStarCat[
'objId'][:] = flagStarStruct[
'OBJID']
1500 flagStarCat[
'objFlag'][:] = flagStarStruct[
'OBJFLAG']