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 .utilities import lookupStaticCalibrations
51from .focalPlaneProjector import FocalPlaneProjector
55__all__ = ['FgcmFitCycleConfig', 'FgcmFitCycleTask']
57MULTIPLE_CYCLES_MAX = 10
60class FgcmFitCycleConnections(pipeBase.PipelineTaskConnections,
61 dimensions=("instrument",),
62 defaultTemplates={
"previousCycleNumber":
"-1",
64 camera = connectionTypes.PrerequisiteInput(
65 doc=
"Camera instrument",
67 storageClass=
"Camera",
68 dimensions=(
"instrument",),
69 lookupFunction=lookupStaticCalibrations,
73 fgcmLookUpTable = connectionTypes.PrerequisiteInput(
74 doc=(
"Atmosphere + instrument look-up-table for FGCM throughput and "
75 "chromatic corrections."),
76 name=
"fgcmLookUpTable",
77 storageClass=
"Catalog",
78 dimensions=(
"instrument",),
82 fgcmVisitCatalog = connectionTypes.Input(
83 doc=
"Catalog of visit information for fgcm",
84 name=
"fgcmVisitCatalog",
85 storageClass=
"Catalog",
86 dimensions=(
"instrument",),
90 fgcmStarObservations = connectionTypes.Input(
91 doc=
"Catalog of star observations for fgcm",
92 name=
"fgcmStarObservations",
93 storageClass=
"Catalog",
94 dimensions=(
"instrument",),
98 fgcmStarIds = connectionTypes.Input(
99 doc=
"Catalog of fgcm calibration star IDs",
101 storageClass=
"Catalog",
102 dimensions=(
"instrument",),
106 fgcmStarIndices = connectionTypes.Input(
107 doc=
"Catalog of fgcm calibration star indices",
108 name=
"fgcmStarIndices",
109 storageClass=
"Catalog",
110 dimensions=(
"instrument",),
114 fgcmReferenceStars = connectionTypes.Input(
115 doc=
"Catalog of fgcm-matched reference stars",
116 name=
"fgcmReferenceStars",
117 storageClass=
"Catalog",
118 dimensions=(
"instrument",),
122 fgcmFlaggedStarsInput = connectionTypes.PrerequisiteInput(
123 doc=
"Catalog of flagged stars for fgcm calibration from previous fit cycle",
124 name=
"fgcmFlaggedStars{previousCycleNumber}",
125 storageClass=
"Catalog",
126 dimensions=(
"instrument",),
130 fgcmFitParametersInput = connectionTypes.PrerequisiteInput(
131 doc=
"Catalog of fgcm fit parameters from previous fit cycle",
132 name=
"fgcmFitParameters{previousCycleNumber}",
133 storageClass=
"Catalog",
134 dimensions=(
"instrument",),
138 fgcmFitParameters = connectionTypes.Output(
139 doc=
"Catalog of fgcm fit parameters from current fit cycle",
140 name=
"fgcmFitParameters{cycleNumber}",
141 storageClass=
"Catalog",
142 dimensions=(
"instrument",),
145 fgcmFlaggedStars = connectionTypes.Output(
146 doc=
"Catalog of flagged stars for fgcm calibration from current fit cycle",
147 name=
"fgcmFlaggedStars{cycleNumber}",
148 storageClass=
"Catalog",
149 dimensions=(
"instrument",),
152 fgcmZeropoints = connectionTypes.Output(
153 doc=
"Catalog of fgcm zeropoint data from current fit cycle",
154 name=
"fgcmZeropoints{cycleNumber}",
155 storageClass=
"Catalog",
156 dimensions=(
"instrument",),
159 fgcmAtmosphereParameters = connectionTypes.Output(
160 doc=
"Catalog of atmospheric fit parameters from current fit cycle",
161 name=
"fgcmAtmosphereParameters{cycleNumber}",
162 storageClass=
"Catalog",
163 dimensions=(
"instrument",),
166 fgcmStandardStars = connectionTypes.Output(
167 doc=
"Catalog of standard star magnitudes from current fit cycle",
168 name=
"fgcmStandardStars{cycleNumber}",
169 storageClass=
"SimpleCatalog",
170 dimensions=(
"instrument",),
176 for cycle
in range(MULTIPLE_CYCLES_MAX):
177 vars()[f
"fgcmFitParameters{cycle}"] = connectionTypes.Output(
178 doc=f
"Catalog of fgcm fit parameters from fit cycle {cycle}",
179 name=f
"fgcmFitParameters{cycle}",
180 storageClass=
"Catalog",
181 dimensions=(
"instrument",),
183 vars()[f
"fgcmFlaggedStars{cycle}"] = connectionTypes.Output(
184 doc=f
"Catalog of flagged stars for fgcm calibration from fit cycle {cycle}",
185 name=f
"fgcmFlaggedStars{cycle}",
186 storageClass=
"Catalog",
187 dimensions=(
"instrument",),
189 vars()[f
"fgcmZeropoints{cycle}"] = connectionTypes.Output(
190 doc=f
"Catalog of fgcm zeropoint data from fit cycle {cycle}",
191 name=f
"fgcmZeropoints{cycle}",
192 storageClass=
"Catalog",
193 dimensions=(
"instrument",),
195 vars()[f
"fgcmAtmosphereParameters{cycle}"] = connectionTypes.Output(
196 doc=f
"Catalog of atmospheric fit parameters from fit cycle {cycle}",
197 name=f
"fgcmAtmosphereParameters{cycle}",
198 storageClass=
"Catalog",
199 dimensions=(
"instrument",),
201 vars()[f
"fgcmStandardStars{cycle}"] = connectionTypes.Output(
202 doc=f
"Catalog of standard star magnitudes from fit cycle {cycle}",
203 name=f
"fgcmStandardStars{cycle}",
204 storageClass=
"SimpleCatalog",
205 dimensions=(
"instrument",),
208 def __init__(self, *, config=None):
209 super().__init__(config=config)
211 if not config.doReferenceCalibration:
212 self.inputs.remove(
"fgcmReferenceStars")
214 if str(
int(config.connections.cycleNumber)) != config.connections.cycleNumber:
215 raise ValueError(
"cycleNumber must be of integer format")
216 if str(
int(config.connections.previousCycleNumber)) != config.connections.previousCycleNumber:
217 raise ValueError(
"previousCycleNumber must be of integer format")
218 if int(config.connections.previousCycleNumber) != (
int(config.connections.cycleNumber) - 1):
219 raise ValueError(
"previousCycleNumber must be 1 less than cycleNumber")
221 if int(config.connections.cycleNumber) == 0:
222 self.prerequisiteInputs.remove(
"fgcmFlaggedStarsInput")
223 self.prerequisiteInputs.remove(
"fgcmFitParametersInput")
225 if not self.config.doMultipleCycles:
227 if not self.config.isFinalCycle
and not self.config.outputStandardsBeforeFinalCycle:
228 self.outputs.remove(
"fgcmStandardStars")
230 if not self.config.isFinalCycle
and not self.config.outputZeropointsBeforeFinalCycle:
231 self.outputs.remove(
"fgcmZeropoints")
232 self.outputs.remove(
"fgcmAtmosphereParameters")
235 for cycle
in range(0, MULTIPLE_CYCLES_MAX):
236 self.outputs.remove(f
"fgcmFitParameters{cycle}")
237 self.outputs.remove(f
"fgcmFlaggedStars{cycle}")
238 self.outputs.remove(f
"fgcmZeropoints{cycle}")
239 self.outputs.remove(f
"fgcmAtmosphereParameters{cycle}")
240 self.outputs.remove(f
"fgcmStandardStars{cycle}")
245 self.outputs.remove(
"fgcmFitParameters")
246 self.outputs.remove(
"fgcmFlaggedStars")
247 self.outputs.remove(
"fgcmZeropoints")
248 self.outputs.remove(
"fgcmAtmosphereParameters")
249 self.outputs.remove(
"fgcmStandardStars")
252 for cycle
in range(self.config.multipleCyclesFinalCycleNumber + 1,
253 MULTIPLE_CYCLES_MAX):
254 self.outputs.remove(f
"fgcmFitParameters{cycle}")
255 self.outputs.remove(f
"fgcmFlaggedStars{cycle}")
256 self.outputs.remove(f
"fgcmZeropoints{cycle}")
257 self.outputs.remove(f
"fgcmAtmosphereParameters{cycle}")
258 self.outputs.remove(f
"fgcmStandardStars{cycle}")
261 for cycle
in range(self.config.multipleCyclesFinalCycleNumber):
262 if not self.config.outputZeropointsBeforeFinalCycle:
263 self.outputs.remove(f
"fgcmZeropoints{cycle}")
264 self.outputs.remove(f
"fgcmAtmosphereParameters{cycle}")
265 if not self.config.outputStandardsBeforeFinalCycle:
266 self.outputs.remove(f
"fgcmStandardStars{cycle}")
269class FgcmFitCycleConfig(pipeBase.PipelineTaskConfig,
270 pipelineConnections=FgcmFitCycleConnections):
271 """Config for FgcmFitCycle"""
273 doMultipleCycles = pexConfig.Field(
274 doc=
"Run multiple fit cycles in one task",
278 multipleCyclesFinalCycleNumber = pexConfig.RangeField(
279 doc=(
"Final cycle number in multiple cycle mode. The initial cycle "
280 "is 0, with limited parameters fit. The next cycle is 1 with "
281 "full parameter fit. The final cycle is a clean-up with no "
282 "parameters fit. There will be a total of "
283 "(multipleCycleFinalCycleNumber + 1) cycles run, and the final "
284 "cycle number cannot be less than 2."),
288 max=MULTIPLE_CYCLES_MAX,
291 bands = pexConfig.ListField(
292 doc=
"Bands to run calibration",
296 fitBands = pexConfig.ListField(
297 doc=(
"Bands to use in atmospheric fit. The bands not listed here will have "
298 "the atmosphere constrained from the 'fitBands' on the same night. "
299 "Must be a subset of `config.bands`"),
303 requiredBands = pexConfig.ListField(
304 doc=(
"Bands that are required for a star to be considered a calibration star. "
305 "Must be a subset of `config.bands`"),
309 physicalFilterMap = pexConfig.DictField(
310 doc=
"Mapping from 'physicalFilter' to band.",
315 doReferenceCalibration = pexConfig.Field(
316 doc=
"Use reference catalog as additional constraint on calibration",
320 refStarSnMin = pexConfig.Field(
321 doc=
"Reference star signal-to-noise minimum to use in calibration. Set to <=0 for no cut.",
325 refStarOutlierNSig = pexConfig.Field(
326 doc=(
"Number of sigma compared to average mag for reference star to be considered an outlier. "
327 "Computed per-band, and if it is an outlier in any band it is rejected from fits."),
331 applyRefStarColorCuts = pexConfig.Field(
332 doc=(
"Apply color cuts defined in ``starColorCuts`` to reference stars? "
333 "These cuts are in addition to any cuts defined in ``refStarColorCuts``"),
337 useExposureReferenceOffset = pexConfig.Field(
338 doc=(
"Use per-exposure (visit) offsets between calibrated stars and reference stars "
339 "for final zeropoints? This may help uniformity for disjoint surveys."),
343 nCore = pexConfig.Field(
344 doc=
"Number of cores to use",
348 nStarPerRun = pexConfig.Field(
349 doc=
"Number of stars to run in each chunk",
353 nExpPerRun = pexConfig.Field(
354 doc=
"Number of exposures to run in each chunk",
358 reserveFraction = pexConfig.Field(
359 doc=
"Fraction of stars to reserve for testing",
363 freezeStdAtmosphere = pexConfig.Field(
364 doc=
"Freeze atmosphere parameters to standard (for testing)",
368 precomputeSuperStarInitialCycle = pexConfig.Field(
369 doc=
"Precompute superstar flat for initial cycle",
373 superStarSubCcdDict = pexConfig.DictField(
374 doc=(
"Per-band specification on whether to compute superstar flat on sub-ccd scale. "
375 "Must have one entry per band."),
380 superStarSubCcdChebyshevOrder = pexConfig.Field(
381 doc=(
"Order of the 2D chebyshev polynomials for sub-ccd superstar fit. "
382 "Global default is first-order polynomials, and should be overridden "
383 "on a camera-by-camera basis depending on the ISR."),
387 superStarSubCcdTriangular = pexConfig.Field(
388 doc=(
"Should the sub-ccd superstar chebyshev matrix be triangular to "
389 "suppress high-order cross terms?"),
393 superStarSigmaClip = pexConfig.Field(
394 doc=
"Number of sigma to clip outliers when selecting for superstar flats",
398 focalPlaneSigmaClip = pexConfig.Field(
399 doc=
"Number of sigma to clip outliers per focal-plane.",
403 ccdGraySubCcdDict = pexConfig.DictField(
404 doc=(
"Per-band specification on whether to compute achromatic per-ccd residual "
405 "('ccd gray') on a sub-ccd scale."),
410 ccdGraySubCcdChebyshevOrder = pexConfig.Field(
411 doc=
"Order of the 2D chebyshev polynomials for sub-ccd gray fit.",
415 ccdGraySubCcdTriangular = pexConfig.Field(
416 doc=(
"Should the sub-ccd gray chebyshev matrix be triangular to "
417 "suppress high-order cross terms?"),
421 ccdGrayFocalPlaneDict = pexConfig.DictField(
422 doc=(
"Per-band specification on whether to compute focal-plane residual "
423 "('ccd gray') corrections."),
428 ccdGrayFocalPlaneFitMinCcd = pexConfig.Field(
429 doc=(
"Minimum number of 'good' CCDs required to perform focal-plane "
430 "gray corrections. If there are fewer good CCDs then the gray "
431 "correction is computed per-ccd."),
435 ccdGrayFocalPlaneChebyshevOrder = pexConfig.Field(
436 doc=
"Order of the 2D chebyshev polynomials for focal plane fit.",
440 cycleNumber = pexConfig.Field(
441 doc=(
"FGCM fit cycle number. This is automatically incremented after each run "
442 "and stage of outlier rejection. See cookbook for details."),
446 isFinalCycle = pexConfig.Field(
447 doc=(
"Is this the final cycle of the fitting? Will automatically compute final "
448 "selection of stars and photometric exposures, and will output zeropoints "
449 "and standard stars for use in fgcmOutputProducts"),
453 maxIterBeforeFinalCycle = pexConfig.Field(
454 doc=(
"Maximum fit iterations, prior to final cycle. The number of iterations "
455 "will always be 0 in the final cycle for cleanup and final selection."),
459 deltaMagBkgOffsetPercentile = pexConfig.Field(
460 doc=(
"Percentile brightest stars on a visit/ccd to use to compute net "
461 "offset from local background subtraction."),
465 deltaMagBkgPerCcd = pexConfig.Field(
466 doc=(
"Compute net offset from local background subtraction per-ccd? "
467 "Otherwise, use computation per visit."),
471 utBoundary = pexConfig.Field(
472 doc=
"Boundary (in UTC) from day-to-day",
476 washMjds = pexConfig.ListField(
477 doc=
"Mirror wash MJDs",
481 epochMjds = pexConfig.ListField(
482 doc=
"Epoch boundaries in MJD",
486 minObsPerBand = pexConfig.Field(
487 doc=
"Minimum good observations per band",
493 latitude = pexConfig.Field(
494 doc=
"Observatory latitude",
498 defaultCameraOrientation = pexConfig.Field(
499 doc=
"Default camera orientation for QA plots.",
503 brightObsGrayMax = pexConfig.Field(
504 doc=
"Maximum gray extinction to be considered bright observation",
508 minStarPerCcd = pexConfig.Field(
509 doc=(
"Minimum number of good stars per CCD to be used in calibration fit. "
510 "CCDs with fewer stars will have their calibration estimated from other "
511 "CCDs in the same visit, with zeropoint error increased accordingly."),
515 minCcdPerExp = pexConfig.Field(
516 doc=(
"Minimum number of good CCDs per exposure/visit to be used in calibration fit. "
517 "Visits with fewer good CCDs will have CCD zeropoints estimated where possible."),
521 maxCcdGrayErr = pexConfig.Field(
522 doc=
"Maximum error on CCD gray offset to be considered photometric",
526 minStarPerExp = pexConfig.Field(
527 doc=(
"Minimum number of good stars per exposure/visit to be used in calibration fit. "
528 "Visits with fewer good stars will have CCD zeropoints estimated where possible."),
532 minExpPerNight = pexConfig.Field(
533 doc=
"Minimum number of good exposures/visits to consider a partly photometric night",
537 expGrayInitialCut = pexConfig.Field(
538 doc=(
"Maximum exposure/visit gray value for initial selection of possible photometric "
543 expGrayPhotometricCutDict = pexConfig.DictField(
544 doc=(
"Per-band specification on maximum (negative) achromatic exposure residual "
545 "('gray term') for a visit to be considered photometric. Must have one "
546 "entry per band. Broad-band filters should be -0.05."),
551 expGrayHighCutDict = pexConfig.DictField(
552 doc=(
"Per-band specification on maximum (positive) achromatic exposure residual "
553 "('gray term') for a visit to be considered photometric. Must have one "
554 "entry per band. Broad-band filters should be 0.2."),
559 expGrayRecoverCut = pexConfig.Field(
560 doc=(
"Maximum (negative) exposure gray to be able to recover bad ccds via interpolation. "
561 "Visits with more gray extinction will only get CCD zeropoints if there are "
562 "sufficient star observations (minStarPerCcd) on that CCD."),
566 expVarGrayPhotometricCutDict = pexConfig.DictField(
567 doc=(
"Per-band specification on maximum exposure variance to be considered possibly "
568 "photometric. Must have one entry per band. Broad-band filters should be "
574 expGrayErrRecoverCut = pexConfig.Field(
575 doc=(
"Maximum exposure gray error to be able to recover bad ccds via interpolation. "
576 "Visits with more gray variance will only get CCD zeropoints if there are "
577 "sufficient star observations (minStarPerCcd) on that CCD."),
581 aperCorrFitNBins = pexConfig.Field(
582 doc=(
"Number of aperture bins used in aperture correction fit. When set to 0"
583 "no fit will be performed, and the config.aperCorrInputSlopes will be "
584 "used if available."),
588 aperCorrInputSlopeDict = pexConfig.DictField(
589 doc=(
"Per-band specification of aperture correction input slope parameters. These "
590 "are used on the first fit iteration, and aperture correction parameters will "
591 "be updated from the data if config.aperCorrFitNBins > 0. It is recommended "
592 "to set this when there is insufficient data to fit the parameters (e.g. "
598 sedboundaryterms = pexConfig.ConfigField(
599 doc=
"Mapping from bands to SED boundary term names used is sedterms.",
600 dtype=SedboundarytermDict,
602 sedterms = pexConfig.ConfigField(
603 doc=
"Mapping from terms to bands for fgcm linear SED approximations.",
606 sigFgcmMaxErr = pexConfig.Field(
607 doc=
"Maximum mag error for fitting sigma_FGCM",
611 sigFgcmMaxEGrayDict = pexConfig.DictField(
612 doc=(
"Per-band specification for maximum (absolute) achromatic residual (gray value) "
613 "for observations in sigma_fgcm (raw repeatability). Broad-band filters "
619 ccdGrayMaxStarErr = pexConfig.Field(
620 doc=(
"Maximum error on a star observation to use in ccd gray (achromatic residual) "
625 approxThroughputDict = pexConfig.DictField(
626 doc=(
"Per-band specification of the approximate overall throughput at the start of "
627 "calibration observations. Must have one entry per band. Typically should "
633 sigmaCalRange = pexConfig.ListField(
634 doc=
"Allowed range for systematic error floor estimation",
636 default=(0.001, 0.003),
638 sigmaCalFitPercentile = pexConfig.ListField(
639 doc=
"Magnitude percentile range to fit systematic error floor",
641 default=(0.05, 0.15),
643 sigmaCalPlotPercentile = pexConfig.ListField(
644 doc=
"Magnitude percentile range to plot systematic error floor",
646 default=(0.05, 0.95),
648 sigma0Phot = pexConfig.Field(
649 doc=
"Systematic error floor for all zeropoints",
653 mapLongitudeRef = pexConfig.Field(
654 doc=
"Reference longitude for plotting maps",
658 mapNSide = pexConfig.Field(
659 doc=
"Healpix nside for plotting maps",
663 outfileBase = pexConfig.Field(
664 doc=
"Filename start for plot output files",
668 starColorCuts = pexConfig.ListField(
669 doc=(
"Encoded star-color cuts (using calibration star colors). "
670 "This is a list with each entry a string of the format "
671 "``band1,band2,low,high`` such that only stars of color "
672 "low < band1 - band2 < high will be used for calibration."),
674 default=(
"NO_DATA",),
676 refStarColorCuts = pexConfig.ListField(
677 doc=(
"Encoded star color cuts specifically to apply to reference stars. "
678 "This is a list with each entry a string of the format "
679 "``band1,band2,low,high`` such that only stars of color "
680 "low < band1 - band2 < high will be used as reference stars."),
682 default=(
"NO_DATA",),
684 colorSplitBands = pexConfig.ListField(
685 doc=
"Band names to use to split stars by color. Must have 2 entries.",
690 modelMagErrors = pexConfig.Field(
691 doc=
"Should FGCM model the magnitude errors from sky/fwhm? (False means trust inputs)",
695 useQuadraticPwv = pexConfig.Field(
696 doc=
"Model PWV with a quadratic term for variation through the night?",
700 instrumentParsPerBand = pexConfig.Field(
701 doc=(
"Model instrumental parameters per band? "
702 "Otherwise, instrumental parameters (QE changes with time) are "
703 "shared among all bands."),
707 instrumentSlopeMinDeltaT = pexConfig.Field(
708 doc=(
"Minimum time change (in days) between observations to use in constraining "
709 "instrument slope."),
713 fitMirrorChromaticity = pexConfig.Field(
714 doc=
"Fit (intraband) mirror chromatic term?",
718 coatingMjds = pexConfig.ListField(
719 doc=
"Mirror coating dates in MJD",
723 outputStandardsBeforeFinalCycle = pexConfig.Field(
724 doc=
"Output standard stars prior to final cycle? Used in debugging.",
728 outputZeropointsBeforeFinalCycle = pexConfig.Field(
729 doc=
"Output standard stars prior to final cycle? Used in debugging.",
733 useRepeatabilityForExpGrayCutsDict = pexConfig.DictField(
734 doc=(
"Per-band specification on whether to use star repeatability (instead of exposures) "
735 "for computing photometric cuts. Recommended for tract mode or bands with few visits."),
740 autoPhotometricCutNSig = pexConfig.Field(
741 doc=(
"Number of sigma for automatic computation of (low) photometric cut. "
742 "Cut is based on exposure gray width (per band), unless "
743 "useRepeatabilityForExpGrayCuts is set, in which case the star "
744 "repeatability is used (also per band)."),
748 autoHighCutNSig = pexConfig.Field(
749 doc=(
"Number of sigma for automatic computation of (high) outlier cut. "
750 "Cut is based on exposure gray width (per band), unless "
751 "useRepeatabilityForExpGrayCuts is set, in which case the star "
752 "repeatability is used (also per band)."),
756 quietMode = pexConfig.Field(
757 doc=
"Be less verbose with logging.",
761 doPlots = pexConfig.Field(
762 doc=
"Make fgcm QA plots.",
766 randomSeed = pexConfig.Field(
767 doc=
"Random seed for fgcm for consistency in tests.",
772 deltaAperFitMinNgoodObs = pexConfig.Field(
773 doc=
"Minimum number of good observations to use mean delta-aper values in fits.",
777 deltaAperFitPerCcdNx = pexConfig.Field(
778 doc=(
"Number of x bins per ccd when computing delta-aper background offsets. "
779 "Only used when ``doComputeDeltaAperPerCcd`` is True."),
783 deltaAperFitPerCcdNy = pexConfig.Field(
784 doc=(
"Number of y bins per ccd when computing delta-aper background offsets. "
785 "Only used when ``doComputeDeltaAperPerCcd`` is True."),
789 deltaAperFitSpatialNside = pexConfig.Field(
790 doc=
"Healpix nside to compute spatial delta-aper background offset maps.",
794 deltaAperInnerRadiusArcsec = pexConfig.Field(
795 doc=(
"Inner radius used to compute deltaMagAper (arcseconds). "
796 "Must be positive and less than ``deltaAperOuterRadiusArcsec`` if "
797 "any of ``doComputeDeltaAperPerVisit``, ``doComputeDeltaAperPerStar``, "
798 "``doComputeDeltaAperMap``, ``doComputeDeltaAperPerCcd`` are set."),
802 deltaAperOuterRadiusArcsec = pexConfig.Field(
803 doc=(
"Outer radius used to compute deltaMagAper (arcseconds). "
804 "Must be positive and greater than ``deltaAperInnerRadiusArcsec`` if "
805 "any of ``doComputeDeltaAperPerVisit``, ``doComputeDeltaAperPerStar``, "
806 "``doComputeDeltaAperMap``, ``doComputeDeltaAperPerCcd`` are set."),
810 doComputeDeltaAperPerVisit = pexConfig.Field(
811 doc=(
"Do the computation of delta-aper background offsets per visit? "
812 "Note: this option can be very slow when there are many visits."),
816 doComputeDeltaAperPerStar = pexConfig.Field(
817 doc=
"Do the computation of delta-aper mean values per star?",
821 doComputeDeltaAperMap = pexConfig.Field(
822 doc=(
"Do the computation of delta-aper spatial maps? "
823 "This is only used if ``doComputeDeltaAperPerStar`` is True,"),
827 doComputeDeltaAperPerCcd = pexConfig.Field(
828 doc=
"Do the computation of per-ccd delta-aper background offsets?",
836 if self.connections.previousCycleNumber !=
str(self.cycleNumber - 1):
837 msg =
"cycleNumber in template must be connections.previousCycleNumber + 1"
838 raise RuntimeError(msg)
839 if self.connections.cycleNumber !=
str(self.cycleNumber):
840 msg =
"cycleNumber in template must be equal to connections.cycleNumber"
841 raise RuntimeError(msg)
843 for band
in self.fitBands:
844 if band
not in self.bands:
845 msg =
'fitBand %s not in bands' % (band)
846 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.fitBands, self, msg)
847 for band
in self.requiredBands:
848 if band
not in self.bands:
849 msg =
'requiredBand %s not in bands' % (band)
850 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.requiredBands, self, msg)
851 for band
in self.colorSplitBands:
852 if band
not in self.bands:
853 msg =
'colorSplitBand %s not in bands' % (band)
854 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.colorSplitBands, self, msg)
855 for band
in self.bands:
856 if band
not in self.superStarSubCcdDict:
857 msg =
'band %s not in superStarSubCcdDict' % (band)
858 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.superStarSubCcdDict,
860 if band
not in self.ccdGraySubCcdDict:
861 msg =
'band %s not in ccdGraySubCcdDict' % (band)
862 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.ccdGraySubCcdDict,
864 if band
not in self.expGrayPhotometricCutDict:
865 msg =
'band %s not in expGrayPhotometricCutDict' % (band)
866 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.expGrayPhotometricCutDict,
868 if band
not in self.expGrayHighCutDict:
869 msg =
'band %s not in expGrayHighCutDict' % (band)
870 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.expGrayHighCutDict,
872 if band
not in self.expVarGrayPhotometricCutDict:
873 msg =
'band %s not in expVarGrayPhotometricCutDict' % (band)
874 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.expVarGrayPhotometricCutDict,
876 if band
not in self.sigFgcmMaxEGrayDict:
877 msg =
'band %s not in sigFgcmMaxEGrayDict' % (band)
878 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.sigFgcmMaxEGrayDict,
880 if band
not in self.approxThroughputDict:
881 msg =
'band %s not in approxThroughputDict' % (band)
882 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.approxThroughputDict,
884 if band
not in self.useRepeatabilityForExpGrayCutsDict:
885 msg =
'band %s not in useRepeatabilityForExpGrayCutsDict' % (band)
886 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.useRepeatabilityForExpGrayCutsDict,
889 if self.doComputeDeltaAperPerVisit
or self.doComputeDeltaAperMap \
890 or self.doComputeDeltaAperPerCcd:
891 if self.deltaAperInnerRadiusArcsec <= 0.0:
892 msg =
'deltaAperInnerRadiusArcsec must be positive if deltaAper computations are turned on.'
893 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.deltaAperInnerRadiusArcsec,
895 if self.deltaAperOuterRadiusArcsec <= 0.0:
896 msg =
'deltaAperOuterRadiusArcsec must be positive if deltaAper computations are turned on.'
897 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.deltaAperOuterRadiusArcsec,
899 if self.deltaAperOuterRadiusArcsec <= self.deltaAperInnerRadiusArcsec:
900 msg = (
'deltaAperOuterRadiusArcsec must be greater than deltaAperInnerRadiusArcsec if '
901 'deltaAper computations are turned on.')
902 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.deltaAperOuterRadiusArcsec,
906class FgcmFitCycleTask(pipeBase.PipelineTask):
908 Run Single fit cycle for FGCM
global calibration
911 ConfigClass = FgcmFitCycleConfig
912 _DefaultName = "fgcmFitCycle"
914 def __init__(self, initInputs=None, **kwargs):
915 super().__init__(**kwargs)
917 def runQuantum(self, butlerQC, inputRefs, outputRefs):
918 camera = butlerQC.get(inputRefs.camera)
922 handleDict[
'fgcmLookUpTable'] = butlerQC.get(inputRefs.fgcmLookUpTable)
923 handleDict[
'fgcmVisitCatalog'] = butlerQC.get(inputRefs.fgcmVisitCatalog)
924 handleDict[
'fgcmStarObservations'] = butlerQC.get(inputRefs.fgcmStarObservations)
925 handleDict[
'fgcmStarIds'] = butlerQC.get(inputRefs.fgcmStarIds)
926 handleDict[
'fgcmStarIndices'] = butlerQC.get(inputRefs.fgcmStarIndices)
927 if self.config.doReferenceCalibration:
928 handleDict[
'fgcmReferenceStars'] = butlerQC.get(inputRefs.fgcmReferenceStars)
929 if self.config.cycleNumber > 0:
930 handleDict[
'fgcmFlaggedStars'] = butlerQC.get(inputRefs.fgcmFlaggedStarsInput)
931 handleDict[
'fgcmFitParameters'] = butlerQC.get(inputRefs.fgcmFitParametersInput)
933 fgcmDatasetDict =
None
934 if self.config.doMultipleCycles:
936 config = copy.copy(self.config)
937 config.update(cycleNumber=0)
938 for cycle
in range(self.config.multipleCyclesFinalCycleNumber + 1):
939 if cycle == self.config.multipleCyclesFinalCycleNumber:
940 config.update(isFinalCycle=
True)
943 handleDict[
'fgcmFlaggedStars'] = fgcmDatasetDict[
'fgcmFlaggedStars']
944 handleDict[
'fgcmFitParameters'] = fgcmDatasetDict[
'fgcmFitParameters']
946 fgcmDatasetDict, config = self._fgcmFitCycle(camera, handleDict, config=config)
947 butlerQC.put(fgcmDatasetDict[
'fgcmFitParameters'],
948 getattr(outputRefs, f
'fgcmFitParameters{cycle}'))
949 butlerQC.put(fgcmDatasetDict[
'fgcmFlaggedStars'],
950 getattr(outputRefs, f
'fgcmFlaggedStars{cycle}'))
951 if self.outputZeropoints:
952 butlerQC.put(fgcmDatasetDict[
'fgcmZeropoints'],
953 getattr(outputRefs, f
'fgcmZeropoints{cycle}'))
954 butlerQC.put(fgcmDatasetDict[
'fgcmAtmosphereParameters'],
955 getattr(outputRefs, f
'fgcmAtmosphereParameters{cycle}'))
956 if self.outputStandards:
957 butlerQC.put(fgcmDatasetDict[
'fgcmStandardStars'],
958 getattr(outputRefs, f
'fgcmStandardStars{cycle}'))
961 fgcmDatasetDict, _ = self._fgcmFitCycle(camera, handleDict)
963 butlerQC.put(fgcmDatasetDict[
'fgcmFitParameters'], outputRefs.fgcmFitParameters)
964 butlerQC.put(fgcmDatasetDict[
'fgcmFlaggedStars'], outputRefs.fgcmFlaggedStars)
965 if self.outputZeropoints:
966 butlerQC.put(fgcmDatasetDict[
'fgcmZeropoints'], outputRefs.fgcmZeropoints)
967 butlerQC.put(fgcmDatasetDict[
'fgcmAtmosphereParameters'], outputRefs.fgcmAtmosphereParameters)
968 if self.outputStandards:
969 butlerQC.put(fgcmDatasetDict[
'fgcmStandardStars'], outputRefs.fgcmStandardStars)
971 def _fgcmFitCycle(self, camera, handleDict, config=None):
979 All handles are `lsst.daf.butler.DeferredDatasetHandle`
980 handle dictionary with keys:
982 ``
"fgcmLookUpTable"``
983 handle
for the FGCM look-up table.
984 ``
"fgcmVisitCatalog"``
985 handle
for visit summary catalog.
986 ``
"fgcmStarObservations"``
987 handle
for star observation catalog.
989 handle
for star id catalog.
990 ``
"fgcmStarIndices"``
991 handle
for star index catalog.
992 ``
"fgcmReferenceStars"``
993 handle
for matched reference star catalog.
994 ``
"fgcmFlaggedStars"``
995 handle
for flagged star catalog.
996 ``
"fgcmFitParameters"``
997 handle
for fit parameter catalog.
999 Configuration to use to override self.config.
1003 fgcmDatasetDict : `dict`
1004 Dictionary of datasets to persist.
1006 if config
is not None:
1009 _config = self.config
1012 self.maxIter = _config.maxIterBeforeFinalCycle
1013 self.outputStandards = _config.outputStandardsBeforeFinalCycle
1014 self.outputZeropoints = _config.outputZeropointsBeforeFinalCycle
1015 self.resetFitParameters =
True
1017 if _config.isFinalCycle:
1022 self.outputStandards =
True
1023 self.outputZeropoints =
True
1024 self.resetFitParameters =
False
1026 lutCat = handleDict[
'fgcmLookUpTable'].get()
1028 dict(_config.physicalFilterMap))
1032 self.maxIter, self.resetFitParameters,
1033 self.outputZeropoints,
1034 lutIndexVals[0][
'FILTERNAMES'])
1037 visitCat = handleDict[
'fgcmVisitCatalog'].get()
1042 self.config.defaultCameraOrientation)
1044 noFitsDict = {
'lutIndex': lutIndexVals,
1046 'expInfo': fgcmExpInfo,
1047 'focalPlaneProjector': focalPlaneProjector}
1050 fgcmFitCycle = fgcm.FgcmFitCycle(configDict, useFits=
False,
1051 noFitsDict=noFitsDict, noOutput=
True)
1054 if (fgcmFitCycle.initialCycle):
1056 fgcmPars = fgcm.FgcmParameters.newParsWithArrays(fgcmFitCycle.fgcmConfig,
1061 parCat = handleDict[
'fgcmFitParameters']
1063 parCat = handleDict[
'fgcmFitParameters'].get()
1064 inParInfo, inParams, inSuperStar = self._loadParameters(parCat)
1066 fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(fgcmFitCycle.fgcmConfig,
1073 fgcmStars = fgcm.FgcmStars(fgcmFitCycle.fgcmConfig)
1075 starObs = handleDict[
'fgcmStarObservations'].get()
1076 starIds = handleDict[
'fgcmStarIds'].get()
1077 starIndices = handleDict[
'fgcmStarIndices'].get()
1080 if 'fgcmFlaggedStars' in handleDict:
1082 flaggedStars = handleDict[
'fgcmFlaggedStars']
1084 flaggedStars = handleDict[
'fgcmFlaggedStars'].get()
1085 flagId = flaggedStars[
'objId'][:]
1086 flagFlag = flaggedStars[
'objFlag'][:]
1092 if _config.doReferenceCalibration:
1093 refStars = handleDict[
'fgcmReferenceStars'].get()
1097 _config.physicalFilterMap)
1098 refId = refStars[
'fgcm_id'][:]
1108 visitIndex = np.searchsorted(fgcmExpInfo[
'VISIT'], starObs[
'visit'][starIndices[
'obsIndex']])
1120 conv = starObs[0][
'ra'].asDegrees() /
float(starObs[0][
'ra'])
1122 fgcmStars.loadStars(fgcmPars,
1123 starObs[
'visit'][starIndices[
'obsIndex']],
1124 starObs[
'ccd'][starIndices[
'obsIndex']],
1125 starObs[
'ra'][starIndices[
'obsIndex']] * conv,
1126 starObs[
'dec'][starIndices[
'obsIndex']] * conv,
1127 starObs[
'instMag'][starIndices[
'obsIndex']],
1128 starObs[
'instMagErr'][starIndices[
'obsIndex']],
1129 fgcmExpInfo[
'FILTERNAME'][visitIndex],
1130 starIds[
'fgcm_id'][:],
1133 starIds[
'obsArrIndex'][:],
1135 obsX=starObs[
'x'][starIndices[
'obsIndex']],
1136 obsY=starObs[
'y'][starIndices[
'obsIndex']],
1137 obsDeltaMagBkg=starObs[
'deltaMagBkg'][starIndices[
'obsIndex']],
1138 obsDeltaAper=starObs[
'deltaMagAper'][starIndices[
'obsIndex']],
1139 psfCandidate=starObs[
'psf_candidate'][starIndices[
'obsIndex']],
1142 refMagErr=refMagErr,
1160 fgcmFitCycle.setLUT(fgcmLut)
1161 fgcmFitCycle.setStars(fgcmStars, fgcmPars)
1162 fgcmFitCycle.setPars(fgcmPars)
1165 fgcmFitCycle.finishSetup()
1174 fgcmDatasetDict = self._makeFgcmOutputDatasets(fgcmFitCycle)
1179 updatedPhotometricCutDict = {b:
float(fgcmFitCycle.updatedPhotometricCut[i])
for
1180 i, b
in enumerate(_config.bands)}
1181 updatedHighCutDict = {band:
float(fgcmFitCycle.updatedHighCut[i])
for
1182 i, band
in enumerate(_config.bands)}
1184 outConfig = copy.copy(_config)
1185 outConfig.update(cycleNumber=(_config.cycleNumber + 1),
1186 precomputeSuperStarInitialCycle=
False,
1187 freezeStdAtmosphere=
False,
1188 expGrayPhotometricCutDict=updatedPhotometricCutDict,
1189 expGrayHighCutDict=updatedHighCutDict)
1191 outConfig.connections.update(previousCycleNumber=
str(_config.cycleNumber),
1192 cycleNumber=
str(_config.cycleNumber + 1))
1194 configFileName =
'%s_cycle%02d_config.py' % (outConfig.outfileBase,
1195 outConfig.cycleNumber)
1196 outConfig.save(configFileName)
1198 if _config.isFinalCycle == 1:
1200 self.log.
info(
"Everything is in place to run fgcmOutputProducts.py")
1202 self.log.
info(
"Saved config for next cycle to %s" % (configFileName))
1203 self.log.
info(
"Be sure to look at:")
1204 self.log.
info(
" config.expGrayPhotometricCut")
1205 self.log.
info(
" config.expGrayHighCut")
1206 self.log.
info(
"If you are satisfied with the fit, please set:")
1207 self.log.
info(
" config.isFinalCycle = True")
1209 fgcmFitCycle.freeSharedMemory()
1211 return fgcmDatasetDict, outConfig
1213 def _loadParameters(self, parCat):
1215 Load FGCM parameters from a previous fit cycle
1220 Parameter catalog
in afw table form.
1224 inParInfo: `numpy.ndarray`
1225 Numpy array parameter information formatted
for input to fgcm
1226 inParameters: `numpy.ndarray`
1227 Numpy array parameter values formatted
for input to fgcm
1228 inSuperStar: `numpy.array`
1229 Superstar flat formatted
for input to fgcm
1231 parLutFilterNames = np.array(parCat[0]['lutFilterNames'].split(
','))
1232 parFitBands = np.array(parCat[0][
'fitBands'].split(
','))
1234 inParInfo = np.zeros(1, dtype=[(
'NCCD',
'i4'),
1235 (
'LUTFILTERNAMES', parLutFilterNames.dtype.str,
1236 (parLutFilterNames.size, )),
1237 (
'FITBANDS', parFitBands.dtype.str, (parFitBands.size, )),
1238 (
'LNTAUUNIT',
'f8'),
1239 (
'LNTAUSLOPEUNIT',
'f8'),
1240 (
'ALPHAUNIT',
'f8'),
1241 (
'LNPWVUNIT',
'f8'),
1242 (
'LNPWVSLOPEUNIT',
'f8'),
1243 (
'LNPWVQUADRATICUNIT',
'f8'),
1244 (
'LNPWVGLOBALUNIT',
'f8'),
1246 (
'QESYSUNIT',
'f8'),
1247 (
'FILTEROFFSETUNIT',
'f8'),
1248 (
'HASEXTERNALPWV',
'i2'),
1249 (
'HASEXTERNALTAU',
'i2')])
1250 inParInfo[
'NCCD'] = parCat[
'nCcd']
1251 inParInfo[
'LUTFILTERNAMES'][:] = parLutFilterNames
1252 inParInfo[
'FITBANDS'][:] = parFitBands
1253 inParInfo[
'HASEXTERNALPWV'] = parCat[
'hasExternalPwv']
1254 inParInfo[
'HASEXTERNALTAU'] = parCat[
'hasExternalTau']
1256 inParams = np.zeros(1, dtype=[(
'PARALPHA',
'f8', (parCat[
'parAlpha'].size, )),
1257 (
'PARO3',
'f8', (parCat[
'parO3'].size, )),
1258 (
'PARLNTAUINTERCEPT',
'f8',
1259 (parCat[
'parLnTauIntercept'].size, )),
1260 (
'PARLNTAUSLOPE',
'f8',
1261 (parCat[
'parLnTauSlope'].size, )),
1262 (
'PARLNPWVINTERCEPT',
'f8',
1263 (parCat[
'parLnPwvIntercept'].size, )),
1264 (
'PARLNPWVSLOPE',
'f8',
1265 (parCat[
'parLnPwvSlope'].size, )),
1266 (
'PARLNPWVQUADRATIC',
'f8',
1267 (parCat[
'parLnPwvQuadratic'].size, )),
1268 (
'PARQESYSINTERCEPT',
'f8',
1269 (parCat[
'parQeSysIntercept'].size, )),
1270 (
'COMPQESYSSLOPE',
'f8',
1271 (parCat[
'compQeSysSlope'].size, )),
1272 (
'PARFILTEROFFSET',
'f8',
1273 (parCat[
'parFilterOffset'].size, )),
1274 (
'PARFILTEROFFSETFITFLAG',
'i2',
1275 (parCat[
'parFilterOffsetFitFlag'].size, )),
1276 (
'PARRETRIEVEDLNPWVSCALE',
'f8'),
1277 (
'PARRETRIEVEDLNPWVOFFSET',
'f8'),
1278 (
'PARRETRIEVEDLNPWVNIGHTLYOFFSET',
'f8',
1279 (parCat[
'parRetrievedLnPwvNightlyOffset'].size, )),
1280 (
'COMPABSTHROUGHPUT',
'f8',
1281 (parCat[
'compAbsThroughput'].size, )),
1282 (
'COMPREFOFFSET',
'f8',
1283 (parCat[
'compRefOffset'].size, )),
1284 (
'COMPREFSIGMA',
'f8',
1285 (parCat[
'compRefSigma'].size, )),
1286 (
'COMPMIRRORCHROMATICITY',
'f8',
1287 (parCat[
'compMirrorChromaticity'].size, )),
1288 (
'MIRRORCHROMATICITYPIVOT',
'f8',
1289 (parCat[
'mirrorChromaticityPivot'].size, )),
1290 (
'COMPMEDIANSEDSLOPE',
'f8',
1291 (parCat[
'compMedianSedSlope'].size, )),
1292 (
'COMPAPERCORRPIVOT',
'f8',
1293 (parCat[
'compAperCorrPivot'].size, )),
1294 (
'COMPAPERCORRSLOPE',
'f8',
1295 (parCat[
'compAperCorrSlope'].size, )),
1296 (
'COMPAPERCORRSLOPEERR',
'f8',
1297 (parCat[
'compAperCorrSlopeErr'].size, )),
1298 (
'COMPAPERCORRRANGE',
'f8',
1299 (parCat[
'compAperCorrRange'].size, )),
1300 (
'COMPMODELERREXPTIMEPIVOT',
'f8',
1301 (parCat[
'compModelErrExptimePivot'].size, )),
1302 (
'COMPMODELERRFWHMPIVOT',
'f8',
1303 (parCat[
'compModelErrFwhmPivot'].size, )),
1304 (
'COMPMODELERRSKYPIVOT',
'f8',
1305 (parCat[
'compModelErrSkyPivot'].size, )),
1306 (
'COMPMODELERRPARS',
'f8',
1307 (parCat[
'compModelErrPars'].size, )),
1308 (
'COMPEXPGRAY',
'f8',
1309 (parCat[
'compExpGray'].size, )),
1310 (
'COMPVARGRAY',
'f8',
1311 (parCat[
'compVarGray'].size, )),
1312 (
'COMPEXPDELTAMAGBKG',
'f8',
1313 (parCat[
'compExpDeltaMagBkg'].size, )),
1314 (
'COMPNGOODSTARPEREXP',
'i4',
1315 (parCat[
'compNGoodStarPerExp'].size, )),
1316 (
'COMPEXPREFOFFSET',
'f8',
1317 (parCat[
'compExpRefOffset'].size, )),
1318 (
'COMPSIGFGCM',
'f8',
1319 (parCat[
'compSigFgcm'].size, )),
1320 (
'COMPSIGMACAL',
'f8',
1321 (parCat[
'compSigmaCal'].size, )),
1322 (
'COMPRETRIEVEDLNPWV',
'f8',
1323 (parCat[
'compRetrievedLnPwv'].size, )),
1324 (
'COMPRETRIEVEDLNPWVRAW',
'f8',
1325 (parCat[
'compRetrievedLnPwvRaw'].size, )),
1326 (
'COMPRETRIEVEDLNPWVFLAG',
'i2',
1327 (parCat[
'compRetrievedLnPwvFlag'].size, )),
1328 (
'COMPRETRIEVEDTAUNIGHT',
'f8',
1329 (parCat[
'compRetrievedTauNight'].size, )),
1330 (
'COMPEPSILON',
'f8',
1331 (parCat[
'compEpsilon'].size, )),
1332 (
'COMPMEDDELTAAPER',
'f8',
1333 (parCat[
'compMedDeltaAper'].size, )),
1334 (
'COMPGLOBALEPSILON',
'f4',
1335 (parCat[
'compGlobalEpsilon'].size, )),
1336 (
'COMPEPSILONMAP',
'f4',
1337 (parCat[
'compEpsilonMap'].size, )),
1338 (
'COMPEPSILONNSTARMAP',
'i4',
1339 (parCat[
'compEpsilonNStarMap'].size, )),
1340 (
'COMPEPSILONCCDMAP',
'f4',
1341 (parCat[
'compEpsilonCcdMap'].size, )),
1342 (
'COMPEPSILONCCDNSTARMAP',
'i4',
1343 (parCat[
'compEpsilonCcdNStarMap'].size, ))])
1345 inParams[
'PARALPHA'][:] = parCat[
'parAlpha'][0, :]
1346 inParams[
'PARO3'][:] = parCat[
'parO3'][0, :]
1347 inParams[
'PARLNTAUINTERCEPT'][:] = parCat[
'parLnTauIntercept'][0, :]
1348 inParams[
'PARLNTAUSLOPE'][:] = parCat[
'parLnTauSlope'][0, :]
1349 inParams[
'PARLNPWVINTERCEPT'][:] = parCat[
'parLnPwvIntercept'][0, :]
1350 inParams[
'PARLNPWVSLOPE'][:] = parCat[
'parLnPwvSlope'][0, :]
1351 inParams[
'PARLNPWVQUADRATIC'][:] = parCat[
'parLnPwvQuadratic'][0, :]
1352 inParams[
'PARQESYSINTERCEPT'][:] = parCat[
'parQeSysIntercept'][0, :]
1353 inParams[
'COMPQESYSSLOPE'][:] = parCat[
'compQeSysSlope'][0, :]
1354 inParams[
'PARFILTEROFFSET'][:] = parCat[
'parFilterOffset'][0, :]
1355 inParams[
'PARFILTEROFFSETFITFLAG'][:] = parCat[
'parFilterOffsetFitFlag'][0, :]
1356 inParams[
'PARRETRIEVEDLNPWVSCALE'] = parCat[
'parRetrievedLnPwvScale']
1357 inParams[
'PARRETRIEVEDLNPWVOFFSET'] = parCat[
'parRetrievedLnPwvOffset']
1358 inParams[
'PARRETRIEVEDLNPWVNIGHTLYOFFSET'][:] = parCat[
'parRetrievedLnPwvNightlyOffset'][0, :]
1359 inParams[
'COMPABSTHROUGHPUT'][:] = parCat[
'compAbsThroughput'][0, :]
1360 inParams[
'COMPREFOFFSET'][:] = parCat[
'compRefOffset'][0, :]
1361 inParams[
'COMPREFSIGMA'][:] = parCat[
'compRefSigma'][0, :]
1362 inParams[
'COMPMIRRORCHROMATICITY'][:] = parCat[
'compMirrorChromaticity'][0, :]
1363 inParams[
'MIRRORCHROMATICITYPIVOT'][:] = parCat[
'mirrorChromaticityPivot'][0, :]
1364 inParams[
'COMPMEDIANSEDSLOPE'][:] = parCat[
'compMedianSedSlope'][0, :]
1365 inParams[
'COMPAPERCORRPIVOT'][:] = parCat[
'compAperCorrPivot'][0, :]
1366 inParams[
'COMPAPERCORRSLOPE'][:] = parCat[
'compAperCorrSlope'][0, :]
1367 inParams[
'COMPAPERCORRSLOPEERR'][:] = parCat[
'compAperCorrSlopeErr'][0, :]
1368 inParams[
'COMPAPERCORRRANGE'][:] = parCat[
'compAperCorrRange'][0, :]
1369 inParams[
'COMPMODELERREXPTIMEPIVOT'][:] = parCat[
'compModelErrExptimePivot'][0, :]
1370 inParams[
'COMPMODELERRFWHMPIVOT'][:] = parCat[
'compModelErrFwhmPivot'][0, :]
1371 inParams[
'COMPMODELERRSKYPIVOT'][:] = parCat[
'compModelErrSkyPivot'][0, :]
1372 inParams[
'COMPMODELERRPARS'][:] = parCat[
'compModelErrPars'][0, :]
1373 inParams[
'COMPEXPGRAY'][:] = parCat[
'compExpGray'][0, :]
1374 inParams[
'COMPVARGRAY'][:] = parCat[
'compVarGray'][0, :]
1375 inParams[
'COMPEXPDELTAMAGBKG'][:] = parCat[
'compExpDeltaMagBkg'][0, :]
1376 inParams[
'COMPNGOODSTARPEREXP'][:] = parCat[
'compNGoodStarPerExp'][0, :]
1377 inParams[
'COMPEXPREFOFFSET'][:] = parCat[
'compExpRefOffset'][0, :]
1378 inParams[
'COMPSIGFGCM'][:] = parCat[
'compSigFgcm'][0, :]
1379 inParams[
'COMPSIGMACAL'][:] = parCat[
'compSigmaCal'][0, :]
1380 inParams[
'COMPRETRIEVEDLNPWV'][:] = parCat[
'compRetrievedLnPwv'][0, :]
1381 inParams[
'COMPRETRIEVEDLNPWVRAW'][:] = parCat[
'compRetrievedLnPwvRaw'][0, :]
1382 inParams[
'COMPRETRIEVEDLNPWVFLAG'][:] = parCat[
'compRetrievedLnPwvFlag'][0, :]
1383 inParams[
'COMPRETRIEVEDTAUNIGHT'][:] = parCat[
'compRetrievedTauNight'][0, :]
1384 inParams[
'COMPEPSILON'][:] = parCat[
'compEpsilon'][0, :]
1385 inParams[
'COMPMEDDELTAAPER'][:] = parCat[
'compMedDeltaAper'][0, :]
1386 inParams[
'COMPGLOBALEPSILON'][:] = parCat[
'compGlobalEpsilon'][0, :]
1387 inParams[
'COMPEPSILONMAP'][:] = parCat[
'compEpsilonMap'][0, :]
1388 inParams[
'COMPEPSILONNSTARMAP'][:] = parCat[
'compEpsilonNStarMap'][0, :]
1389 inParams[
'COMPEPSILONCCDMAP'][:] = parCat[
'compEpsilonCcdMap'][0, :]
1390 inParams[
'COMPEPSILONCCDNSTARMAP'][:] = parCat[
'compEpsilonCcdNStarMap'][0, :]
1392 inSuperStar = np.zeros(parCat[
'superstarSize'][0, :], dtype=
'f8')
1393 inSuperStar[:, :, :, :] = parCat[
'superstar'][0, :].reshape(inSuperStar.shape)
1395 return (inParInfo, inParams, inSuperStar)
1397 def _makeFgcmOutputDatasets(self, fgcmFitCycle):
1399 Persist FGCM datasets through the butler.
1403 fgcmFitCycle: `lsst.fgcm.FgcmFitCycle`
1404 Fgcm Fit cycle object
1406 fgcmDatasetDict = {}
1409 parInfo, pars = fgcmFitCycle.fgcmPars.parsToArrays()
1414 lutFilterNameString = comma.join([n.decode(
'utf-8')
1415 for n
in parInfo[
'LUTFILTERNAMES'][0]])
1416 fitBandString = comma.join([n.decode(
'utf-8')
1417 for n
in parInfo[
'FITBANDS'][0]])
1419 parSchema = self._makeParSchema(parInfo, pars, fgcmFitCycle.fgcmPars.parSuperStarFlat,
1420 lutFilterNameString, fitBandString)
1421 parCat = self._makeParCatalog(parSchema, parInfo, pars,
1422 fgcmFitCycle.fgcmPars.parSuperStarFlat,
1423 lutFilterNameString, fitBandString)
1425 fgcmDatasetDict[
'fgcmFitParameters'] = parCat
1430 flagStarSchema = self._makeFlagStarSchema()
1431 flagStarStruct = fgcmFitCycle.fgcmStars.getFlagStarIndices()
1432 flagStarCat = self._makeFlagStarCat(flagStarSchema, flagStarStruct)
1434 fgcmDatasetDict[
'fgcmFlaggedStars'] = flagStarCat
1437 if self.outputZeropoints:
1438 superStarChebSize = fgcmFitCycle.fgcmZpts.zpStruct[
'FGCM_FZPT_SSTAR_CHEB'].shape[1]
1439 zptChebSize = fgcmFitCycle.fgcmZpts.zpStruct[
'FGCM_FZPT_CHEB'].shape[1]
1442 zptCat =
makeZptCat(zptSchema, fgcmFitCycle.fgcmZpts.zpStruct)
1444 fgcmDatasetDict[
'fgcmZeropoints'] = zptCat
1449 atmCat =
makeAtmCat(atmSchema, fgcmFitCycle.fgcmZpts.atmStruct)
1451 fgcmDatasetDict[
'fgcmAtmosphereParameters'] = atmCat
1454 if self.outputStandards:
1455 stdStruct, goodBands = fgcmFitCycle.fgcmStars.retrieveStdStarCatalog(fgcmFitCycle.fgcmPars)
1457 stdCat =
makeStdCat(stdSchema, stdStruct, goodBands)
1459 fgcmDatasetDict[
'fgcmStandardStars'] = stdCat
1461 return fgcmDatasetDict
1463 def _makeParSchema(self, parInfo, pars, parSuperStarFlat,
1464 lutFilterNameString, fitBandString):
1466 Make the parameter persistence schema
1470 parInfo: `numpy.ndarray`
1471 Parameter information returned by fgcm
1472 pars: `numpy.ndarray`
1473 Parameter values returned by fgcm
1474 parSuperStarFlat: `numpy.array`
1475 Superstar flat values returned by fgcm
1476 lutFilterNameString: `str`
1477 Combined string of all the lutFilterNames
1478 fitBandString: `str`
1479 Combined string of all the fitBands
1483 parSchema: `afwTable.schema`
1489 parSchema.addField(
'nCcd', type=np.int32, doc=
'Number of CCDs')
1490 parSchema.addField(
'lutFilterNames', type=str, doc=
'LUT Filter names in parameter file',
1491 size=len(lutFilterNameString))
1492 parSchema.addField(
'fitBands', type=str, doc=
'Bands that were fit',
1493 size=len(fitBandString))
1494 parSchema.addField(
'lnTauUnit', type=np.float64, doc=
'Step units for ln(AOD)')
1495 parSchema.addField(
'lnTauSlopeUnit', type=np.float64,
1496 doc=
'Step units for ln(AOD) slope')
1497 parSchema.addField(
'alphaUnit', type=np.float64, doc=
'Step units for alpha')
1498 parSchema.addField(
'lnPwvUnit', type=np.float64, doc=
'Step units for ln(pwv)')
1499 parSchema.addField(
'lnPwvSlopeUnit', type=np.float64,
1500 doc=
'Step units for ln(pwv) slope')
1501 parSchema.addField(
'lnPwvQuadraticUnit', type=np.float64,
1502 doc=
'Step units for ln(pwv) quadratic term')
1503 parSchema.addField(
'lnPwvGlobalUnit', type=np.float64,
1504 doc=
'Step units for global ln(pwv) parameters')
1505 parSchema.addField(
'o3Unit', type=np.float64, doc=
'Step units for O3')
1506 parSchema.addField(
'qeSysUnit', type=np.float64, doc=
'Step units for mirror gray')
1507 parSchema.addField(
'filterOffsetUnit', type=np.float64, doc=
'Step units for filter offset')
1508 parSchema.addField(
'hasExternalPwv', type=np.int32, doc=
'Parameters fit using external pwv')
1509 parSchema.addField(
'hasExternalTau', type=np.int32, doc=
'Parameters fit using external tau')
1512 parSchema.addField(
'parAlpha', type=
'ArrayD', doc=
'Alpha parameter vector',
1513 size=pars[
'PARALPHA'].size)
1514 parSchema.addField(
'parO3', type=
'ArrayD', doc=
'O3 parameter vector',
1515 size=pars[
'PARO3'].size)
1516 parSchema.addField(
'parLnTauIntercept', type=
'ArrayD',
1517 doc=
'ln(Tau) intercept parameter vector',
1518 size=pars[
'PARLNTAUINTERCEPT'].size)
1519 parSchema.addField(
'parLnTauSlope', type=
'ArrayD',
1520 doc=
'ln(Tau) slope parameter vector',
1521 size=pars[
'PARLNTAUSLOPE'].size)
1522 parSchema.addField(
'parLnPwvIntercept', type=
'ArrayD', doc=
'ln(pwv) intercept parameter vector',
1523 size=pars[
'PARLNPWVINTERCEPT'].size)
1524 parSchema.addField(
'parLnPwvSlope', type=
'ArrayD', doc=
'ln(pwv) slope parameter vector',
1525 size=pars[
'PARLNPWVSLOPE'].size)
1526 parSchema.addField(
'parLnPwvQuadratic', type=
'ArrayD', doc=
'ln(pwv) quadratic parameter vector',
1527 size=pars[
'PARLNPWVQUADRATIC'].size)
1528 parSchema.addField(
'parQeSysIntercept', type=
'ArrayD', doc=
'Mirror gray intercept parameter vector',
1529 size=pars[
'PARQESYSINTERCEPT'].size)
1530 parSchema.addField(
'compQeSysSlope', type=
'ArrayD', doc=
'Mirror gray slope parameter vector',
1531 size=pars[0][
'COMPQESYSSLOPE'].size)
1532 parSchema.addField(
'parFilterOffset', type=
'ArrayD', doc=
'Filter offset parameter vector',
1533 size=pars[
'PARFILTEROFFSET'].size)
1534 parSchema.addField(
'parFilterOffsetFitFlag', type=
'ArrayI', doc=
'Filter offset parameter fit flag',
1535 size=pars[
'PARFILTEROFFSETFITFLAG'].size)
1536 parSchema.addField(
'parRetrievedLnPwvScale', type=np.float64,
1537 doc=
'Global scale for retrieved ln(pwv)')
1538 parSchema.addField(
'parRetrievedLnPwvOffset', type=np.float64,
1539 doc=
'Global offset for retrieved ln(pwv)')
1540 parSchema.addField(
'parRetrievedLnPwvNightlyOffset', type=
'ArrayD',
1541 doc=
'Nightly offset for retrieved ln(pwv)',
1542 size=pars[
'PARRETRIEVEDLNPWVNIGHTLYOFFSET'].size)
1543 parSchema.addField(
'compAbsThroughput', type=
'ArrayD',
1544 doc=
'Absolute throughput (relative to transmission curves)',
1545 size=pars[
'COMPABSTHROUGHPUT'].size)
1546 parSchema.addField(
'compRefOffset', type=
'ArrayD',
1547 doc=
'Offset between reference stars and calibrated stars',
1548 size=pars[
'COMPREFOFFSET'].size)
1549 parSchema.addField(
'compRefSigma', type=
'ArrayD',
1550 doc=
'Width of reference star/calibrated star distribution',
1551 size=pars[
'COMPREFSIGMA'].size)
1552 parSchema.addField(
'compMirrorChromaticity', type=
'ArrayD',
1553 doc=
'Computed mirror chromaticity terms',
1554 size=pars[
'COMPMIRRORCHROMATICITY'].size)
1555 parSchema.addField(
'mirrorChromaticityPivot', type=
'ArrayD',
1556 doc=
'Mirror chromaticity pivot mjd',
1557 size=pars[
'MIRRORCHROMATICITYPIVOT'].size)
1558 parSchema.addField(
'compMedianSedSlope', type=
'ArrayD',
1559 doc=
'Computed median SED slope (per band)',
1560 size=pars[
'COMPMEDIANSEDSLOPE'].size)
1561 parSchema.addField(
'compAperCorrPivot', type=
'ArrayD', doc=
'Aperture correction pivot',
1562 size=pars[
'COMPAPERCORRPIVOT'].size)
1563 parSchema.addField(
'compAperCorrSlope', type=
'ArrayD', doc=
'Aperture correction slope',
1564 size=pars[
'COMPAPERCORRSLOPE'].size)
1565 parSchema.addField(
'compAperCorrSlopeErr', type=
'ArrayD', doc=
'Aperture correction slope error',
1566 size=pars[
'COMPAPERCORRSLOPEERR'].size)
1567 parSchema.addField(
'compAperCorrRange', type=
'ArrayD', doc=
'Aperture correction range',
1568 size=pars[
'COMPAPERCORRRANGE'].size)
1569 parSchema.addField(
'compModelErrExptimePivot', type=
'ArrayD', doc=
'Model error exptime pivot',
1570 size=pars[
'COMPMODELERREXPTIMEPIVOT'].size)
1571 parSchema.addField(
'compModelErrFwhmPivot', type=
'ArrayD', doc=
'Model error fwhm pivot',
1572 size=pars[
'COMPMODELERRFWHMPIVOT'].size)
1573 parSchema.addField(
'compModelErrSkyPivot', type=
'ArrayD', doc=
'Model error sky pivot',
1574 size=pars[
'COMPMODELERRSKYPIVOT'].size)
1575 parSchema.addField(
'compModelErrPars', type=
'ArrayD', doc=
'Model error parameters',
1576 size=pars[
'COMPMODELERRPARS'].size)
1577 parSchema.addField(
'compExpGray', type=
'ArrayD', doc=
'Computed exposure gray',
1578 size=pars[
'COMPEXPGRAY'].size)
1579 parSchema.addField(
'compVarGray', type=
'ArrayD', doc=
'Computed exposure variance',
1580 size=pars[
'COMPVARGRAY'].size)
1581 parSchema.addField(
'compExpDeltaMagBkg', type=
'ArrayD',
1582 doc=
'Computed exposure offset due to background',
1583 size=pars[
'COMPEXPDELTAMAGBKG'].size)
1584 parSchema.addField(
'compNGoodStarPerExp', type=
'ArrayI',
1585 doc=
'Computed number of good stars per exposure',
1586 size=pars[
'COMPNGOODSTARPEREXP'].size)
1587 parSchema.addField(
'compExpRefOffset', type=
'ArrayD',
1588 doc=
'Computed per-visit median offset between standard stars and ref stars.',
1589 size=pars[
'COMPEXPREFOFFSET'].size)
1590 parSchema.addField(
'compSigFgcm', type=
'ArrayD', doc=
'Computed sigma_fgcm (intrinsic repeatability)',
1591 size=pars[
'COMPSIGFGCM'].size)
1592 parSchema.addField(
'compSigmaCal', type=
'ArrayD', doc=
'Computed sigma_cal (systematic error floor)',
1593 size=pars[
'COMPSIGMACAL'].size)
1594 parSchema.addField(
'compRetrievedLnPwv', type=
'ArrayD', doc=
'Retrieved ln(pwv) (smoothed)',
1595 size=pars[
'COMPRETRIEVEDLNPWV'].size)
1596 parSchema.addField(
'compRetrievedLnPwvRaw', type=
'ArrayD', doc=
'Retrieved ln(pwv) (raw)',
1597 size=pars[
'COMPRETRIEVEDLNPWVRAW'].size)
1598 parSchema.addField(
'compRetrievedLnPwvFlag', type=
'ArrayI', doc=
'Retrieved ln(pwv) Flag',
1599 size=pars[
'COMPRETRIEVEDLNPWVFLAG'].size)
1600 parSchema.addField(
'compRetrievedTauNight', type=
'ArrayD', doc=
'Retrieved tau (per night)',
1601 size=pars[
'COMPRETRIEVEDTAUNIGHT'].size)
1602 parSchema.addField(
'compEpsilon', type=
'ArrayD',
1603 doc=
'Computed epsilon background offset per visit (nJy/arcsec2)',
1604 size=pars[
'COMPEPSILON'].size)
1605 parSchema.addField(
'compMedDeltaAper', type=
'ArrayD',
1606 doc=
'Median delta mag aper per visit',
1607 size=pars[
'COMPMEDDELTAAPER'].size)
1608 parSchema.addField(
'compGlobalEpsilon', type=
'ArrayD',
1609 doc=
'Computed epsilon bkg offset (global) (nJy/arcsec2)',
1610 size=pars[
'COMPGLOBALEPSILON'].size)
1611 parSchema.addField(
'compEpsilonMap', type=
'ArrayD',
1612 doc=
'Computed epsilon maps (nJy/arcsec2)',
1613 size=pars[
'COMPEPSILONMAP'].size)
1614 parSchema.addField(
'compEpsilonNStarMap', type=
'ArrayI',
1615 doc=
'Number of stars per pixel in computed epsilon maps',
1616 size=pars[
'COMPEPSILONNSTARMAP'].size)
1617 parSchema.addField(
'compEpsilonCcdMap', type=
'ArrayD',
1618 doc=
'Computed epsilon ccd maps (nJy/arcsec2)',
1619 size=pars[
'COMPEPSILONCCDMAP'].size)
1620 parSchema.addField(
'compEpsilonCcdNStarMap', type=
'ArrayI',
1621 doc=
'Number of stars per ccd bin in epsilon ccd maps',
1622 size=pars[
'COMPEPSILONCCDNSTARMAP'].size)
1624 parSchema.addField(
'superstarSize', type=
'ArrayI', doc=
'Superstar matrix size',
1626 parSchema.addField(
'superstar', type=
'ArrayD', doc=
'Superstar matrix (flattened)',
1627 size=parSuperStarFlat.size)
1631 def _makeParCatalog(self, parSchema, parInfo, pars, parSuperStarFlat,
1632 lutFilterNameString, fitBandString):
1634 Make the FGCM parameter catalog for persistence
1639 Parameter catalog schema
1640 pars: `numpy.ndarray`
1641 FGCM parameters to put into parCat
1642 parSuperStarFlat: `numpy.array`
1643 FGCM superstar flat array to put into parCat
1644 lutFilterNameString: `str`
1645 Combined string of all the lutFilterNames
1646 fitBandString: `str`
1647 Combined string of all the fitBands
1651 parCat: `afwTable.BasicCatalog`
1652 Atmosphere
and instrumental model parameter catalog
for persistence
1660 rec = parCat.addNew()
1663 rec[
'nCcd'] = parInfo[
'NCCD']
1664 rec[
'lutFilterNames'] = lutFilterNameString
1665 rec[
'fitBands'] = fitBandString
1667 rec[
'hasExternalPwv'] = 0
1668 rec[
'hasExternalTau'] = 0
1672 scalarNames = [
'parRetrievedLnPwvScale',
'parRetrievedLnPwvOffset']
1674 arrNames = [
'parAlpha',
'parO3',
'parLnTauIntercept',
'parLnTauSlope',
1675 'parLnPwvIntercept',
'parLnPwvSlope',
'parLnPwvQuadratic',
1676 'parQeSysIntercept',
'compQeSysSlope',
1677 'parRetrievedLnPwvNightlyOffset',
'compAperCorrPivot',
1678 'parFilterOffset',
'parFilterOffsetFitFlag',
1679 'compAbsThroughput',
'compRefOffset',
'compRefSigma',
1680 'compMirrorChromaticity',
'mirrorChromaticityPivot',
1681 'compAperCorrSlope',
'compAperCorrSlopeErr',
'compAperCorrRange',
1682 'compModelErrExptimePivot',
'compModelErrFwhmPivot',
1683 'compModelErrSkyPivot',
'compModelErrPars',
1684 'compExpGray',
'compVarGray',
'compNGoodStarPerExp',
'compSigFgcm',
1685 'compSigmaCal',
'compExpDeltaMagBkg',
'compMedianSedSlope',
1686 'compRetrievedLnPwv',
'compRetrievedLnPwvRaw',
'compRetrievedLnPwvFlag',
1687 'compRetrievedTauNight',
'compEpsilon',
'compMedDeltaAper',
1688 'compGlobalEpsilon',
'compEpsilonMap',
'compEpsilonNStarMap',
1689 'compEpsilonCcdMap',
'compEpsilonCcdNStarMap',
'compExpRefOffset']
1691 for scalarName
in scalarNames:
1692 rec[scalarName] = pars[scalarName.upper()]
1694 for arrName
in arrNames:
1695 rec[arrName][:] = np.atleast_1d(pars[0][arrName.upper()])[:]
1698 rec[
'superstarSize'][:] = parSuperStarFlat.shape
1699 rec[
'superstar'][:] = parSuperStarFlat.ravel()
1703 def _makeFlagStarSchema(self):
1705 Make the flagged-stars schema
1714 flagStarSchema.addField('objId', type=np.int32, doc=
'FGCM object id')
1715 flagStarSchema.addField(
'objFlag', type=np.int32, doc=
'FGCM object flag')
1717 return flagStarSchema
1719 def _makeFlagStarCat(self, flagStarSchema, flagStarStruct):
1721 Make the flagged star catalog for persistence
1727 flagStarStruct: `numpy.ndarray`
1728 Flagged star structure
from fgcm
1733 Flagged star catalog
for persistence
1737 flagStarCat.resize(flagStarStruct.size)
1739 flagStarCat['objId'][:] = flagStarStruct[
'OBJID']
1740 flagStarCat[
'objFlag'][:] = flagStarStruct[
'OBJFLAG']
An immutable representation of a camera.
Defines the fields and offsets for a table.
def extractReferenceMags(refStars, bands, filterMap)
def makeStdSchema(nBands)
def makeAtmCat(atmSchema, atmStruct)
def makeConfigDict(config, log, camera, maxIter, resetFitParameters, outputZeropoints, lutFilterNames, tract=None)
def translateFgcmLut(lutCat, physicalFilterMap)
def makeZptCat(zptSchema, zpStruct)
def makeStdCat(stdSchema, stdStruct, goodBands)
def makeZptSchema(superStarChebyshevSize, zptChebyshevSize)
def translateVisitCatalog(visitCat)