23"""Perform a single fit cycle of FGCM.
25This task runs a single "fit cycle" of fgcm. Prior to running this task
26one must run both fgcmMakeLut (to construct the atmosphere and instrumental
27look-up-table) and fgcmBuildStars (to extract visits and star observations
30The 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
32be inspected to set parameters for outlier rejection on the following
33cycle. Please see the fgcmcal Cookbook for details.
40import lsst.pex.config
as pexConfig
41import lsst.pipe.base
as pipeBase
42from lsst.pipe.base
import connectionTypes
43import lsst.afw.table
as afwTable
45from .utilities
import makeConfigDict, translateFgcmLut, translateVisitCatalog
46from .utilities
import extractReferenceMags
47from .utilities
import makeZptSchema, makeZptCat
48from .utilities
import makeAtmSchema, makeAtmCat, makeStdSchema, makeStdCat
49from .sedterms
import SedboundarytermDict, SedtermDict
50from .focalPlaneProjector
import FocalPlaneProjector
54__all__ = [
'FgcmFitCycleConfig',
'FgcmFitCycleTask']
56MULTIPLE_CYCLES_MAX = 10
60 dimensions=(
"instrument",),
61 defaultTemplates={
"previousCycleNumber":
"-1",
63 camera = connectionTypes.PrerequisiteInput(
64 doc=
"Camera instrument",
66 storageClass=
"Camera",
67 dimensions=(
"instrument",),
71 fgcmLookUpTable = connectionTypes.PrerequisiteInput(
72 doc=(
"Atmosphere + instrument look-up-table for FGCM throughput and "
73 "chromatic corrections."),
74 name=
"fgcmLookUpTable",
75 storageClass=
"Catalog",
76 dimensions=(
"instrument",),
80 fgcmVisitCatalog = connectionTypes.Input(
81 doc=
"Catalog of visit information for fgcm",
82 name=
"fgcmVisitCatalog",
83 storageClass=
"Catalog",
84 dimensions=(
"instrument",),
88 fgcmStarObservationsParquet = connectionTypes.Input(
89 doc=(
"Catalog of star observations for fgcm, in parquet format. "
90 "Used if useParquetCatalogFormat is True."),
91 name=
"fgcm_star_observations",
92 storageClass=
"ArrowAstropy",
93 dimensions=(
"instrument",),
97 fgcmStarIdsParquet = connectionTypes.Input(
98 doc=(
"Catalog of fgcm calibration star IDs, in parquet format. "
99 "Used if useParquetCatalogFormat is True."),
100 name=
"fgcm_star_ids",
101 storageClass=
"ArrowAstropy",
102 dimensions=(
"instrument",),
106 fgcmReferenceStarsParquet = connectionTypes.Input(
107 doc=(
"Catalog of fgcm-matched reference stars, in parquet format. "
108 "Used if useParquetCatalogFormat is True."),
109 name=
"fgcm_reference_stars",
110 storageClass=
"ArrowAstropy",
111 dimensions=(
"instrument",),
115 fgcmStarObservations = connectionTypes.Input(
116 doc=(
"Catalog of star observations for fgcm; old format. "
117 "Used if useParquetCatalogFormat is False."),
118 name=
"fgcmStarObservations",
119 storageClass=
"Catalog",
120 dimensions=(
"instrument",),
124 fgcmStarIds = connectionTypes.Input(
125 doc=(
"Catalog of fgcm calibration star IDs. "
126 "Used if useParquetCatalogFormat is False."),
128 storageClass=
"Catalog",
129 dimensions=(
"instrument",),
133 fgcmStarIndices = connectionTypes.Input(
134 doc=(
"Catalog of fgcm calibration star indices; old format."
135 "Used if useParquetCatalogFormat is False."),
136 name=
"fgcmStarIndices",
137 storageClass=
"Catalog",
138 dimensions=(
"instrument",),
142 fgcmReferenceStars = connectionTypes.Input(
143 doc=(
"Catalog of fgcm-matched reference stars; old format."
144 "Used if useParquetCatalogFormat is False."),
145 name=
"fgcmReferenceStars",
146 storageClass=
"Catalog",
147 dimensions=(
"instrument",),
151 def __init__(self, *, config=None):
152 super().__init__(config=config)
157 if str(int(config.connections.cycleNumber)) != config.connections.cycleNumber:
158 raise ValueError(
"cycleNumber must be of integer format")
159 if str(int(config.connections.previousCycleNumber)) != config.connections.previousCycleNumber:
160 raise ValueError(
"previousCycleNumber must be of integer format")
164 if int(config.connections.previousCycleNumber) != (int(config.connections.cycleNumber) - 1):
165 raise ValueError(
"previousCycleNumber must be 1 less than cycleNumber")
167 instDims = (
"instrument",)
168 bandDims = (
"instrument",
"band")
169 filterDims = (
"instrument",
"physical_filter")
170 filterDetectorDims = (
"instrument",
"physical_filter",
"detector")
172 inputAndOutputConnections = [
173 (
"FitParameters",
"Catalog",
"Catalog of fgcm fit parameters.", instDims),
174 (
"FlaggedStars",
"Catalog",
"Catalog of flagged stars for fgcm calibration.", instDims),
176 multicycleOutputConnections = [
177 (
"OutputConfig",
"Config",
"Configuration for next fgcm fit cycle.", instDims),
179 optionalZpOutputConnections = [
180 (
"Zeropoints",
"Catalog",
"Catalog of fgcm zeropoint data.", instDims),
181 (
"AtmosphereParameters",
"Catalog",
"Catalog of atmospheric fit parameters.", instDims),
183 optionalStarOutputConnections = [
184 (
"StandardStars",
"SimpleCatalog",
"Catalog of standard star magnitudes.", instDims),
187 epochs = [f
"epoch{i}" for i
in range(len(config.epochMjds))]
191 (
"Zeropoints_Plot",
"Plot",
"Plot of fgcm zeropoints.", instDims),
194 "Plot of gray term per exposure per time for deep fields.",
196 (
"NightlyAlpha_Plot",
"Plot",
"Plot of nightly AOD alpha term.", instDims),
197 (
"NightlyTau_Plot",
"Plot",
"Plot of nightly aerosol optical depth (tau).", instDims),
198 (
"NightlyPwv_Plot",
"Plot",
"Plot of nightly water vapor.", instDims),
199 (
"NightlyO3_Plot",
"Plot",
"Plot of nightly ozone.", instDims),
200 (
"FilterOffsets_Plot",
"Plot",
"Plot of in-band filter offsets.", instDims),
201 (
"AbsThroughputs_Plot",
"Plot",
"Plot of absolute throughput fractions.", instDims),
202 (
"QESysWashesInitial_Plot",
"Plot",
"Plot of initial system QE with mirror washes.", instDims),
203 (
"QESysWashesFinal_Plot",
"Plot",
"Plot of final system QE with mirror washes.", instDims),
204 (
"RpwvVsRpwvInput_Plot",
206 "Plot of change in per-visit ``retrieved`` PWV from previous fit cycle.",
208 (
"RpwvVsRpwvSmooth_Plot",
210 "Plot of per-visit ``retrieved`` PWV vs. smoothed PWV.",
212 (
"ModelPwvVsRpwv_Plot",
214 "Plot of model PWV vs. per-visit ``retrieved`` PWV.",
218 "Plot of chisq as a function of iteration.",
222 plotConnections.extend(
224 (
"Apercorr_Plot",
"Plot",
"Plot of fgcm aperture corrections.", bandDims),
225 (
"EpsilonGlobal_Plot",
227 "Plot of global background over/undersubtraction.",
231 "Map of spatially varying background over/undersubtraction.",
233 (
"ExpgrayInitial_Plot",
235 "Histogram of initial gray term per exposure.",
237 (
"CompareRedblueExpgray_Plot",
239 "Plot of red/blue split gray term per exposure",
241 (
"Expgray_Plot",
"Plot",
"Histogram of gray term per exposure.", bandDims),
242 (
"ExpgrayAirmass_Plot",
244 "Plot of exposure gray term as a function of airmass.",
246 (
"ExpgrayCompareMjdRedblue_Plot",
248 "Plot of red/blue split gray term per exposure as a function of time.",
252 "Plot of grey term per exposure as a function of time of night.",
254 (
"ExpgrayCompareBands_Plot",
256 "Plot of gray term per exposure between bands nearby in time.",
258 (
"ExpgrayReference_Plot",
260 "Histogram of gray term per exposure compared to reference mags.",
262 (
"QESysRefstarsStdInitial_Plot",
264 "Plot of reference mag - calibrated (standard) mag vs. time (before fit).",
266 (
"QESysRefstarsStdFinal_Plot",
268 "Plot of reference mag - calibrated (standard) mag vs. time (after fit).",
270 (
"QESysRefstarsObsInitial_Plot",
272 "Plot of reference mag - observed (instrumental) mag vs. time (before fit).",
274 (
"QESysRefstarsObsFinal_Plot",
276 "Plot of reference mag - observed (instrumental) mag vs. time (after fit).",
278 (
"ModelMagerrInitial_Plot",
280 "Plots for magnitude error model, initial estimate.",
282 (
"ModelMagerrPostfit_Plot",
284 "Plots for magnitude error model, after fitting.",
286 (
"SigmaFgcmAllStars_Plot",
288 "Histograms for intrinsic scatter for all bright stars.",
290 (
"SigmaFgcmReservedStars_Plot",
292 "Histograms for intrinsic scatter for reserved bright stars.",
294 (
"SigmaFgcmReservedStarsCrunched_Plot",
296 "Histograms for intrinsic scatter for reserved bright stars (after gray correction).",
298 (
"SigmaFgcmReservedStarsCrunchedNonPhotom_Plot",
300 "Histograms for intrinsic scatter for reserved bright stars (including non-photometric).",
302 (
"SigmaFgcmPullsAllStars_Plot",
304 "Histograms for pulls for all bright stars.",
306 (
"SigmaFgcmPullsReservedStars_Plot",
308 "Histograms for pulls for reserved bright stars.",
310 (
"SigmaFgcmPullsReservedStarsCrunched_Plot",
312 "Histograms for pulls for reserved bright stars (after gray correction).",
314 (
"SigmaFgcmPullsReservedStarsCrunchedNonPhotom_Plot",
316 "Histograms for pulls for reserved bright stars (including non-photometric).",
320 "Plot showing scatter as a function of systematic error floor.",
324 "Histograms of scatter with respect to reference stars.",
326 (
"RefResidVsColorAll_Plot",
328 "Plot of reference star residuals vs. color (all stars).",
330 (
"RefResidVsColorCut_Plot",
332 "Plot of reference star residuals vs. color (reference star color cuts).",
337 plotConnections.extend(
339 (
"I1R1_Plot",
"Plot",
"Plot of fgcm R1 vs. I1.", filterDims),
340 (
"I1_Plot",
"Plot",
"Focal plane map of fgcm I1.", filterDims),
341 (
"R1_Plot",
"Plot",
"Focal plane map of fgcm R1.", filterDims),
342 (
"R1mI1Matchscale_Plot",
"Plot",
"Focal plane map of fgcm R1 - I1.", filterDims),
343 (
"R1mI1_Plot",
"Plot",
"Focal plane map of fgcm R1 - I1 (rescaled).", filterDims),
344 (
"R1mI1_vs_mjd_Plot",
"Plot",
"R1 - I1 residuals vs. mjd.", filterDims),
345 (
"CompareRedblueMirrorchrom_Plot",
347 "Comparison of mirror chromaticity residuals for red/blue stars.",
349 (
"CcdChromaticity_Plot",
351 "Focal plane map of fgcm ccd chromaticity.",
353 (
"EpsilonDetector_Plot",
355 "Focal plane map of background over/undersubtraction.",
357 (
"EpsilonDetectorMatchscale_Plot",
359 "Focal plane map of background over/undersubtraction.",
364 plotConnections.extend(
367 f
"Superstar_{epoch}_Plot",
369 "Plot of illumination Correction.",
374 if self.config.superStarPlotCcdResiduals:
375 plotConnections.extend(
378 f
"SuperstarResidual_{epoch}_Plot",
380 "Binned illumination correction residuals.",
384 f
"SuperstarResidualStd_{epoch}_Plot",
386 "Binned illumination correction residual stdev.",
392 if config.doMultipleCycles:
396 for cycle
in range(config.multipleCyclesFinalCycleNumber):
397 outputConnections = copy.copy(inputAndOutputConnections)
398 outputConnections.extend(multicycleOutputConnections)
399 if config.outputZeropointsBeforeFinalCycle:
400 outputConnections.extend(optionalZpOutputConnections)
401 if config.outputStandardsBeforeFinalCycle:
402 outputConnections.extend(optionalStarOutputConnections)
410 if cycle == (config.multipleCyclesFinalCycleNumber - 1) \
411 or config.doPlotsBeforeFinalCycles:
412 outputConnections.extend(plotConnections)
414 for (name, storageClass, doc, dims)
in outputConnections:
415 connectionName = f
"fgcm_Cycle{cycle}_{name}"
416 storageName = connectionName
417 outConnection = connectionTypes.Output(
419 storageClass=storageClass,
422 multiple=(len(dims) > 1),
424 setattr(self, connectionName, outConnection)
427 outputConnections = copy.copy(inputAndOutputConnections)
428 outputConnections.extend(multicycleOutputConnections)
429 outputConnections.extend(optionalZpOutputConnections)
430 outputConnections.extend(optionalStarOutputConnections)
432 outputConnections.extend(plotConnections)
433 for (name, storageClass, doc, dims)
in outputConnections:
434 connectionName = f
"fgcm_Cycle{config.multipleCyclesFinalCycleNumber}_{name}"
435 storageName = connectionName
436 outConnection = connectionTypes.Output(
438 storageClass=storageClass,
441 multiple=(len(dims) > 1),
443 setattr(self, connectionName, outConnection)
446 if config.cycleNumber > 0:
447 inputConnections = copy.copy(inputAndOutputConnections)
449 inputConnections = []
450 outputConnections = copy.copy(inputAndOutputConnections)
455 if config.isFinalCycle
or config.outputZeropointsBeforeFinalCycle:
456 outputConnections.extend(optionalZpOutputConnections)
457 if config.isFinalCycle
or config.outputStandardsBeforeFinalCycle:
458 outputConnections.extend(optionalStarOutputConnections)
461 outputConnections.extend(plotConnections)
463 for (name, storageClass, doc, dims)
in inputConnections:
464 connectionName = f
"fgcm{name}Input"
465 storageName = f
"fgcm_Cycle{config.cycleNumber - 1}_{name}"
466 inConnection = connectionTypes.PrerequisiteInput(
468 storageClass=storageClass,
472 setattr(self, connectionName, inConnection)
474 for (name, storageClass, doc, dims)
in outputConnections:
475 connectionName = f
"fgcm{name}"
476 storageName = f
"fgcm_Cycle{config.cycleNumber}_{name}"
478 if storageClass ==
"Plot":
479 connectionName = storageName
480 outConnection = connectionTypes.Output(
482 storageClass=storageClass,
485 multiple=(len(dims) > 1),
487 setattr(self, connectionName, outConnection)
489 if not config.doReferenceCalibration:
490 self.inputs.remove(
"fgcmReferenceStars")
491 self.inputs.remove(
"fgcmReferenceStarsParquet")
493 if config.useParquetCatalogFormat:
494 self.inputs.remove(
"fgcmStarObservations")
495 self.inputs.remove(
"fgcmStarIds")
496 self.inputs.remove(
"fgcmStarIndices")
497 if config.doReferenceCalibration:
498 self.inputs.remove(
"fgcmReferenceStars")
500 self.inputs.remove(
"fgcmStarObservationsParquet")
501 self.inputs.remove(
"fgcmStarIdsParquet")
502 if config.doReferenceCalibration:
503 self.inputs.remove(
"fgcmReferenceStarsParquet")
506class FgcmFitCycleConfig(pipeBase.PipelineTaskConfig,
507 pipelineConnections=FgcmFitCycleConnections):
508 """Config for FgcmFitCycle"""
510 doMultipleCycles = pexConfig.Field(
511 doc=
"Run multiple fit cycles in one task",
515 useParquetCatalogFormat = pexConfig.Field(
516 doc=
"Use parquet catalog format?",
520 multipleCyclesFinalCycleNumber = pexConfig.RangeField(
521 doc=(
"Final cycle number in multiple cycle mode. The initial cycle "
522 "is 0, with limited parameters fit. The next cycle is 1 with "
523 "full parameter fit. The final cycle is a clean-up with no "
524 "parameters fit. There will be a total of "
525 "(multipleCycleFinalCycleNumber + 1) cycles run, and the final "
526 "cycle number cannot be less than 2."),
530 max=MULTIPLE_CYCLES_MAX,
533 bands = pexConfig.ListField(
534 doc=
"Bands to run calibration",
538 fitBands = pexConfig.ListField(
539 doc=(
"Bands to use in atmospheric fit. The bands not listed here will have "
540 "the atmosphere constrained from the 'fitBands' on the same night. "
541 "Must be a subset of `config.bands`"),
545 requiredBands = pexConfig.ListField(
546 doc=(
"Bands that are required for a star to be considered a calibration star. "
547 "Must be a subset of `config.bands`"),
551 physicalFilterMap = pexConfig.DictField(
552 doc=
"Mapping from 'physicalFilter' to band.",
557 doReferenceCalibration = pexConfig.Field(
558 doc=
"Use reference catalog as additional constraint on calibration",
562 refStarSnMin = pexConfig.Field(
563 doc=
"Reference star signal-to-noise minimum to use in calibration. Set to <=0 for no cut.",
567 refStarOutlierNSig = pexConfig.Field(
568 doc=(
"Number of sigma compared to average mag for reference star to be considered an outlier. "
569 "Computed per-band, and if it is an outlier in any band it is rejected from fits."),
573 applyRefStarColorCuts = pexConfig.Field(
574 doc=(
"Apply color cuts defined in ``starColorCuts`` to reference stars? "
575 "These cuts are in addition to any cuts defined in ``refStarColorCuts``"),
579 refStarMaxFracUse = pexConfig.Field(
580 doc=(
"Maximum fraction of reference stars to use in the fit. Remainder will "
581 "be used only for validation."),
585 useExposureReferenceOffset = pexConfig.Field(
586 doc=(
"Use per-exposure (visit) offsets between calibrated stars and reference stars "
587 "for final zeropoints? This may help uniformity for disjoint surveys."),
591 nStarPerRun = pexConfig.Field(
592 doc=
"Number of stars to run in each chunk. Larger values tend to be faster for large "
593 "datasets, at the expense of some memory overhead.",
597 nStarPerGrayRun = pexConfig.Field(
598 doc=
"Number of stars to run in each chunk (including gray correction). Increasing this "
599 "value may increase the peak memory use significantly.",
603 nExpPerRun = pexConfig.Field(
604 doc=
"Number of exposures to run in each chunk",
608 reserveFraction = pexConfig.Field(
609 doc=
"Fraction of stars to reserve for testing",
613 freezeStdAtmosphere = pexConfig.Field(
614 doc=
"Freeze atmosphere parameters to standard (for testing)",
618 precomputeSuperStarInitialCycle = pexConfig.Field(
619 doc=
"Precompute superstar flat for initial cycle",
623 superStarSubCcdDict = pexConfig.DictField(
624 doc=(
"Per-band specification on whether to compute superstar flat on sub-ccd scale. "
625 "Must have one entry per band."),
630 superStarSubCcdChebyshevOrder = pexConfig.Field(
631 doc=(
"Order of the 2D chebyshev polynomials for sub-ccd superstar fit. "
632 "Global default is first-order polynomials, and should be overridden "
633 "on a camera-by-camera basis depending on the ISR."),
637 superStarSubCcdTriangular = pexConfig.Field(
638 doc=(
"Should the sub-ccd superstar chebyshev matrix be triangular to "
639 "suppress high-order cross terms?"),
643 superStarSigmaClip = pexConfig.Field(
644 doc=
"Number of sigma to clip outliers when selecting for superstar flats",
648 superStarPlotCcdResiduals = pexConfig.Field(
649 doc=
"If plotting is enabled, should per-detector residuals be plotted? "
650 "This may produce a lot of output, and should be used only for "
651 "debugging purposes.",
655 superStarForceZeroMean = pexConfig.Field(
656 doc=
"When computing the super-star flat, force the focal-plane mean to "
657 "zero (per band)? This should only be used when computing stand-alone "
658 "illumination corrections.",
662 focalPlaneSigmaClip = pexConfig.Field(
663 doc=
"Number of sigma to clip outliers per focal-plane.",
667 ccdGraySubCcdDict = pexConfig.DictField(
668 doc=(
"Per-band specification on whether to compute achromatic per-ccd residual "
669 "('ccd gray') on a sub-ccd scale. This will only be used as a fallback "
670 "if ``ccdGrayFocalPlaneDict[band]`` is set to True."),
675 ccdGraySubCcdChebyshevOrder = pexConfig.Field(
676 doc=
"Order of the 2D chebyshev polynomials for sub-ccd gray fit.",
680 ccdGraySubCcdTriangular = pexConfig.Field(
681 doc=(
"Should the sub-ccd gray chebyshev matrix be triangular to "
682 "suppress high-order cross terms?"),
686 ccdGrayFocalPlaneDict = pexConfig.DictField(
687 doc=(
"Per-band specification on whether to compute focal-plane residual "
688 "('ccd gray') corrections. The focal-plane residuals will be used "
689 "instead of per-CCD residuals except when there are insufficient "
690 "good CCDs (see ``ccdGrayFocalPlaneFitMinCcd``). Therefore, this "
691 "is not recommended for large focal planes with non-photometric "
697 ccdGrayFocalPlaneFitMinCcd = pexConfig.Field(
698 doc=(
"Minimum number of 'good' CCDs required to perform focal-plane "
699 "gray corrections. If there are fewer good CCDs then the gray "
700 "correction is computed per-ccd."),
704 ccdGrayFocalPlaneChebyshevOrder = pexConfig.Field(
705 doc=
"Order of the 2D chebyshev polynomials for focal plane fit.",
709 ccdGrayFocalPlaneMaxStars = pexConfig.Field(
710 doc=
"Maximum number of stars to use for focal plane fit. Required to keep "
711 "matrix memory usage from running away. If there are more stars than "
712 "this then they will be down-sampled.",
716 cycleNumber = pexConfig.Field(
717 doc=(
"FGCM fit cycle number. This is automatically incremented after each run "
718 "and stage of outlier rejection. See cookbook for details."),
722 isFinalCycle = pexConfig.Field(
723 doc=(
"Is this the final cycle of the fitting? Will automatically compute final "
724 "selection of stars and photometric exposures, and will output zeropoints "
725 "and standard stars for use in fgcmOutputProducts"),
729 maxIterBeforeFinalCycle = pexConfig.Field(
730 doc=(
"Maximum fit iterations, prior to final cycle. The number of iterations "
731 "will always be 0 in the final cycle for cleanup and final selection."),
735 deltaMagBkgOffsetPercentile = pexConfig.Field(
736 doc=(
"Percentile brightest stars on a visit/ccd to use to compute net "
737 "offset from local background subtraction."),
741 deltaMagBkgPerCcd = pexConfig.Field(
742 doc=(
"Compute net offset from local background subtraction per-ccd? "
743 "Otherwise, use computation per visit."),
747 utBoundary = pexConfig.Field(
748 doc=
"Boundary (in UTC) from day-to-day",
752 washMjds = pexConfig.ListField(
753 doc=
"Mirror wash MJDs",
757 epochMjds = pexConfig.ListField(
758 doc=
"Epoch boundaries in MJD",
762 minObsPerBand = pexConfig.Field(
763 doc=
"Minimum good observations per band",
769 latitude = pexConfig.Field(
770 doc=
"Observatory latitude",
774 mirrorArea = pexConfig.Field(
775 doc=
"Mirror area (square meters) of telescope. If not set, will "
776 "try to estimate from camera.telescopeDiameter.",
781 cameraGain = pexConfig.Field(
782 doc=
"Average camera gain. If not set, will use the median of the "
783 "camera model/detector/amplifier gains.",
788 defaultCameraOrientation = pexConfig.Field(
789 doc=
"Default camera orientation for QA plots.",
793 brightObsGrayMax = pexConfig.Field(
794 doc=
"Maximum gray extinction to be considered bright observation",
798 minStarPerCcd = pexConfig.Field(
799 doc=(
"Minimum number of good stars per CCD to be used in calibration fit. "
800 "CCDs with fewer stars will have their calibration estimated from other "
801 "CCDs in the same visit, with zeropoint error increased accordingly."),
805 minCcdPerExp = pexConfig.Field(
806 doc=(
"Minimum number of good CCDs per exposure/visit to be used in calibration fit. "
807 "Visits with fewer good CCDs will have CCD zeropoints estimated where possible."),
811 maxCcdGrayErr = pexConfig.Field(
812 doc=
"Maximum error on CCD gray offset to be considered photometric",
816 minStarPerExp = pexConfig.Field(
817 doc=(
"Minimum number of good stars per exposure/visit to be used in calibration fit. "
818 "Visits with fewer good stars will have CCD zeropoints estimated where possible."),
822 minExpPerNight = pexConfig.Field(
823 doc=
"Minimum number of good exposures/visits to consider a partly photometric night",
827 expGrayInitialCut = pexConfig.Field(
828 doc=(
"Maximum exposure/visit gray value for initial selection of possible photometric "
833 expFwhmCutDict = pexConfig.DictField(
834 doc=(
"Per-band specification on maximum exposure FWHM (arcseconds) that will "
835 "be considered for the model fit. Exposures with median FWHM larger "
836 "than this threshold will get zeropoints based on matching to good "
842 expGrayPhotometricCutDict = pexConfig.DictField(
843 doc=(
"Per-band specification on maximum (negative) achromatic exposure residual "
844 "('gray term') for a visit to be considered photometric. Must have one "
845 "entry per band. Broad-band filters should be -0.05."),
850 expGrayHighCutDict = pexConfig.DictField(
851 doc=(
"Per-band specification on maximum (positive) achromatic exposure residual "
852 "('gray term') for a visit to be considered photometric. Must have one "
853 "entry per band. Broad-band filters should be 0.2."),
858 expGrayRecoverCut = pexConfig.Field(
859 doc=(
"Maximum (negative) exposure gray to be able to recover bad ccds via interpolation. "
860 "Visits with more gray extinction will only get CCD zeropoints if there are "
861 "sufficient star observations (minStarPerCcd) on that CCD."),
865 expVarGrayPhotometricCutDict = pexConfig.DictField(
866 doc=(
"Per-band specification on maximum exposure variance to be considered possibly "
867 "photometric. Must have one entry per band. Broad-band filters should be "
873 expGrayErrRecoverCut = pexConfig.Field(
874 doc=(
"Maximum exposure gray error to be able to recover bad ccds via interpolation. "
875 "Visits with more gray variance will only get CCD zeropoints if there are "
876 "sufficient star observations (minStarPerCcd) on that CCD."),
880 aperCorrPerCcd = pexConfig.Field(
881 doc=
"Use aperture corrections per-ccd (detector) instead of per-visit?",
885 aperCorrFitNBins = pexConfig.Field(
886 doc=(
"Number of aperture bins used in aperture correction fit. When set to 0"
887 "no fit will be performed, and the config.aperCorrInputSlopes will be "
888 "used if available."),
892 aperCorrInputSlopeDict = pexConfig.DictField(
893 doc=(
"Per-band specification of aperture correction input slope parameters. These "
894 "are used on the first fit iteration, and aperture correction parameters will "
895 "be updated from the data if config.aperCorrFitNBins > 0. It is recommended "
896 "to set this when there is insufficient data to fit the parameters (e.g. "
902 sedboundaryterms = pexConfig.ConfigField(
903 doc=
"Mapping from bands to SED boundary term names used is sedterms.",
904 dtype=SedboundarytermDict,
906 sedterms = pexConfig.ConfigField(
907 doc=
"Mapping from terms to bands for fgcm linear SED approximations.",
910 sigFgcmMaxErr = pexConfig.Field(
911 doc=
"Maximum mag error for fitting sigma_FGCM",
915 sigFgcmMaxEGrayDict = pexConfig.DictField(
916 doc=(
"Per-band specification for maximum (absolute) achromatic residual (gray value) "
917 "for observations in sigma_fgcm (raw repeatability). Broad-band filters "
923 ccdGrayMaxStarErr = pexConfig.Field(
924 doc=(
"Maximum error on a star observation to use in ccd gray (achromatic residual) "
929 approxThroughputDict = pexConfig.DictField(
930 doc=(
"Per-band specification of the approximate overall throughput at the start of "
931 "calibration observations. Must have one entry per band. Typically should "
937 sigmaCalRange = pexConfig.ListField(
938 doc=
"Allowed range for systematic error floor estimation",
940 default=(0.001, 0.003),
942 sigmaCalFitPercentile = pexConfig.ListField(
943 doc=
"Magnitude percentile range to fit systematic error floor",
945 default=(0.05, 0.15),
947 sigmaCalPlotPercentile = pexConfig.ListField(
948 doc=
"Magnitude percentile range to plot systematic error floor",
950 default=(0.05, 0.95),
952 sigma0Phot = pexConfig.Field(
953 doc=
"Systematic error floor for all zeropoints",
957 mapLongitudeRef = pexConfig.Field(
958 doc=
"Reference longitude for plotting maps",
962 mapNSide = pexConfig.Field(
963 doc=
"Healpix nside for plotting maps",
967 outfileBase = pexConfig.Field(
968 doc=
"Filename start for plot output files",
972 starColorCuts = pexConfig.ListField(
973 doc=(
"Encoded star-color cuts (using calibration star colors). "
974 "This is a list with each entry a string of the format "
975 "``band1,band2,low,high`` such that only stars of color "
976 "low < band1 - band2 < high will be used for calibration."),
978 default=(
"NO_DATA",),
980 refStarColorCuts = pexConfig.ListField(
981 doc=(
"Encoded star color cuts specifically to apply to reference stars. "
982 "This is a list with each entry a string of the format "
983 "``band1,band2,low,high`` such that only stars of color "
984 "low < band1 - band2 < high will be used as reference stars."),
986 default=(
"NO_DATA",),
988 colorSplitBands = pexConfig.ListField(
989 doc=
"Band names to use to split stars by color. Must have 2 entries.",
994 modelMagErrors = pexConfig.Field(
995 doc=
"Should FGCM model the magnitude errors from sky/fwhm? (False means trust inputs)",
999 useQuadraticPwv = pexConfig.Field(
1000 doc=
"Model PWV with a quadratic term for variation through the night?",
1004 instrumentParsPerBand = pexConfig.Field(
1005 doc=(
"Model instrumental parameters per band? "
1006 "Otherwise, instrumental parameters (QE changes with time) are "
1007 "shared among all bands."),
1011 instrumentSlopeMinDeltaT = pexConfig.Field(
1012 doc=(
"Minimum time change (in days) between observations to use in constraining "
1013 "instrument slope."),
1017 fitMirrorChromaticity = pexConfig.Field(
1018 doc=
"Fit (intraband) mirror chromatic term?",
1022 fitCcdChromaticityDict = pexConfig.DictField(
1023 doc=
"Specification on whether to compute first-order quantum efficiency (QE) "
1024 "adjustments. Key is band, and value will be True or False. Any band "
1025 "not explicitly specified will default to False.",
1030 coatingMjds = pexConfig.ListField(
1031 doc=
"Mirror coating dates in MJD",
1035 outputStandardsBeforeFinalCycle = pexConfig.Field(
1036 doc=
"Output standard stars prior to final cycle? Used in debugging.",
1040 outputZeropointsBeforeFinalCycle = pexConfig.Field(
1041 doc=
"Output standard stars prior to final cycle? Used in debugging.",
1045 useRepeatabilityForExpGrayCutsDict = pexConfig.DictField(
1046 doc=(
"Per-band specification on whether to use star repeatability (instead of exposures) "
1047 "for computing photometric cuts. Recommended for tract mode or bands with few visits."),
1052 autoPhotometricCutNSig = pexConfig.Field(
1053 doc=(
"Number of sigma for automatic computation of (low) photometric cut. "
1054 "Cut is based on exposure gray width (per band), unless "
1055 "useRepeatabilityForExpGrayCuts is set, in which case the star "
1056 "repeatability is used (also per band)."),
1060 autoHighCutNSig = pexConfig.Field(
1061 doc=(
"Number of sigma for automatic computation of (high) outlier cut. "
1062 "Cut is based on exposure gray width (per band), unless "
1063 "useRepeatabilityForExpGrayCuts is set, in which case the star "
1064 "repeatability is used (also per band)."),
1068 quietMode = pexConfig.Field(
1069 doc=
"Be less verbose with logging.",
1073 doPlots = pexConfig.Field(
1074 doc=
"Make fgcm QA plots.",
1078 doPlotsBeforeFinalCycles = pexConfig.Field(
1079 doc=
"Make fgcm QA plots before the final two fit cycles? This only applies in"
1080 "multi-cycle mode, and if doPlots is True. These are typically the most"
1081 "important QA plots to inspect.",
1085 randomSeed = pexConfig.Field(
1086 doc=
"Random seed for fgcm for consistency in tests.",
1091 deltaAperFitMinNgoodObs = pexConfig.Field(
1092 doc=
"Minimum number of good observations to use mean delta-aper values in fits.",
1096 deltaAperFitPerCcdNx = pexConfig.Field(
1097 doc=(
"Number of x bins per ccd when computing delta-aper background offsets. "
1098 "Only used when ``doComputeDeltaAperPerCcd`` is True."),
1102 deltaAperFitPerCcdNy = pexConfig.Field(
1103 doc=(
"Number of y bins per ccd when computing delta-aper background offsets. "
1104 "Only used when ``doComputeDeltaAperPerCcd`` is True."),
1108 deltaAperFitSpatialNside = pexConfig.Field(
1109 doc=
"Healpix nside to compute spatial delta-aper background offset maps.",
1113 deltaAperInnerRadiusArcsec = pexConfig.Field(
1114 doc=(
"Inner radius used to compute deltaMagAper (arcseconds). "
1115 "Must be positive and less than ``deltaAperOuterRadiusArcsec`` if "
1116 "any of ``doComputeDeltaAperPerVisit``, ``doComputeDeltaAperPerStar``, "
1117 "``doComputeDeltaAperMap``, ``doComputeDeltaAperPerCcd`` are set."),
1121 deltaAperOuterRadiusArcsec = pexConfig.Field(
1122 doc=(
"Outer radius used to compute deltaMagAper (arcseconds). "
1123 "Must be positive and greater than ``deltaAperInnerRadiusArcsec`` if "
1124 "any of ``doComputeDeltaAperPerVisit``, ``doComputeDeltaAperPerStar``, "
1125 "``doComputeDeltaAperMap``, ``doComputeDeltaAperPerCcd`` are set."),
1129 doComputeDeltaAperPerVisit = pexConfig.Field(
1130 doc=(
"Do the computation of delta-aper background offsets per visit? "
1131 "Note: this option can be very slow when there are many visits."),
1135 doComputeDeltaAperPerStar = pexConfig.Field(
1136 doc=
"Do the computation of delta-aper mean values per star?",
1140 doComputeDeltaAperMap = pexConfig.Field(
1141 doc=(
"Do the computation of delta-aper spatial maps? "
1142 "This is only used if ``doComputeDeltaAperPerStar`` is True,"),
1146 doComputeDeltaAperPerCcd = pexConfig.Field(
1147 doc=
"Do the computation of per-ccd delta-aper background offsets?",
1155 if self.connections.previousCycleNumber != str(self.cycleNumber - 1):
1156 msg =
"cycleNumber in template must be connections.previousCycleNumber + 1"
1157 raise RuntimeError(msg)
1158 if self.connections.cycleNumber != str(self.cycleNumber):
1159 msg =
"cycleNumber in template must be equal to connections.cycleNumber"
1160 raise RuntimeError(msg)
1162 for band
in self.fitBands:
1163 if band
not in self.bands:
1164 msg =
'fitBand %s not in bands' % (band)
1165 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.fitBands, self, msg)
1166 for band
in self.requiredBands:
1167 if band
not in self.bands:
1168 msg =
'requiredBand %s not in bands' % (band)
1169 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.requiredBands, self, msg)
1170 for band
in self.colorSplitBands:
1171 if band
not in self.bands:
1172 msg =
'colorSplitBand %s not in bands' % (band)
1173 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.colorSplitBands, self, msg)
1174 for band
in self.bands:
1175 if band
not in self.superStarSubCcdDict:
1176 msg =
'band %s not in superStarSubCcdDict' % (band)
1177 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.superStarSubCcdDict,
1179 if band
not in self.ccdGraySubCcdDict:
1180 msg =
'band %s not in ccdGraySubCcdDict' % (band)
1181 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.ccdGraySubCcdDict,
1183 if band
not in self.expGrayPhotometricCutDict:
1184 msg =
'band %s not in expGrayPhotometricCutDict' % (band)
1185 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.expGrayPhotometricCutDict,
1187 if band
not in self.expGrayHighCutDict:
1188 msg =
'band %s not in expGrayHighCutDict' % (band)
1189 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.expGrayHighCutDict,
1191 if band
not in self.expVarGrayPhotometricCutDict:
1192 msg =
'band %s not in expVarGrayPhotometricCutDict' % (band)
1193 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.expVarGrayPhotometricCutDict,
1195 if band
not in self.sigFgcmMaxEGrayDict:
1196 msg =
'band %s not in sigFgcmMaxEGrayDict' % (band)
1197 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.sigFgcmMaxEGrayDict,
1199 if band
not in self.approxThroughputDict:
1200 msg =
'band %s not in approxThroughputDict' % (band)
1201 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.approxThroughputDict,
1203 if band
not in self.useRepeatabilityForExpGrayCutsDict:
1204 msg =
'band %s not in useRepeatabilityForExpGrayCutsDict' % (band)
1205 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.useRepeatabilityForExpGrayCutsDict,
1208 if self.doComputeDeltaAperPerVisit
or self.doComputeDeltaAperMap \
1209 or self.doComputeDeltaAperPerCcd:
1210 if self.deltaAperInnerRadiusArcsec <= 0.0:
1211 msg =
'deltaAperInnerRadiusArcsec must be positive if deltaAper computations are turned on.'
1212 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.deltaAperInnerRadiusArcsec,
1214 if self.deltaAperOuterRadiusArcsec <= 0.0:
1215 msg =
'deltaAperOuterRadiusArcsec must be positive if deltaAper computations are turned on.'
1216 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.deltaAperOuterRadiusArcsec,
1218 if self.deltaAperOuterRadiusArcsec <= self.deltaAperInnerRadiusArcsec:
1219 msg = (
'deltaAperOuterRadiusArcsec must be greater than deltaAperInnerRadiusArcsec if '
1220 'deltaAper computations are turned on.')
1221 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.deltaAperOuterRadiusArcsec,
1225class FgcmFitCycleTask(pipeBase.PipelineTask):
1227 Run Single fit cycle for FGCM global calibration
1230 ConfigClass = FgcmFitCycleConfig
1231 _DefaultName =
"fgcmFitCycle"
1233 def __init__(self, initInputs=None, **kwargs):
1234 super().__init__(**kwargs)
1236 self.multiCycleLoaded =
False
1238 def runQuantum(self, butlerQC, inputRefs, outputRefs):
1239 camera = butlerQC.get(inputRefs.camera)
1241 nCore = butlerQC.resources.num_cores
1245 handleDict[
'fgcmLookUpTable'] = butlerQC.get(inputRefs.fgcmLookUpTable)
1246 handleDict[
'fgcmVisitCatalog'] = butlerQC.get(inputRefs.fgcmVisitCatalog)
1248 if self.config.useParquetCatalogFormat:
1249 handleDict[
'fgcmStarObservations'] = butlerQC.get(inputRefs.fgcmStarObservationsParquet)
1250 handleDict[
'fgcmStarIds'] = butlerQC.get(inputRefs.fgcmStarIdsParquet)
1251 if self.config.doReferenceCalibration:
1252 handleDict[
'fgcmReferenceStars'] = butlerQC.get(inputRefs.fgcmReferenceStarsParquet)
1254 handleDict[
'fgcmStarObservations'] = butlerQC.get(inputRefs.fgcmStarObservations)
1255 handleDict[
'fgcmStarIds'] = butlerQC.get(inputRefs.fgcmStarIds)
1256 handleDict[
'fgcmStarIndices'] = butlerQC.get(inputRefs.fgcmStarIndices)
1257 if self.config.doReferenceCalibration:
1258 handleDict[
'fgcmReferenceStars'] = butlerQC.get(inputRefs.fgcmReferenceStars)
1259 if self.config.cycleNumber > 0:
1260 handleDict[
'fgcmFlaggedStars'] = butlerQC.get(inputRefs.fgcmFlaggedStarsInput)
1261 handleDict[
'fgcmFitParameters'] = butlerQC.get(inputRefs.fgcmFitParametersInput)
1263 fgcmDatasetDict =
None
1264 fgcmMultiCycleObjects =
None
1265 if self.config.doMultipleCycles:
1267 config = copy.copy(self.config)
1268 config.update(cycleNumber=0)
1269 for cycle
in range(self.config.multipleCyclesFinalCycleNumber + 1):
1270 if cycle == self.config.multipleCyclesFinalCycleNumber:
1271 config.update(isFinalCycle=
True)
1274 handleDict[
'fgcmFlaggedStars'] = fgcmDatasetDict[
'fgcmFlaggedStars']
1275 handleDict[
'fgcmFitParameters'] = fgcmDatasetDict[
'fgcmFitParameters']
1282 for outputRefName
in outputRefs.keys():
1283 if outputRefName.endswith(
"Plot")
and f
"Cycle{cycle}" in outputRefName:
1284 refs = getattr(outputRefs, outputRefName)
1285 if isinstance(refs, (tuple, list)):
1286 if "physical_filter" in refs[0].dimensions
and "detector" in refs[0].dimensions:
1288 physical_filter = ref.dataId[
"physical_filter"]
1289 detector = ref.dataId[
"detector"]
1290 handleDictKey = f
"{outputRefName}_{physical_filter}_{detector}"
1291 plotHandleDict[handleDictKey] = ref
1292 elif "physical_filter" in refs[0].dimensions:
1294 handleDictKey = f
"{outputRefName}_{ref.dataId['physical_filter']}"
1295 plotHandleDict[handleDictKey] = ref
1296 elif "band" in refs[0].dimensions:
1298 handleDictKey = f
"{outputRefName}_{ref.dataId['band']}"
1299 plotHandleDict[handleDictKey] = ref
1301 plotHandleDict[outputRefName] = refs
1303 fgcmDatasetDict, config, fgcmMultiCycleObjects = self._fgcmFitCycle(
1307 plotHandleDict=plotHandleDict,
1310 fgcmMultiCycleObjects=fgcmMultiCycleObjects,
1312 butlerQC.put(fgcmDatasetDict[
'fgcmFitParameters'],
1313 getattr(outputRefs, f
'fgcm_Cycle{cycle}_FitParameters'))
1314 butlerQC.put(fgcmDatasetDict[
'fgcmFlaggedStars'],
1315 getattr(outputRefs, f
'fgcm_Cycle{cycle}_FlaggedStars'))
1316 butlerQC.put(config,
1317 getattr(outputRefs, f
'fgcm_Cycle{cycle}_OutputConfig'))
1318 if self.outputZeropoints:
1319 butlerQC.put(fgcmDatasetDict[
'fgcmZeropoints'],
1320 getattr(outputRefs, f
'fgcm_Cycle{cycle}_Zeropoints'))
1321 butlerQC.put(fgcmDatasetDict[
'fgcmAtmosphereParameters'],
1322 getattr(outputRefs, f
'fgcm_Cycle{cycle}_AtmosphereParameters'))
1323 if self.outputStandards:
1324 butlerQC.put(fgcmDatasetDict[
'fgcmStandardStars'],
1325 getattr(outputRefs, f
'fgcm_Cycle{cycle}_StandardStars'))
1332 for outputRefName
in outputRefs.keys():
1333 if outputRefName.endswith(
"Plot")
and f
"Cycle{self.config.cycleNumber}" in outputRefName:
1334 refs = getattr(outputRefs, outputRefName)
1335 if isinstance(refs, (tuple, list)):
1336 if "physical_filter" in refs[0].dimensions
and "detector" in refs[0].dimensions:
1338 physical_filter = ref.dataId[
"physical_filter"]
1339 detector = ref.dataId[
"detector"]
1340 handleDictKey = f
"{outputRefName}_{physical_filter}_{detector}"
1341 plotHandleDict[handleDictKey] = ref
1342 elif "physical_filter" in refs[0].dimensions:
1344 handleDictKey = f
"{outputRefName}_{ref.dataId['physical_filter']}"
1345 plotHandleDict[handleDictKey] = ref
1346 elif "band" in refs[0].dimensions:
1348 handleDictKey = f
"{outputRefName}_{ref.dataId['band']}"
1349 plotHandleDict[handleDictKey] = ref
1351 plotHandleDict[outputRefName] = refs
1353 fgcmDatasetDict, _ = self._fgcmFitCycle(
1358 plotHandleDict=plotHandleDict,
1362 butlerQC.put(fgcmDatasetDict[
'fgcmFitParameters'], outputRefs.fgcmFitParameters)
1363 butlerQC.put(fgcmDatasetDict[
'fgcmFlaggedStars'], outputRefs.fgcmFlaggedStars)
1364 if self.outputZeropoints:
1365 butlerQC.put(fgcmDatasetDict[
'fgcmZeropoints'], outputRefs.fgcmZeropoints)
1366 butlerQC.put(fgcmDatasetDict[
'fgcmAtmosphereParameters'], outputRefs.fgcmAtmosphereParameters)
1367 if self.outputStandards:
1368 butlerQC.put(fgcmDatasetDict[
'fgcmStandardStars'], outputRefs.fgcmStandardStars)
1375 plotHandleDict=None,
1379 fgcmMultiCycleObjects=None,
1386 camera : `lsst.afw.cameraGeom.Camera`
1388 All handles are `lsst.daf.butler.DeferredDatasetHandle`
1389 handle dictionary with keys:
1391 ``"fgcmLookUpTable"``
1392 handle for the FGCM look-up table.
1393 ``"fgcmVisitCatalog"``
1394 handle for visit summary catalog.
1395 ``"fgcmStarObservations"``
1396 handle for star observation catalog.
1398 handle for star id catalog.
1399 ``"fgcmStarIndices"``
1400 handle for star index catalog.
1401 ``"fgcmReferenceStars"``
1402 handle for matched reference star catalog.
1403 ``"fgcmFlaggedStars"``
1404 handle for flagged star catalog.
1405 ``"fgcmFitParameters"``
1406 handle for fit parameter catalog.
1407 butlerQC : `lsst.pipe.base.QuantumContext`, optional
1408 Quantum context used for serializing plots.
1409 plotHandleDict : `dict` [`str`, `lsst.daf.butler.DatasetRef`], optional
1410 Dictionary of plot dataset refs, keyed by plot name.
1411 config : `lsst.pex.config.Config`, optional
1412 Configuration to use to override self.config.
1413 nCore : `int`, optional
1414 Number of cores to use during fitting.
1415 multiCycle : `bool`, optional
1416 Is this part of a multicycle run?
1417 fgcmMultiCycleObjects : `dict`, optional
1418 Dictionary of multi cycle objects from previous cycle.
1419 This should be blank on first call; subsequent calls will use
1420 the dictionary returned from the previous call. The keys include
1421 ``fgcmFitCycle`` (the primary fgcm fit object) and ``fgcmExpInfo``
1422 (information about each exposure/visit).
1426 fgcmDatasetDict : `dict`
1427 Dictionary of datasets to persist.
1428 config : `lsst.pex.config.Config`
1429 Configuration object for next cycle.
1430 fgcmMultiCycleObjects : `dict`, optional
1431 Dictionary of multi cycle objects; returned if multicycle is True.
1432 To be passed to next call of this method (see Parameters above).
1434 if config
is not None:
1437 _config = self.config
1439 if multiCycle
and not self.multiCycleLoaded:
1440 fgcmMultiCycleObjects = {}
1443 self.maxIter = _config.maxIterBeforeFinalCycle
1444 self.outputStandards = _config.outputStandardsBeforeFinalCycle
1445 self.outputZeropoints = _config.outputZeropointsBeforeFinalCycle
1446 self.resetFitParameters =
True
1448 if _config.isFinalCycle:
1453 self.outputStandards =
True
1454 self.outputZeropoints =
True
1455 self.resetFitParameters =
False
1458 doPlots = _config.doPlots
1459 if doPlots
and multiCycle:
1460 if _config.cycleNumber < (_config.multipleCyclesFinalCycleNumber - 1) \
1461 and not _config.doPlotsBeforeFinalCycles:
1464 if not self.multiCycleLoaded:
1465 lutCat = handleDict[
'fgcmLookUpTable'].get()
1466 fgcmLut, lutIndexVals, lutStd = translateFgcmLut(lutCat,
1467 dict(_config.physicalFilterMap))
1470 filterNames = lutIndexVals[0][
'FILTERNAMES']
1472 configDict = makeConfigDict(
1477 self.resetFitParameters,
1478 self.outputZeropoints,
1484 visitCat = handleDict[
'fgcmVisitCatalog'].get()
1485 fgcmExpInfo = translateVisitCatalog(visitCat)
1488 if len(camera) == fgcmLut.nCCD:
1489 useScienceDetectors =
False
1494 useScienceDetectors =
True
1496 self.log.info(
"Loading focal plane projector")
1499 self.config.defaultCameraOrientation,
1500 useScienceDetectors=useScienceDetectors,
1504 'lutIndex': lutIndexVals,
1506 'expInfo': fgcmExpInfo,
1507 'focalPlaneProjector': focalPlaneProjector,
1510 fgcmFitCycle = fgcm.FgcmFitCycle(
1513 noFitsDict=noFitsDict,
1516 plotHandleDict=plotHandleDict,
1519 if fgcmFitCycle.initialCycle:
1521 fgcmPars = fgcm.FgcmParameters.newParsWithArrays(
1522 fgcmFitCycle.fgcmConfig,
1526 plotHandleDict=plotHandleDict,
1529 if isinstance(handleDict[
'fgcmFitParameters'], afwTable.BaseCatalog):
1530 parCat = handleDict[
'fgcmFitParameters']
1532 parCat = handleDict[
'fgcmFitParameters'].get()
1533 inParInfo, inParams, inSuperStar = self._loadParameters(parCat)
1536 fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(
1537 fgcmFitCycle.fgcmConfig,
1543 plotHandleDict=plotHandleDict,
1546 fgcmStars = fgcm.FgcmStars(
1547 fgcmFitCycle.fgcmConfig,
1549 plotHandleDict=plotHandleDict,
1552 starObs = handleDict[
'fgcmStarObservations'].get()
1553 starIds = handleDict[
'fgcmStarIds'].get()
1554 if not self.config.useParquetCatalogFormat:
1555 starIndices = handleDict[
'fgcmStarIndices'].get()
1560 if 'fgcmFlaggedStars' in handleDict:
1561 if isinstance(handleDict[
'fgcmFlaggedStars'], afwTable.BaseCatalog):
1562 flaggedStars = handleDict[
'fgcmFlaggedStars']
1564 flaggedStars = handleDict[
'fgcmFlaggedStars'].get()
1565 flagId = flaggedStars[
'objId'][:]
1566 flagFlag = flaggedStars[
'objFlag'][:]
1569 elif self.config.useParquetCatalogFormat:
1575 flagged = (starIds[
'obj_flag'] > 0)
1576 flagId = starIds[
'fgcm_id'][flagged]
1577 flagFlag = starIds[
'obj_flag'][flagged]
1582 if _config.doReferenceCalibration:
1583 refStars = handleDict[
'fgcmReferenceStars'].get()
1585 refMag, refMagErr = extractReferenceMags(
1588 _config.physicalFilterMap,
1591 refId = refStars[
'fgcm_id'][:]
1601 if self.config.useParquetCatalogFormat:
1602 visitIndex = np.searchsorted(fgcmExpInfo[
'VISIT'], starObs[
'visit'])
1604 visitIndex = np.searchsorted(fgcmExpInfo[
'VISIT'], starObs[
'visit'][starIndices[
'obsIndex']])
1615 if self.config.useParquetCatalogFormat:
1618 self.log.info(
"Loading stars and such")
1619 fgcmStars.loadStars(
1621 starObs[
'visit'][:],
1622 starObs[
'detector'][:],
1625 starObs[
'inst_mag'][:],
1626 starObs[
'inst_mag_err'][:],
1627 fgcmExpInfo[
'FILTERNAME'][visitIndex],
1628 starIds[
'fgcm_id'][:],
1631 starIds[
'obs_arr_index'][:],
1632 starIds[
'n_obs'][:],
1633 obsX=starObs[
'x'][:],
1634 obsY=starObs[
'y'][:],
1635 obsDeltaMagBkg=starObs[
'delta_mag_bkg'][:],
1636 obsDeltaAper=starObs[
'delta_mag_aper'][:],
1639 refMagErr=refMagErr,
1643 objIDAlternate=starIds[
'isolated_star_id'],
1651 conv = starObs[0][
'ra'].asDegrees() / float(starObs[0][
'ra'])
1653 fgcmStars.loadStars(
1655 starObs[
'visit'][starIndices[
'obsIndex']],
1656 starObs[
'ccd'][starIndices[
'obsIndex']],
1657 starObs[
'ra'][starIndices[
'obsIndex']] * conv,
1658 starObs[
'dec'][starIndices[
'obsIndex']] * conv,
1659 starObs[
'instMag'][starIndices[
'obsIndex']],
1660 starObs[
'instMagErr'][starIndices[
'obsIndex']],
1661 fgcmExpInfo[
'FILTERNAME'][visitIndex],
1662 starIds[
'fgcm_id'][:],
1665 starIds[
'obsArrIndex'][:],
1667 obsX=starObs[
'x'][starIndices[
'obsIndex']],
1668 obsY=starObs[
'y'][starIndices[
'obsIndex']],
1669 obsDeltaMagBkg=starObs[
'deltaMagBkg'][starIndices[
'obsIndex']],
1670 obsDeltaAper=starObs[
'deltaMagAper'][starIndices[
'obsIndex']],
1671 psfCandidate=starObs[
'psf_candidate'][starIndices[
'obsIndex']],
1674 refMagErr=refMagErr,
1692 fgcmFitCycle.setLUT(fgcmLut)
1693 fgcmFitCycle.setStars(fgcmStars, fgcmPars)
1696 fgcmMultiCycleObjects[
"fgcmFitCycle"] = fgcmFitCycle
1697 fgcmMultiCycleObjects[
"fgcmExpInfo"] = fgcmExpInfo
1699 fgcmFitCycle.setPars(fgcmPars)
1700 fgcmFitCycle.finishSetup()
1703 fgcmFitCycle = fgcmMultiCycleObjects[
"fgcmFitCycle"]
1704 fgcmExpInfo = fgcmMultiCycleObjects[
"fgcmExpInfo"]
1707 self.log.info(
"Updating fgcm configuration for cycle %d", _config.cycleNumber)
1708 fgcmFitCycle.updateConfigNextCycle(
1709 _config.cycleNumber,
1710 maxIter=self.maxIter,
1711 resetParameters=self.resetFitParameters,
1712 outputStandards=self.outputStandards,
1713 outputZeropoints=self.outputZeropoints,
1714 freezeStdAtmosphere=_config.freezeStdAtmosphere,
1715 expGrayPhotometricCutDict=dict(_config.expGrayPhotometricCutDict),
1716 expGrayHighCutDict=dict(_config.expGrayHighCutDict),
1717 doPlots=_config.doPlots,
1721 if isinstance(handleDict[
'fgcmFitParameters'], afwTable.BaseCatalog):
1722 parCat = handleDict[
'fgcmFitParameters']
1724 parCat = handleDict[
'fgcmFitParameters'].get()
1725 inParInfo, inParams, inSuperStar = self._loadParameters(parCat)
1728 fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(
1729 fgcmFitCycle.fgcmConfig,
1735 plotHandleDict=plotHandleDict,
1739 fgcmFitCycle.fgcmStars.reloadStarMagnitudes()
1740 fgcmFitCycle.fgcmStars.computeAllNobs(fgcmPars)
1742 fgcmFitCycle.setPars(fgcmPars)
1743 fgcmFitCycle.finishReset(butlerQC=butlerQC, plotHandleDict=plotHandleDict)
1751 fgcmDatasetDict = self._makeFgcmOutputDatasets(fgcmFitCycle)
1756 updatedPhotometricCutDict = {b: float(fgcmFitCycle.updatedPhotometricCut[i])
for
1757 i, b
in enumerate(_config.bands)}
1758 updatedHighCutDict = {band: float(fgcmFitCycle.updatedHighCut[i])
for
1759 i, band
in enumerate(_config.bands)}
1761 outConfig = copy.copy(_config)
1762 outConfig.update(cycleNumber=(_config.cycleNumber + 1),
1763 precomputeSuperStarInitialCycle=
False,
1764 freezeStdAtmosphere=
False,
1765 expGrayPhotometricCutDict=updatedPhotometricCutDict,
1766 expGrayHighCutDict=updatedHighCutDict)
1768 outConfig.connections.update(previousCycleNumber=str(_config.cycleNumber),
1769 cycleNumber=str(_config.cycleNumber + 1))
1772 configFileName =
'%s_cycle%02d_config.py' % (outConfig.outfileBase,
1773 outConfig.cycleNumber)
1774 outConfig.save(configFileName)
1776 if _config.isFinalCycle == 1:
1778 self.log.info(
"Everything is in place to run fgcmOutputProducts.py")
1780 self.log.info(
"Saved config for next cycle to %s" % (configFileName))
1781 self.log.info(
"Be sure to look at:")
1782 self.log.info(
" config.expGrayPhotometricCut")
1783 self.log.info(
" config.expGrayHighCut")
1784 self.log.info(
"If you are satisfied with the fit, please set:")
1785 self.log.info(
" config.isFinalCycle = True")
1787 if not multiCycle
or config.isFinalCycle:
1788 fgcmFitCycle.freeSharedMemory()
1791 self.multiCycleLoaded =
True
1793 return fgcmDatasetDict, outConfig, fgcmMultiCycleObjects
1795 return fgcmDatasetDict, outConfig
1797 def _loadParameters(self, parCat):
1799 Load FGCM parameters from a previous fit cycle
1803 parCat : `lsst.afw.table.BaseCatalog`
1804 Parameter catalog in afw table form.
1808 inParInfo: `numpy.ndarray`
1809 Numpy array parameter information formatted for input to fgcm
1810 inParameters: `numpy.ndarray`
1811 Numpy array parameter values formatted for input to fgcm
1812 inSuperStar: `numpy.array`
1813 Superstar flat formatted for input to fgcm
1815 parLutFilterNames = np.array(parCat[0][
'lutFilterNames'].split(
','))
1816 parFitBands = np.array(parCat[0][
'fitBands'].split(
','))
1818 inParInfo = np.zeros(1, dtype=[(
'NCCD',
'i4'),
1819 (
'LUTFILTERNAMES', parLutFilterNames.dtype.str,
1820 (parLutFilterNames.size, )),
1821 (
'FITBANDS', parFitBands.dtype.str, (parFitBands.size, )),
1822 (
'LNTAUUNIT',
'f8'),
1823 (
'LNTAUSLOPEUNIT',
'f8'),
1824 (
'ALPHAUNIT',
'f8'),
1825 (
'LNPWVUNIT',
'f8'),
1826 (
'LNPWVSLOPEUNIT',
'f8'),
1827 (
'LNPWVQUADRATICUNIT',
'f8'),
1828 (
'LNPWVGLOBALUNIT',
'f8'),
1830 (
'QESYSUNIT',
'f8'),
1831 (
'FILTEROFFSETUNIT',
'f8'),
1832 (
'HASEXTERNALPWV',
'i2'),
1833 (
'HASEXTERNALTAU',
'i2')])
1834 inParInfo[
'NCCD'] = parCat[
'nCcd']
1835 inParInfo[
'LUTFILTERNAMES'][:] = parLutFilterNames
1836 inParInfo[
'FITBANDS'][:] = parFitBands
1837 inParInfo[
'HASEXTERNALPWV'] = parCat[
'hasExternalPwv']
1838 inParInfo[
'HASEXTERNALTAU'] = parCat[
'hasExternalTau']
1840 inParams = np.zeros(1, dtype=[(
'PARALPHA',
'f8', (parCat[
'parAlpha'].size, )),
1841 (
'PARO3',
'f8', (parCat[
'parO3'].size, )),
1842 (
'PARLNTAUINTERCEPT',
'f8',
1843 (parCat[
'parLnTauIntercept'].size, )),
1844 (
'PARLNTAUSLOPE',
'f8',
1845 (parCat[
'parLnTauSlope'].size, )),
1846 (
'PARLNPWVINTERCEPT',
'f8',
1847 (parCat[
'parLnPwvIntercept'].size, )),
1848 (
'PARLNPWVSLOPE',
'f8',
1849 (parCat[
'parLnPwvSlope'].size, )),
1850 (
'PARLNPWVQUADRATIC',
'f8',
1851 (parCat[
'parLnPwvQuadratic'].size, )),
1852 (
'PARQESYSINTERCEPT',
'f8',
1853 (parCat[
'parQeSysIntercept'].size, )),
1854 (
'COMPQESYSSLOPE',
'f8',
1855 (parCat[
'compQeSysSlope'].size, )),
1856 (
'PARFILTEROFFSET',
'f8',
1857 (parCat[
'parFilterOffset'].size, )),
1858 (
'PARFILTEROFFSETFITFLAG',
'i2',
1859 (parCat[
'parFilterOffsetFitFlag'].size, )),
1860 (
'PARRETRIEVEDLNPWVSCALE',
'f8'),
1861 (
'PARRETRIEVEDLNPWVOFFSET',
'f8'),
1862 (
'PARRETRIEVEDLNPWVNIGHTLYOFFSET',
'f8',
1863 (parCat[
'parRetrievedLnPwvNightlyOffset'].size, )),
1864 (
'COMPABSTHROUGHPUT',
'f8',
1865 (parCat[
'compAbsThroughput'].size, )),
1866 (
'COMPREFOFFSET',
'f8',
1867 (parCat[
'compRefOffset'].size, )),
1868 (
'COMPREFSIGMA',
'f8',
1869 (parCat[
'compRefSigma'].size, )),
1870 (
'COMPMIRRORCHROMATICITY',
'f8',
1871 (parCat[
'compMirrorChromaticity'].size, )),
1872 (
'MIRRORCHROMATICITYPIVOT',
'f8',
1873 (parCat[
'mirrorChromaticityPivot'].size, )),
1874 (
'COMPCCDCHROMATICITY',
'f8',
1875 (parCat[
'compCcdChromaticity'].size, )),
1876 (
'COMPMEDIANSEDSLOPE',
'f8',
1877 (parCat[
'compMedianSedSlope'].size, )),
1878 (
'COMPAPERCORRPIVOT',
'f8',
1879 (parCat[
'compAperCorrPivot'].size, )),
1880 (
'COMPAPERCORRSLOPE',
'f8',
1881 (parCat[
'compAperCorrSlope'].size, )),
1882 (
'COMPAPERCORRSLOPEERR',
'f8',
1883 (parCat[
'compAperCorrSlopeErr'].size, )),
1884 (
'COMPAPERCORRRANGE',
'f8',
1885 (parCat[
'compAperCorrRange'].size, )),
1886 (
'COMPMODELERREXPTIMEPIVOT',
'f8',
1887 (parCat[
'compModelErrExptimePivot'].size, )),
1888 (
'COMPMODELERRFWHMPIVOT',
'f8',
1889 (parCat[
'compModelErrFwhmPivot'].size, )),
1890 (
'COMPMODELERRSKYPIVOT',
'f8',
1891 (parCat[
'compModelErrSkyPivot'].size, )),
1892 (
'COMPMODELERRPARS',
'f8',
1893 (parCat[
'compModelErrPars'].size, )),
1894 (
'COMPEXPGRAY',
'f8',
1895 (parCat[
'compExpGray'].size, )),
1896 (
'COMPVARGRAY',
'f8',
1897 (parCat[
'compVarGray'].size, )),
1898 (
'COMPEXPDELTAMAGBKG',
'f8',
1899 (parCat[
'compExpDeltaMagBkg'].size, )),
1900 (
'COMPNGOODSTARPEREXP',
'i4',
1901 (parCat[
'compNGoodStarPerExp'].size, )),
1902 (
'COMPEXPREFOFFSET',
'f8',
1903 (parCat[
'compExpRefOffset'].size, )),
1904 (
'COMPSIGFGCM',
'f8',
1905 (parCat[
'compSigFgcm'].size, )),
1906 (
'COMPSIGMACAL',
'f8',
1907 (parCat[
'compSigmaCal'].size, )),
1908 (
'COMPRETRIEVEDLNPWV',
'f8',
1909 (parCat[
'compRetrievedLnPwv'].size, )),
1910 (
'COMPRETRIEVEDLNPWVRAW',
'f8',
1911 (parCat[
'compRetrievedLnPwvRaw'].size, )),
1912 (
'COMPRETRIEVEDLNPWVFLAG',
'i2',
1913 (parCat[
'compRetrievedLnPwvFlag'].size, )),
1914 (
'COMPRETRIEVEDTAUNIGHT',
'f8',
1915 (parCat[
'compRetrievedTauNight'].size, )),
1916 (
'COMPEPSILON',
'f8',
1917 (parCat[
'compEpsilon'].size, )),
1918 (
'COMPMEDDELTAAPER',
'f8',
1919 (parCat[
'compMedDeltaAper'].size, )),
1920 (
'COMPGLOBALEPSILON',
'f4',
1921 (parCat[
'compGlobalEpsilon'].size, )),
1922 (
'COMPEPSILONMAP',
'f4',
1923 (parCat[
'compEpsilonMap'].size, )),
1924 (
'COMPEPSILONNSTARMAP',
'i4',
1925 (parCat[
'compEpsilonNStarMap'].size, )),
1926 (
'COMPEPSILONCCDMAP',
'f4',
1927 (parCat[
'compEpsilonCcdMap'].size, )),
1928 (
'COMPEPSILONCCDNSTARMAP',
'i4',
1929 (parCat[
'compEpsilonCcdNStarMap'].size, ))])
1931 inParams[
'PARALPHA'][:] = parCat[
'parAlpha'][0, :]
1932 inParams[
'PARO3'][:] = parCat[
'parO3'][0, :]
1933 inParams[
'PARLNTAUINTERCEPT'][:] = parCat[
'parLnTauIntercept'][0, :]
1934 inParams[
'PARLNTAUSLOPE'][:] = parCat[
'parLnTauSlope'][0, :]
1935 inParams[
'PARLNPWVINTERCEPT'][:] = parCat[
'parLnPwvIntercept'][0, :]
1936 inParams[
'PARLNPWVSLOPE'][:] = parCat[
'parLnPwvSlope'][0, :]
1937 inParams[
'PARLNPWVQUADRATIC'][:] = parCat[
'parLnPwvQuadratic'][0, :]
1938 inParams[
'PARQESYSINTERCEPT'][:] = parCat[
'parQeSysIntercept'][0, :]
1939 inParams[
'COMPQESYSSLOPE'][:] = parCat[
'compQeSysSlope'][0, :]
1940 inParams[
'PARFILTEROFFSET'][:] = parCat[
'parFilterOffset'][0, :]
1941 inParams[
'PARFILTEROFFSETFITFLAG'][:] = parCat[
'parFilterOffsetFitFlag'][0, :]
1942 inParams[
'PARRETRIEVEDLNPWVSCALE'] = parCat[
'parRetrievedLnPwvScale']
1943 inParams[
'PARRETRIEVEDLNPWVOFFSET'] = parCat[
'parRetrievedLnPwvOffset']
1944 inParams[
'PARRETRIEVEDLNPWVNIGHTLYOFFSET'][:] = parCat[
'parRetrievedLnPwvNightlyOffset'][0, :]
1945 inParams[
'COMPABSTHROUGHPUT'][:] = parCat[
'compAbsThroughput'][0, :]
1946 inParams[
'COMPREFOFFSET'][:] = parCat[
'compRefOffset'][0, :]
1947 inParams[
'COMPREFSIGMA'][:] = parCat[
'compRefSigma'][0, :]
1948 inParams[
'COMPMIRRORCHROMATICITY'][:] = parCat[
'compMirrorChromaticity'][0, :]
1949 inParams[
'MIRRORCHROMATICITYPIVOT'][:] = parCat[
'mirrorChromaticityPivot'][0, :]
1950 inParams[
'COMPCCDCHROMATICITY'][:] = parCat[
'compCcdChromaticity'][0, :]
1951 inParams[
'COMPMEDIANSEDSLOPE'][:] = parCat[
'compMedianSedSlope'][0, :]
1952 inParams[
'COMPAPERCORRPIVOT'][:] = parCat[
'compAperCorrPivot'][0, :]
1953 inParams[
'COMPAPERCORRSLOPE'][:] = parCat[
'compAperCorrSlope'][0, :]
1954 inParams[
'COMPAPERCORRSLOPEERR'][:] = parCat[
'compAperCorrSlopeErr'][0, :]
1955 inParams[
'COMPAPERCORRRANGE'][:] = parCat[
'compAperCorrRange'][0, :]
1956 inParams[
'COMPMODELERREXPTIMEPIVOT'][:] = parCat[
'compModelErrExptimePivot'][0, :]
1957 inParams[
'COMPMODELERRFWHMPIVOT'][:] = parCat[
'compModelErrFwhmPivot'][0, :]
1958 inParams[
'COMPMODELERRSKYPIVOT'][:] = parCat[
'compModelErrSkyPivot'][0, :]
1959 inParams[
'COMPMODELERRPARS'][:] = parCat[
'compModelErrPars'][0, :]
1960 inParams[
'COMPEXPGRAY'][:] = parCat[
'compExpGray'][0, :]
1961 inParams[
'COMPVARGRAY'][:] = parCat[
'compVarGray'][0, :]
1962 inParams[
'COMPEXPDELTAMAGBKG'][:] = parCat[
'compExpDeltaMagBkg'][0, :]
1963 inParams[
'COMPNGOODSTARPEREXP'][:] = parCat[
'compNGoodStarPerExp'][0, :]
1964 inParams[
'COMPEXPREFOFFSET'][:] = parCat[
'compExpRefOffset'][0, :]
1965 inParams[
'COMPSIGFGCM'][:] = parCat[
'compSigFgcm'][0, :]
1966 inParams[
'COMPSIGMACAL'][:] = parCat[
'compSigmaCal'][0, :]
1967 inParams[
'COMPRETRIEVEDLNPWV'][:] = parCat[
'compRetrievedLnPwv'][0, :]
1968 inParams[
'COMPRETRIEVEDLNPWVRAW'][:] = parCat[
'compRetrievedLnPwvRaw'][0, :]
1969 inParams[
'COMPRETRIEVEDLNPWVFLAG'][:] = parCat[
'compRetrievedLnPwvFlag'][0, :]
1970 inParams[
'COMPRETRIEVEDTAUNIGHT'][:] = parCat[
'compRetrievedTauNight'][0, :]
1971 inParams[
'COMPEPSILON'][:] = parCat[
'compEpsilon'][0, :]
1972 inParams[
'COMPMEDDELTAAPER'][:] = parCat[
'compMedDeltaAper'][0, :]
1973 inParams[
'COMPGLOBALEPSILON'][:] = parCat[
'compGlobalEpsilon'][0, :]
1974 inParams[
'COMPEPSILONMAP'][:] = parCat[
'compEpsilonMap'][0, :]
1975 inParams[
'COMPEPSILONNSTARMAP'][:] = parCat[
'compEpsilonNStarMap'][0, :]
1976 inParams[
'COMPEPSILONCCDMAP'][:] = parCat[
'compEpsilonCcdMap'][0, :]
1977 inParams[
'COMPEPSILONCCDNSTARMAP'][:] = parCat[
'compEpsilonCcdNStarMap'][0, :]
1979 inSuperStar = np.zeros(parCat[
'superstarSize'][0, :], dtype=
'f8')
1980 inSuperStar[:, :, :, :] = parCat[
'superstar'][0, :].reshape(inSuperStar.shape)
1982 return (inParInfo, inParams, inSuperStar)
1984 def _makeFgcmOutputDatasets(self, fgcmFitCycle):
1986 Persist FGCM datasets through the butler.
1990 fgcmFitCycle: `lsst.fgcm.FgcmFitCycle`
1991 Fgcm Fit cycle object
1993 fgcmDatasetDict = {}
1996 parInfo, pars = fgcmFitCycle.fgcmPars.parsToArrays()
1998 parSchema = afwTable.Schema()
2001 lutFilterNameString = comma.join([n.decode(
'utf-8')
2002 for n
in parInfo[
'LUTFILTERNAMES'][0]])
2003 fitBandString = comma.join([n.decode(
'utf-8')
2004 for n
in parInfo[
'FITBANDS'][0]])
2006 parSchema = self._makeParSchema(parInfo, pars, fgcmFitCycle.fgcmPars.parSuperStarFlat,
2007 lutFilterNameString, fitBandString)
2008 parCat = self._makeParCatalog(parSchema, parInfo, pars,
2009 fgcmFitCycle.fgcmPars.parSuperStarFlat,
2010 lutFilterNameString, fitBandString)
2012 fgcmDatasetDict[
'fgcmFitParameters'] = parCat
2017 flagStarSchema = self._makeFlagStarSchema()
2018 flagStarStruct = fgcmFitCycle.fgcmStars.getFlagStarIndices()
2019 flagStarCat = self._makeFlagStarCat(flagStarSchema, flagStarStruct)
2021 fgcmDatasetDict[
'fgcmFlaggedStars'] = flagStarCat
2024 if self.outputZeropoints:
2025 superStarChebSize = fgcmFitCycle.fgcmZpts.zpStruct[
'FGCM_FZPT_SSTAR_CHEB'].shape[1]
2026 zptChebSize = fgcmFitCycle.fgcmZpts.zpStruct[
'FGCM_FZPT_CHEB'].shape[1]
2028 zptSchema = makeZptSchema(superStarChebSize, zptChebSize)
2029 zptCat = makeZptCat(zptSchema, fgcmFitCycle.fgcmZpts.zpStruct)
2031 fgcmDatasetDict[
'fgcmZeropoints'] = zptCat
2035 atmSchema = makeAtmSchema()
2036 atmCat = makeAtmCat(atmSchema, fgcmFitCycle.fgcmZpts.atmStruct)
2038 fgcmDatasetDict[
'fgcmAtmosphereParameters'] = atmCat
2041 if self.outputStandards:
2042 stdStruct, goodBands = fgcmFitCycle.fgcmStars.retrieveStdStarCatalog(fgcmFitCycle.fgcmPars)
2043 stdSchema = makeStdSchema(len(goodBands))
2044 stdCat = makeStdCat(stdSchema, stdStruct, goodBands)
2046 fgcmDatasetDict[
'fgcmStandardStars'] = stdCat
2048 return fgcmDatasetDict
2050 def _makeParSchema(self, parInfo, pars, parSuperStarFlat,
2051 lutFilterNameString, fitBandString):
2053 Make the parameter persistence schema
2057 parInfo: `numpy.ndarray`
2058 Parameter information returned by fgcm
2059 pars: `numpy.ndarray`
2060 Parameter values returned by fgcm
2061 parSuperStarFlat: `numpy.array`
2062 Superstar flat values returned by fgcm
2063 lutFilterNameString: `str`
2064 Combined string of all the lutFilterNames
2065 fitBandString: `str`
2066 Combined string of all the fitBands
2070 parSchema: `afwTable.schema`
2073 parSchema = afwTable.Schema()
2076 parSchema.addField(
'nCcd', type=np.int32, doc=
'Number of CCDs')
2077 parSchema.addField(
'lutFilterNames', type=str, doc=
'LUT Filter names in parameter file',
2078 size=len(lutFilterNameString))
2079 parSchema.addField(
'fitBands', type=str, doc=
'Bands that were fit',
2080 size=len(fitBandString))
2081 parSchema.addField(
'lnTauUnit', type=np.float64, doc=
'Step units for ln(AOD)')
2082 parSchema.addField(
'lnTauSlopeUnit', type=np.float64,
2083 doc=
'Step units for ln(AOD) slope')
2084 parSchema.addField(
'alphaUnit', type=np.float64, doc=
'Step units for alpha')
2085 parSchema.addField(
'lnPwvUnit', type=np.float64, doc=
'Step units for ln(pwv)')
2086 parSchema.addField(
'lnPwvSlopeUnit', type=np.float64,
2087 doc=
'Step units for ln(pwv) slope')
2088 parSchema.addField(
'lnPwvQuadraticUnit', type=np.float64,
2089 doc=
'Step units for ln(pwv) quadratic term')
2090 parSchema.addField(
'lnPwvGlobalUnit', type=np.float64,
2091 doc=
'Step units for global ln(pwv) parameters')
2092 parSchema.addField(
'o3Unit', type=np.float64, doc=
'Step units for O3')
2093 parSchema.addField(
'qeSysUnit', type=np.float64, doc=
'Step units for mirror gray')
2094 parSchema.addField(
'filterOffsetUnit', type=np.float64, doc=
'Step units for filter offset')
2095 parSchema.addField(
'hasExternalPwv', type=np.int32, doc=
'Parameters fit using external pwv')
2096 parSchema.addField(
'hasExternalTau', type=np.int32, doc=
'Parameters fit using external tau')
2099 parSchema.addField(
'parAlpha', type=
'ArrayD', doc=
'Alpha parameter vector',
2100 size=pars[
'PARALPHA'].size)
2101 parSchema.addField(
'parO3', type=
'ArrayD', doc=
'O3 parameter vector',
2102 size=pars[
'PARO3'].size)
2103 parSchema.addField(
'parLnTauIntercept', type=
'ArrayD',
2104 doc=
'ln(Tau) intercept parameter vector',
2105 size=pars[
'PARLNTAUINTERCEPT'].size)
2106 parSchema.addField(
'parLnTauSlope', type=
'ArrayD',
2107 doc=
'ln(Tau) slope parameter vector',
2108 size=pars[
'PARLNTAUSLOPE'].size)
2109 parSchema.addField(
'parLnPwvIntercept', type=
'ArrayD', doc=
'ln(pwv) intercept parameter vector',
2110 size=pars[
'PARLNPWVINTERCEPT'].size)
2111 parSchema.addField(
'parLnPwvSlope', type=
'ArrayD', doc=
'ln(pwv) slope parameter vector',
2112 size=pars[
'PARLNPWVSLOPE'].size)
2113 parSchema.addField(
'parLnPwvQuadratic', type=
'ArrayD', doc=
'ln(pwv) quadratic parameter vector',
2114 size=pars[
'PARLNPWVQUADRATIC'].size)
2115 parSchema.addField(
'parQeSysIntercept', type=
'ArrayD', doc=
'Mirror gray intercept parameter vector',
2116 size=pars[
'PARQESYSINTERCEPT'].size)
2117 parSchema.addField(
'compQeSysSlope', type=
'ArrayD', doc=
'Mirror gray slope parameter vector',
2118 size=pars[0][
'COMPQESYSSLOPE'].size)
2119 parSchema.addField(
'parFilterOffset', type=
'ArrayD', doc=
'Filter offset parameter vector',
2120 size=pars[
'PARFILTEROFFSET'].size)
2121 parSchema.addField(
'parFilterOffsetFitFlag', type=
'ArrayI', doc=
'Filter offset parameter fit flag',
2122 size=pars[
'PARFILTEROFFSETFITFLAG'].size)
2123 parSchema.addField(
'parRetrievedLnPwvScale', type=np.float64,
2124 doc=
'Global scale for retrieved ln(pwv)')
2125 parSchema.addField(
'parRetrievedLnPwvOffset', type=np.float64,
2126 doc=
'Global offset for retrieved ln(pwv)')
2127 parSchema.addField(
'parRetrievedLnPwvNightlyOffset', type=
'ArrayD',
2128 doc=
'Nightly offset for retrieved ln(pwv)',
2129 size=pars[
'PARRETRIEVEDLNPWVNIGHTLYOFFSET'].size)
2130 parSchema.addField(
'compAbsThroughput', type=
'ArrayD',
2131 doc=
'Absolute throughput (relative to transmission curves)',
2132 size=pars[
'COMPABSTHROUGHPUT'].size)
2133 parSchema.addField(
'compRefOffset', type=
'ArrayD',
2134 doc=
'Offset between reference stars and calibrated stars',
2135 size=pars[
'COMPREFOFFSET'].size)
2136 parSchema.addField(
'compRefSigma', type=
'ArrayD',
2137 doc=
'Width of reference star/calibrated star distribution',
2138 size=pars[
'COMPREFSIGMA'].size)
2139 parSchema.addField(
'compMirrorChromaticity', type=
'ArrayD',
2140 doc=
'Computed mirror chromaticity terms',
2141 size=pars[
'COMPMIRRORCHROMATICITY'].size)
2142 parSchema.addField(
'mirrorChromaticityPivot', type=
'ArrayD',
2143 doc=
'Mirror chromaticity pivot mjd',
2144 size=pars[
'MIRRORCHROMATICITYPIVOT'].size)
2145 parSchema.addField(
'compCcdChromaticity', type=
'ArrayD',
2146 doc=
'Computed CCD chromaticity terms',
2147 size=pars[
'COMPCCDCHROMATICITY'].size)
2148 parSchema.addField(
'compMedianSedSlope', type=
'ArrayD',
2149 doc=
'Computed median SED slope (per band)',
2150 size=pars[
'COMPMEDIANSEDSLOPE'].size)
2151 parSchema.addField(
'compAperCorrPivot', type=
'ArrayD', doc=
'Aperture correction pivot',
2152 size=pars[
'COMPAPERCORRPIVOT'].size)
2153 parSchema.addField(
'compAperCorrSlope', type=
'ArrayD', doc=
'Aperture correction slope',
2154 size=pars[
'COMPAPERCORRSLOPE'].size)
2155 parSchema.addField(
'compAperCorrSlopeErr', type=
'ArrayD', doc=
'Aperture correction slope error',
2156 size=pars[
'COMPAPERCORRSLOPEERR'].size)
2157 parSchema.addField(
'compAperCorrRange', type=
'ArrayD', doc=
'Aperture correction range',
2158 size=pars[
'COMPAPERCORRRANGE'].size)
2159 parSchema.addField(
'compModelErrExptimePivot', type=
'ArrayD', doc=
'Model error exptime pivot',
2160 size=pars[
'COMPMODELERREXPTIMEPIVOT'].size)
2161 parSchema.addField(
'compModelErrFwhmPivot', type=
'ArrayD', doc=
'Model error fwhm pivot',
2162 size=pars[
'COMPMODELERRFWHMPIVOT'].size)
2163 parSchema.addField(
'compModelErrSkyPivot', type=
'ArrayD', doc=
'Model error sky pivot',
2164 size=pars[
'COMPMODELERRSKYPIVOT'].size)
2165 parSchema.addField(
'compModelErrPars', type=
'ArrayD', doc=
'Model error parameters',
2166 size=pars[
'COMPMODELERRPARS'].size)
2167 parSchema.addField(
'compExpGray', type=
'ArrayD', doc=
'Computed exposure gray',
2168 size=pars[
'COMPEXPGRAY'].size)
2169 parSchema.addField(
'compVarGray', type=
'ArrayD', doc=
'Computed exposure variance',
2170 size=pars[
'COMPVARGRAY'].size)
2171 parSchema.addField(
'compExpDeltaMagBkg', type=
'ArrayD',
2172 doc=
'Computed exposure offset due to background',
2173 size=pars[
'COMPEXPDELTAMAGBKG'].size)
2174 parSchema.addField(
'compNGoodStarPerExp', type=
'ArrayI',
2175 doc=
'Computed number of good stars per exposure',
2176 size=pars[
'COMPNGOODSTARPEREXP'].size)
2177 parSchema.addField(
'compExpRefOffset', type=
'ArrayD',
2178 doc=
'Computed per-visit median offset between standard stars and ref stars.',
2179 size=pars[
'COMPEXPREFOFFSET'].size)
2180 parSchema.addField(
'compSigFgcm', type=
'ArrayD', doc=
'Computed sigma_fgcm (intrinsic repeatability)',
2181 size=pars[
'COMPSIGFGCM'].size)
2182 parSchema.addField(
'compSigmaCal', type=
'ArrayD', doc=
'Computed sigma_cal (systematic error floor)',
2183 size=pars[
'COMPSIGMACAL'].size)
2184 parSchema.addField(
'compRetrievedLnPwv', type=
'ArrayD', doc=
'Retrieved ln(pwv) (smoothed)',
2185 size=pars[
'COMPRETRIEVEDLNPWV'].size)
2186 parSchema.addField(
'compRetrievedLnPwvRaw', type=
'ArrayD', doc=
'Retrieved ln(pwv) (raw)',
2187 size=pars[
'COMPRETRIEVEDLNPWVRAW'].size)
2188 parSchema.addField(
'compRetrievedLnPwvFlag', type=
'ArrayI', doc=
'Retrieved ln(pwv) Flag',
2189 size=pars[
'COMPRETRIEVEDLNPWVFLAG'].size)
2190 parSchema.addField(
'compRetrievedTauNight', type=
'ArrayD', doc=
'Retrieved tau (per night)',
2191 size=pars[
'COMPRETRIEVEDTAUNIGHT'].size)
2192 parSchema.addField(
'compEpsilon', type=
'ArrayD',
2193 doc=
'Computed epsilon background offset per visit (nJy/arcsec2)',
2194 size=pars[
'COMPEPSILON'].size)
2195 parSchema.addField(
'compMedDeltaAper', type=
'ArrayD',
2196 doc=
'Median delta mag aper per visit',
2197 size=pars[
'COMPMEDDELTAAPER'].size)
2198 parSchema.addField(
'compGlobalEpsilon', type=
'ArrayD',
2199 doc=
'Computed epsilon bkg offset (global) (nJy/arcsec2)',
2200 size=pars[
'COMPGLOBALEPSILON'].size)
2201 parSchema.addField(
'compEpsilonMap', type=
'ArrayD',
2202 doc=
'Computed epsilon maps (nJy/arcsec2)',
2203 size=pars[
'COMPEPSILONMAP'].size)
2204 parSchema.addField(
'compEpsilonNStarMap', type=
'ArrayI',
2205 doc=
'Number of stars per pixel in computed epsilon maps',
2206 size=pars[
'COMPEPSILONNSTARMAP'].size)
2207 parSchema.addField(
'compEpsilonCcdMap', type=
'ArrayD',
2208 doc=
'Computed epsilon ccd maps (nJy/arcsec2)',
2209 size=pars[
'COMPEPSILONCCDMAP'].size)
2210 parSchema.addField(
'compEpsilonCcdNStarMap', type=
'ArrayI',
2211 doc=
'Number of stars per ccd bin in epsilon ccd maps',
2212 size=pars[
'COMPEPSILONCCDNSTARMAP'].size)
2213 parSchema.addField(
'epochMjdStart', type=
'ArrayD',
2214 doc=
'Epoch MJD start times',
2215 size=pars[
'EPOCHMJDSTART'].size)
2216 parSchema.addField(
'epochMjdEnd', type=
'ArrayD',
2217 doc=
'EpochMJD end times',
2218 size=pars[
'EPOCHMJDEND'].size)
2220 parSchema.addField(
'superstarSize', type=
'ArrayI', doc=
'Superstar matrix size',
2222 parSchema.addField(
'superstar', type=
'ArrayD', doc=
'Superstar matrix (flattened)',
2223 size=parSuperStarFlat.size)
2227 def _makeParCatalog(self, parSchema, parInfo, pars, parSuperStarFlat,
2228 lutFilterNameString, fitBandString):
2230 Make the FGCM parameter catalog for persistence
2234 parSchema: `lsst.afw.table.Schema`
2235 Parameter catalog schema
2236 pars: `numpy.ndarray`
2237 FGCM parameters to put into parCat
2238 parSuperStarFlat: `numpy.array`
2239 FGCM superstar flat array to put into parCat
2240 lutFilterNameString: `str`
2241 Combined string of all the lutFilterNames
2242 fitBandString: `str`
2243 Combined string of all the fitBands
2247 parCat: `afwTable.BasicCatalog`
2248 Atmosphere and instrumental model parameter catalog for persistence
2251 parCat = afwTable.BaseCatalog(parSchema)
2256 rec = parCat.addNew()
2259 rec[
'nCcd'] = parInfo[
'NCCD'][0]
2260 rec[
'lutFilterNames'] = lutFilterNameString
2261 rec[
'fitBands'] = fitBandString
2263 rec[
'hasExternalPwv'] = 0
2264 rec[
'hasExternalTau'] = 0
2268 scalarNames = [
'parRetrievedLnPwvScale',
'parRetrievedLnPwvOffset']
2270 arrNames = [
'parAlpha',
'parO3',
'parLnTauIntercept',
'parLnTauSlope',
2271 'parLnPwvIntercept',
'parLnPwvSlope',
'parLnPwvQuadratic',
2272 'parQeSysIntercept',
'compQeSysSlope',
2273 'parRetrievedLnPwvNightlyOffset',
'compAperCorrPivot',
2274 'parFilterOffset',
'parFilterOffsetFitFlag',
2275 'compAbsThroughput',
'compRefOffset',
'compRefSigma',
2276 'compMirrorChromaticity',
'mirrorChromaticityPivot',
'compCcdChromaticity',
2277 'compAperCorrSlope',
'compAperCorrSlopeErr',
'compAperCorrRange',
2278 'compModelErrExptimePivot',
'compModelErrFwhmPivot',
2279 'compModelErrSkyPivot',
'compModelErrPars',
2280 'compExpGray',
'compVarGray',
'compNGoodStarPerExp',
'compSigFgcm',
2281 'compSigmaCal',
'compExpDeltaMagBkg',
'compMedianSedSlope',
2282 'compRetrievedLnPwv',
'compRetrievedLnPwvRaw',
'compRetrievedLnPwvFlag',
2283 'compRetrievedTauNight',
'compEpsilon',
'compMedDeltaAper',
2284 'compGlobalEpsilon',
'compEpsilonMap',
'compEpsilonNStarMap',
2285 'compEpsilonCcdMap',
'compEpsilonCcdNStarMap',
'compExpRefOffset',
2286 'epochMjdStart',
'epochMjdEnd']
2288 for scalarName
in scalarNames:
2289 rec[scalarName] = pars[scalarName.upper()][0]
2291 for arrName
in arrNames:
2292 rec[arrName][:] = np.atleast_1d(pars[0][arrName.upper()])[:]
2295 rec[
'superstarSize'][:] = parSuperStarFlat.shape
2296 rec[
'superstar'][:] = parSuperStarFlat.ravel()
2300 def _makeFlagStarSchema(self):
2302 Make the flagged-stars schema
2306 flagStarSchema: `lsst.afw.table.Schema`
2309 flagStarSchema = afwTable.Schema()
2311 flagStarSchema.addField(
'objId', type=np.int32, doc=
'FGCM object id')
2312 flagStarSchema.addField(
'objFlag', type=np.int32, doc=
'FGCM object flag')
2314 return flagStarSchema
2316 def _makeFlagStarCat(self, flagStarSchema, flagStarStruct):
2318 Make the flagged star catalog for persistence
2322 flagStarSchema: `lsst.afw.table.Schema`
2324 flagStarStruct: `numpy.ndarray`
2325 Flagged star structure from fgcm
2329 flagStarCat: `lsst.afw.table.BaseCatalog`
2330 Flagged star catalog for persistence
2333 flagStarCat = afwTable.BaseCatalog(flagStarSchema)
2334 flagStarCat.resize(flagStarStruct.size)
2336 flagStarCat[
'objId'][:] = flagStarStruct[
'OBJID']
2337 flagStarCat[
'objFlag'][:] = flagStarStruct[
'OBJFLAG']