23 """Make the final fgcmcal output products. 25 This task takes the final output from fgcmFitCycle and produces the following 26 outputs for use in the DM stack: the FGCM standard stars in a reference 27 catalog format; the model atmospheres in "transmission_atmosphere_fgcm" 28 format; and the zeropoints in "fgcm_photoCalib" format. Optionally, the 29 task can transfer the 'absolute' calibration from a reference catalog 30 to put the fgcm standard stars in units of Jansky. This is accomplished 31 by matching stars in a sample of healpix pixels, and applying the median 42 from astropy
import units
44 import lsst.pex.config
as pexConfig
45 import lsst.pipe.base
as pipeBase
46 from lsst.afw.image
import TransmissionCurve
47 from lsst.meas.algorithms
import LoadIndexedReferenceObjectsTask
48 from lsst.pipe.tasks.photoCal
import PhotoCalTask
50 import lsst.afw.image
as afwImage
51 import lsst.afw.math
as afwMath
52 import lsst.afw.table
as afwTable
53 from lsst.meas.algorithms
import IndexerRegistry
54 from lsst.meas.algorithms
import DatasetConfig
55 from lsst.meas.algorithms.ingestIndexReferenceTask
import addRefCatMetadata
57 from .utilities
import computeApproxPixelAreaFields
61 __all__ = [
'FgcmOutputProductsConfig',
'FgcmOutputProductsTask',
'FgcmOutputProductsRunner']
65 """Config for FgcmOutputProductsTask""" 67 cycleNumber = pexConfig.Field(
68 doc=
"Final fit cycle from FGCM fit",
75 doReferenceCalibration = pexConfig.Field(
76 doc=(
"Transfer 'absolute' calibration from reference catalog? " 77 "This afterburner step is unnecessary if reference stars " 78 "were used in the full fit in FgcmFitCycleTask."),
82 doRefcatOutput = pexConfig.Field(
83 doc=
"Output standard stars in reference catalog format",
87 doAtmosphereOutput = pexConfig.Field(
88 doc=
"Output atmospheres in transmission_atmosphere_fgcm format",
92 doZeropointOutput = pexConfig.Field(
93 doc=
"Output zeropoints in fgcm_photoCalib format",
97 doComposeWcsJacobian = pexConfig.Field(
98 doc=
"Compose Jacobian of WCS with fgcm calibration for output photoCalib?",
102 refObjLoader = pexConfig.ConfigurableField(
103 target=LoadIndexedReferenceObjectsTask,
104 doc=
"reference object loader for 'absolute' photometric calibration",
106 photoCal = pexConfig.ConfigurableField(
108 doc=
"task to perform 'absolute' calibration",
110 referencePixelizationNside = pexConfig.Field(
111 doc=
"Healpix nside to pixelize catalog to compare to reference catalog",
115 referencePixelizationMinStars = pexConfig.Field(
116 doc=(
"Minimum number of stars per healpix pixel to select for comparison" 117 "to the specified reference catalog"),
121 referenceMinMatch = pexConfig.Field(
122 doc=
"Minimum number of stars matched to reference catalog to be used in statistics",
126 referencePixelizationNPixels = pexConfig.Field(
127 doc=(
"Number of healpix pixels to sample to do comparison. " 128 "Doing too many will take a long time and not yield any more " 129 "precise results because the final number is the median offset " 130 "(per band) from the set of pixels."),
134 datasetConfig = pexConfig.ConfigField(
136 doc=
"Configuration for writing/reading ingested catalog",
140 pexConfig.Config.setDefaults(self)
150 self.
photoCal.applyColorTerms =
False 151 self.
photoCal.fluxField =
'instFlux' 153 self.
photoCal.match.referenceSelection.doSignalToNoise =
True 154 self.
photoCal.match.referenceSelection.signalToNoise.minimum = 10.0
155 self.
photoCal.match.sourceSelection.doSignalToNoise =
True 156 self.
photoCal.match.sourceSelection.signalToNoise.minimum = 10.0
157 self.
photoCal.match.sourceSelection.signalToNoise.fluxField =
'instFlux' 158 self.
photoCal.match.sourceSelection.signalToNoise.errField =
'instFluxErr' 159 self.
photoCal.match.sourceSelection.doFlags =
True 160 self.
photoCal.match.sourceSelection.flags.good = []
161 self.
photoCal.match.sourceSelection.flags.bad = [
'flag_badStar']
162 self.
photoCal.match.sourceSelection.doUnresolved =
False 168 """Subclass of TaskRunner for fgcmOutputProductsTask 170 fgcmOutputProductsTask.run() takes one argument, the butler, and 171 does not run on any data in the repository. 172 This runner does not use any parallelization. 178 Return a list with one element, the butler. 180 return [parsedCmd.butler]
186 butler: `lsst.daf.persistence.Butler` 190 exitStatus: `list` with `pipeBase.Struct` 191 exitStatus (0: success; 1: failure) 192 if self.doReturnResults also 193 results (`np.array` with absolute zeropoint offsets) 195 task = self.TaskClass(butler=butler, config=self.config, log=self.log)
199 results = task.runDataRef(butler)
202 results = task.runDataRef(butler)
203 except Exception
as e:
205 task.log.fatal(
"Failed: %s" % e)
206 if not isinstance(e, pipeBase.TaskError):
207 traceback.print_exc(file=sys.stderr)
209 task.writeMetadata(butler)
211 if self.doReturnResults:
213 return [pipeBase.Struct(exitStatus=exitStatus,
216 return [pipeBase.Struct(exitStatus=exitStatus)]
220 Run the task, with no multiprocessing 224 parsedCmd: `lsst.pipe.base.ArgumentParser` parsed command line 229 if self.precall(parsedCmd):
232 resultList = self(targetList[0])
239 Output products from FGCM global calibration. 242 ConfigClass = FgcmOutputProductsConfig
243 RunnerClass = FgcmOutputProductsRunner
244 _DefaultName =
"fgcmOutputProducts" 248 Instantiate an fgcmOutputProductsTask. 252 butler : `lsst.daf.persistence.Butler` 255 pipeBase.CmdLineTask.__init__(self, **kwargs)
257 if self.config.doReferenceCalibration:
259 self.makeSubtask(
"refObjLoader", butler=butler)
261 if self.config.doRefcatOutput:
262 self.
indexer = IndexerRegistry[self.config.datasetConfig.indexer.name](
263 self.config.datasetConfig.indexer.active)
266 def _getMetadataName(self):
272 Make FGCM output products for use in the stack 276 butler: `lsst.daf.persistence.Butler` 278 Final fit cycle number, override config. 282 offsets: `lsst.pipe.base.Struct` 283 A structure with array of zeropoint offsets 287 RuntimeError: Raised if butler cannot find fgcmBuildStars_config, or 288 fgcmFitCycle_config, or fgcmAtmosphereParameters (and 289 `self.config.doAtmosphereOutput` is true), or fgcmStandardStars (and 290 `self.config.doReferenceCalibration or `self.config.doRefcatOutput` 291 is true), or fgcmZeropoints (and self.config.doZeropointOutput is true). 292 Also will raise if the fgcmFitCycle_config does not refer to the 298 if not butler.datasetExists(
'fgcmBuildStars_config'):
299 raise RuntimeError(
"Cannot find fgcmBuildStars_config, which is prereq for fgcmOutputProducts")
301 fgcmBuildStarsConfig = butler.get(
'fgcmBuildStars_config')
306 if self.config.doComposeWcsJacobian
and not fgcmBuildStarsConfig.doApplyWcsJacobian:
307 raise RuntimeError(
"Cannot compose the WCS jacobian if it hasn't been applied " 308 "in fgcmBuildStarsTask.")
310 if not self.config.doComposeWcsJacobian
and fgcmBuildStarsConfig.doApplyWcsJacobian:
311 self.log.warn(
"Jacobian was applied in build-stars but doComposeWcsJacobian is not set.")
314 if not butler.datasetExists(
'fgcmFitCycle_config', fgcmcycle=self.config.cycleNumber):
315 raise RuntimeError(
"Cannot find fgcmFitCycle_config from cycle %d " % (self.config.cycleNumber) +
316 "which is required for fgcmOutputProducts.")
318 fitCycleConfig = butler.get(
'fgcmFitCycle_config', fgcmcycle=self.config.cycleNumber)
321 if self.config.doReferenceCalibration
and fitCycleConfig.doReferenceCalibration:
322 self.log.warn(
"doReferenceCalibration is set, and is possibly redundant with " 323 "fitCycleConfig.doReferenceCalibration")
326 if (self.config.doAtmosphereOutput
and 327 not butler.datasetExists(
'fgcmAtmosphereParameters', fgcmcycle=self.config.cycleNumber)):
328 raise RuntimeError(
"Atmosphere parameters are missing for cycle %d." %
329 (self.config.cycleNumber))
331 if ((self.config.doReferenceCalibration
or self.config.doRefcatOutput)
and 332 (
not butler.datasetExists(
'fgcmStandardStars',
333 fgcmcycle=self.config.cycleNumber))):
334 raise RuntimeError(
"Standard stars are missing for cycle %d." %
335 (self.config.cycleNumber))
337 if (self.config.doZeropointOutput
and 338 (
not butler.datasetExists(
'fgcmZeropoints', fgcmcycle=self.config.cycleNumber))):
339 raise RuntimeError(
"Zeropoints are missing for cycle %d." %
340 (self.config.cycleNumber))
343 if butler.datasetExists(
'fgcmFitCycle_config', fgcmcycle=self.config.cycleNumber + 1):
344 raise RuntimeError(
"The task fgcmOutputProducts should only be run" 345 "on the final fit cycle products")
347 if self.config.doReferenceCalibration
or self.config.doRefcatOutput:
348 stdCat = butler.get(
'fgcmStandardStars', fgcmcycle=self.config.cycleNumber)
349 md = stdCat.getMetadata()
355 if self.config.doReferenceCalibration:
358 offsets = np.zeros(len(self.
bands))
361 if self.config.doRefcatOutput:
367 if self.config.doZeropointOutput:
368 zptCat = butler.get(
'fgcmZeropoints', fgcmcycle=self.config.cycleNumber)
369 visitCat = butler.get(
'fgcmVisitCatalog')
374 if self.config.doAtmosphereOutput:
375 atmCat = butler.get(
'fgcmAtmosphereParameters', fgcmcycle=self.config.cycleNumber)
379 return pipeBase.Struct(offsets=offsets)
382 visitCat, zptCat, atmCat, stdCat,
383 fgcmBuildStarsConfig, fgcmFitCycleConfig):
385 Generate the output products for a given tract, as specified in the config. 387 This method is here to have an alternate entry-point for 392 butler: `lsst.daf.persistence.Butler` 395 visitCat: `lsst.afw.table.BaseCatalog` 396 FGCM visitCat from `FgcmBuildStarsTask` 397 zptCat: `lsst.afw.table.BaseCatalog` 398 FGCM zeropoint catalog from `FgcmFitCycleTask` 399 atmCat: `lsst.afw.table.BaseCatalog` 400 FGCM atmosphere parameter catalog from `FgcmFitCycleTask` 401 stdCat: `lsst.afw.table.SimpleCatalog` 402 FGCM standard star catalog from `FgcmFitCycleTask` 403 fgcmBuildStarsConfig: `lsst.fgcmcal.FgcmBuildStarsConfig` 404 Configuration object from `FgcmBuildStarsTask` 405 fgcmFitCycleConfig: `lsst.fgcmcal.FgcmFitCycleConfig` 406 Configuration object from `FgcmFitCycleTask` 412 self.
filterMap = fgcmBuildStarsConfig.filterMap
414 if stdCat
is not None:
415 md = stdCat.getMetadata()
416 self.
bands = md.getArray(
'BANDS')
420 if self.config.doReferenceCalibration
and fgcmFitCycleConfig.doReferenceCalibration:
421 self.log.warn(
"doReferenceCalibration is set, and is possibly redundant with " 422 "fitCycleConfig.doReferenceCalibration")
424 if self.config.doComposeWcsJacobian
and not fgcmBuildStarsConfig.doApplyWcsJacobian:
425 raise RuntimeError(
"Cannot compose the WCS jacobian if it hasn't been applied " 426 "in fgcmBuildStarsTask.")
428 if not self.config.doComposeWcsJacobian
and fgcmBuildStarsConfig.doApplyWcsJacobian:
429 self.log.warn(
"Jacobian was applied in build-stars but doComposeWcsJacobian is not set.")
431 if self.config.doReferenceCalibration:
434 offsets = np.zeros(len(self.
bands))
436 if self.config.doRefcatOutput:
438 datasetConfig = copy.copy(self.config.datasetConfig)
439 datasetConfig.ref_dataset_name =
'%s_%d' % (self.config.datasetConfig.ref_dataset_name,
443 if self.config.doZeropointOutput:
446 if self.config.doAtmosphereOutput:
449 return pipeBase.Struct(offsets=offsets)
451 def _computeReferenceOffsets(self, butler, stdCat):
453 Compute offsets relative to a reference catalog. 455 This method splits the star catalog into healpix pixels 456 and computes the calibration transfer for a sample of 457 these pixels to approximate the 'absolute' calibration 458 values (on for each band) to apply to transfer the 463 butler: `lsst.daf.persistence.Butler` 464 stdCat: `lsst.afw.table.SimpleCatalog` 469 offsets: `numpy.array` of floats 470 Per band zeropoint offsets 476 minObs = stdCat[
'ngood'].min(axis=1)
478 goodStars = (minObs >= 1)
479 stdCat = stdCat[goodStars]
481 self.log.info(
"Found %d stars with at least 1 good observation in each band" %
491 sourceMapper = afwTable.SchemaMapper(stdCat.schema)
492 sourceMapper.addMinimalSchema(afwTable.SimpleTable.makeMinimalSchema())
493 sourceMapper.editOutputSchema().addField(
'instFlux', type=np.float64,
494 doc=
"instrumental flux (counts)")
495 sourceMapper.editOutputSchema().addField(
'instFluxErr', type=np.float64,
496 doc=
"instrumental flux error (counts)")
497 badStarKey = sourceMapper.editOutputSchema().addField(
'flag_badStar',
505 theta = np.pi/2. - stdCat[
'coord_dec']
506 phi = stdCat[
'coord_ra']
508 ipring = hp.ang2pix(self.config.referencePixelizationNside, theta, phi)
509 h, rev = esutil.stat.histogram(ipring, rev=
True)
511 gdpix, = np.where(h >= self.config.referencePixelizationMinStars)
513 self.log.info(
"Found %d pixels (nside=%d) with at least %d good stars" %
515 self.config.referencePixelizationNside,
516 self.config.referencePixelizationMinStars))
518 if gdpix.size < self.config.referencePixelizationNPixels:
519 self.log.warn(
"Found fewer good pixels (%d) than preferred in configuration (%d)" %
520 (gdpix.size, self.config.referencePixelizationNPixels))
523 gdpix = np.random.choice(gdpix, size=self.config.referencePixelizationNPixels, replace=
False)
525 results = np.zeros(gdpix.size, dtype=[(
'hpix',
'i4'),
526 (
'nstar',
'i4', len(self.
bands)),
527 (
'nmatch',
'i4', len(self.
bands)),
528 (
'zp',
'f4', len(self.
bands)),
529 (
'zpErr',
'f4', len(self.
bands))])
530 results[
'hpix'] = ipring[rev[rev[gdpix]]]
533 selected = np.zeros(len(stdCat), dtype=np.bool)
535 refFluxFields = [
None]*len(self.
bands)
537 for p, pix
in enumerate(gdpix):
538 i1a = rev[rev[pix]: rev[pix + 1]]
546 for b, band
in enumerate(self.
bands):
549 selected, refFluxFields)
550 results[
'nstar'][p, b] = len(i1a)
551 results[
'nmatch'][p, b] = len(struct.arrays.refMag)
552 results[
'zp'][p, b] = struct.zp
553 results[
'zpErr'][p, b] = struct.sigma
556 offsets = np.zeros(len(self.
bands))
558 for b, band
in enumerate(self.
bands):
560 ok, = np.where(results[
'nmatch'][:, b] >= self.config.referenceMinMatch)
561 offsets[b] = np.median(results[
'zp'][ok, b])
564 madSigma = 1.4826*np.median(np.abs(results[
'zp'][ok, b] - offsets[b]))
565 self.log.info(
"Reference catalog offset for %s band: %.12f +/- %.12f" %
566 (band, offsets[b], madSigma))
570 def _computeOffsetOneBand(self, sourceMapper, badStarKey,
571 b, band, stdCat, selected, refFluxFields):
573 Compute the zeropoint offset between the fgcm stdCat and the reference 574 stars for one pixel in one band 578 sourceMapper: `lsst.afw.table.SchemaMapper` 579 Mapper to go from stdCat to calibratable catalog 580 badStarKey: `lsst.afw.table.Key` 581 Key for the field with bad stars 583 Index of the band in the star catalog 585 Name of band for reference catalog 586 stdCat: `lsst.afw.table.SimpleCatalog` 588 selected: `numpy.array(dtype=np.bool)` 589 Boolean array of which stars are in the pixel 590 refFluxFields: `list` 591 List of names of flux fields for reference catalog 594 sourceCat = afwTable.SimpleCatalog(sourceMapper.getOutputSchema())
595 sourceCat.reserve(selected.sum())
596 sourceCat.extend(stdCat[selected], mapper=sourceMapper)
597 sourceCat[
'instFlux'] = 10.**(stdCat[
'mag_std_noabs'][selected, b]/(-2.5))
598 sourceCat[
'instFluxErr'] = (np.log(10.)/2.5)*(stdCat[
'magErr_std'][selected, b] *
599 sourceCat[
'instFlux'])
603 badStar = (stdCat[
'mag_std_noabs'][selected, b] > 90.0)
604 for rec
in sourceCat[badStar]:
605 rec.set(badStarKey,
True)
607 exposure = afwImage.ExposureF()
608 exposure.setFilter(afwImage.Filter(band))
610 if refFluxFields[b]
is None:
613 ctr = stdCat[0].getCoord()
614 rad = 0.05*lsst.geom.degrees
615 refDataTest = self.refObjLoader.loadSkyCircle(ctr, rad, band)
616 refFluxFields[b] = refDataTest.fluxField
619 calConfig = copy.copy(self.config.photoCal.value)
620 calConfig.match.referenceSelection.signalToNoise.fluxField = refFluxFields[b]
621 calConfig.match.referenceSelection.signalToNoise.errField = refFluxFields[b] +
'Err' 622 calTask = self.config.photoCal.target(refObjLoader=self.refObjLoader,
624 schema=sourceCat.getSchema())
626 struct = calTask.run(exposure, sourceCat)
630 def _outputStandardStars(self, butler, stdCat, offsets, datasetConfig):
632 Output standard stars in indexed reference catalog format. 636 butler: `lsst.daf.persistence.Butler` 637 stdCat: `lsst.afw.table.SimpleCatalog` 638 FGCM standard star catalog from fgcmFitCycleTask 639 offsets: `numpy.array` of floats 640 Per band zeropoint offsets 641 datasetConfig: `lsst.meas.algorithms.DatasetConfig` 642 Config for reference dataset 645 self.log.info(
"Outputting standard stars to %s" % (datasetConfig.ref_dataset_name))
652 conv = stdCat[0][
'coord_ra'].asDegrees()/float(stdCat[0][
'coord_ra'])
653 indices = np.array(self.
indexer.indexPoints(stdCat[
'coord_ra']*conv,
654 stdCat[
'coord_dec']*conv))
659 dataId = self.
indexer.makeDataId(
'master_schema',
660 datasetConfig.ref_dataset_name)
661 masterCat = afwTable.SimpleCatalog(formattedCat.schema)
662 addRefCatMetadata(masterCat)
663 butler.put(masterCat,
'ref_cat', dataId=dataId)
666 h, rev = esutil.stat.histogram(indices, rev=
True)
667 gd, = np.where(h > 0)
668 selected = np.zeros(len(formattedCat), dtype=np.bool)
670 i1a = rev[rev[i]: rev[i + 1]]
679 dataId = self.
indexer.makeDataId(indices[i1a[0]],
680 datasetConfig.ref_dataset_name)
681 butler.put(formattedCat[selected],
'ref_cat', dataId=dataId)
684 dataId = self.
indexer.makeDataId(
None, datasetConfig.ref_dataset_name)
685 butler.put(datasetConfig,
'ref_cat_config', dataId=dataId)
687 self.log.info(
"Done outputting standard stars.")
689 def _formatCatalog(self, fgcmStarCat, offsets):
691 Turn an FGCM-formatted star catalog, applying zeropoint offsets. 695 fgcmStarCat: `lsst.afw.Table.SimpleCatalog` 696 SimpleCatalog as output by fgcmcal 697 offsets: `list` with len(self.bands) entries 698 Zeropoint offsets to apply 702 formattedCat: `lsst.afw.table.SimpleCatalog` 703 SimpleCatalog suitable for using as a reference catalog 706 sourceMapper = afwTable.SchemaMapper(fgcmStarCat.schema)
707 minSchema = LoadIndexedReferenceObjectsTask.makeMinimalSchema(self.
bands,
711 sourceMapper.addMinimalSchema(minSchema)
712 for band
in self.
bands:
713 sourceMapper.editOutputSchema().addField(
'%s_nGood' % (band), type=np.int32)
714 sourceMapper.editOutputSchema().addField(
'%s_nTotal' % (band), type=np.int32)
715 sourceMapper.editOutputSchema().addField(
'%s_nPsfCandidate' % (band), type=np.int32)
717 formattedCat = afwTable.SimpleCatalog(sourceMapper.getOutputSchema())
718 formattedCat.reserve(len(fgcmStarCat))
719 formattedCat.extend(fgcmStarCat, mapper=sourceMapper)
723 for b, band
in enumerate(self.
bands):
724 mag = fgcmStarCat[
'mag_std_noabs'][:, b].astype(np.float64) + offsets[b]
727 flux = (mag*units.ABmag).to_value(units.nJy)
728 fluxErr = (np.log(10.)/2.5)*flux*fgcmStarCat[
'magErr_std'][:, b].astype(np.float64)
730 formattedCat[
'%s_flux' % (band)][:] = flux
731 formattedCat[
'%s_fluxErr' % (band)][:] = fluxErr
732 formattedCat[
'%s_nGood' % (band)][:] = fgcmStarCat[
'ngood'][:, b]
733 formattedCat[
'%s_nTotal' % (band)][:] = fgcmStarCat[
'ntotal'][:, b]
734 formattedCat[
'%s_nPsfCandidate' % (band)][:] = fgcmStarCat[
'npsfcand'][:, b]
736 addRefCatMetadata(formattedCat)
740 def _outputZeropoints(self, butler, zptCat, visitCat, offsets, tract=None):
742 Output the zeropoints in fgcm_photoCalib format. 746 butler: `lsst.daf.persistence.Butler` 747 zptCat: `lsst.afw.table.BaseCatalog` 748 FGCM zeropoint catalog from `FgcmFitCycleTask` 749 visitCat: `lsst.afw.table.BaseCatalog` 750 FGCM visitCat from `FgcmBuildStarsTask` 751 offsets: `numpy.array` 752 Float array of absolute calibration offsets, one for each filter 753 tract: `int`, optional 754 Tract number to output. Default is None (global calibration) 758 datasetType =
'fgcm_photoCalib' 760 datasetType =
'fgcm_tract_photoCalib' 762 self.log.info(
"Outputting %s objects" % (datasetType))
767 cannot_compute = fgcm.fgcmUtilities.zpFlagDict[
'CANNOT_COMPUTE_ZEROPOINT']
768 too_few_stars = fgcm.fgcmUtilities.zpFlagDict[
'TOO_FEW_STARS_ON_CCD']
769 selected = (((zptCat[
'fgcmFlag'] & cannot_compute) == 0) &
770 (zptCat[
'fgcmZptVar'] > 0.0))
774 selected_best = (((zptCat[
'fgcmFlag'] & (cannot_compute | too_few_stars)) == 0) &
775 (zptCat[
'fgcmZptVar'] > 0.0))
778 badVisits = np.unique(zptCat[
'visit'][~selected])
779 goodVisits = np.unique(zptCat[
'visit'][selected])
780 allBadVisits = badVisits[~np.isin(badVisits, goodVisits)]
781 for allBadVisit
in allBadVisits:
782 self.log.warn(f
'No suitable photoCalib for {self.visitDataRefName} {allBadVisit}')
787 for rec
in zptCat[selected_best]:
788 if rec[
'filtername']
in filterMapping:
792 dataRef = butler.dataRef(
'raw', dataId=dataId)
793 filterMapping[rec[
'filtername']] = dataRef.dataId[
'filter']
806 camera = butler.get(
'camera')
808 for ccdIndex, detector
in enumerate(camera):
809 ccdMapping[detector.getId()] = ccdIndex
814 scalingMapping[rec[
'visit']] = rec[
'scaling']
816 if self.config.doComposeWcsJacobian:
819 for rec
in zptCat[selected]:
822 scaling = scalingMapping[rec[
'visit']][ccdMapping[rec[
'ccd']]]
825 rec[
'fgcmfZptChebXyMax'])
828 rec[
'fgcmfZptChebXyMax'],
829 offset=offsetMapping[rec[
'filtername']],
832 if self.config.doComposeWcsJacobian:
834 fgcmField = afwMath.ProductBoundedField([approxPixelAreaFields[rec[
'ccd']],
840 fgcmField = afwMath.ProductBoundedField([fgcmSuperStarField, fgcmZptField])
843 calibCenter = fgcmField.evaluate(fgcmField.getBBox().getCenter())
844 calibErr = (np.log(10.0)/2.5)*calibCenter*np.sqrt(rec[
'fgcmZptVar'])
845 photoCalib = afwImage.PhotoCalib(calibrationMean=calibCenter,
846 calibrationErr=calibErr,
847 calibration=fgcmField,
851 butler.put(photoCalib, datasetType,
854 'filter': filterMapping[rec[
'filtername']]})
856 butler.put(photoCalib, datasetType,
859 'filter': filterMapping[rec[
'filtername']],
862 self.log.info(
"Done outputting %s objects" % (datasetType))
864 def _getChebyshevBoundedField(self, coefficients, xyMax, offset=0.0, scaling=1.0):
866 Make a ChebyshevBoundedField from fgcm coefficients, with optional offset 871 coefficients: `numpy.array` 872 Flattened array of chebyshev coefficients 873 xyMax: `list` of length 2 874 Maximum x and y of the chebyshev bounding box 875 offset: `float`, optional 876 Absolute calibration offset. Default is 0.0 877 scaling: `float`, optional 878 Flat scaling value from fgcmBuildStars. Default is 1.0 882 boundedField: `lsst.afw.math.ChebyshevBoundedField` 885 orderPlus1 = int(np.sqrt(coefficients.size))
886 pars = np.zeros((orderPlus1, orderPlus1))
888 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0.0, 0.0),
889 lsst.geom.Point2I(*xyMax))
891 pars[:, :] = (coefficients.reshape(orderPlus1, orderPlus1) *
892 (10.**(offset/-2.5))*scaling)
894 boundedField = afwMath.ChebyshevBoundedField(bbox, pars)
898 def _outputAtmospheres(self, butler, atmCat, tract=None):
900 Output the atmospheres. 904 butler: `lsst.daf.persistence.Butler` 905 atmCat: `lsst.afw.table.BaseCatalog` 906 FGCM atmosphere parameter catalog from fgcmFitCycleTask 907 tract: `int`, optional 908 Tract number to output. Default is None (global calibration) 911 self.log.info(
"Outputting atmosphere transmissions")
914 lutCat = butler.get(
'fgcmLookUpTable')
916 atmosphereTableName = lutCat[0][
'tablename']
917 elevation = lutCat[0][
'elevation']
918 atmLambda = lutCat[0][
'atmLambda']
923 atmTable = fgcm.FgcmAtmosphereTable.initWithTableName(atmosphereTableName)
931 modGen = fgcm.ModtranGenerator(elevation)
932 lambdaRange = np.array([atmLambda[0], atmLambda[-1]])/10.
933 lambdaStep = (atmLambda[1] - atmLambda[0])/10.
934 except (ValueError, IOError)
as e:
935 raise RuntimeError(
"FGCM look-up-table generated with modtran, " 936 "but modtran not configured to run.")
from e
938 zenith = np.degrees(np.arccos(1./atmCat[
'secZenith']))
940 for i, visit
in enumerate(atmCat[
'visit']):
941 if atmTable
is not None:
943 atmVals = atmTable.interpolateAtmosphere(pmb=atmCat[i][
'pmb'],
944 pwv=atmCat[i][
'pwv'],
946 tau=atmCat[i][
'tau'],
947 alpha=atmCat[i][
'alpha'],
949 ctranslamstd=[atmCat[i][
'cTrans'],
950 atmCat[i][
'lamStd']])
953 modAtm = modGen(pmb=atmCat[i][
'pmb'],
954 pwv=atmCat[i][
'pwv'],
956 tau=atmCat[i][
'tau'],
957 alpha=atmCat[i][
'alpha'],
959 lambdaRange=lambdaRange,
960 lambdaStep=lambdaStep,
961 ctranslamstd=[atmCat[i][
'cTrans'],
962 atmCat[i][
'lamStd']])
963 atmVals = modAtm[
'COMBINED']
966 curve = TransmissionCurve.makeSpatiallyConstant(throughput=atmVals,
967 wavelengths=atmLambda,
968 throughputAtMin=atmVals[0],
969 throughputAtMax=atmVals[-1])
972 butler.put(curve,
"transmission_atmosphere_fgcm",
975 butler.put(curve,
"transmission_atmosphere_fgcm_tract",
979 self.log.info(
"Done outputting atmosphere transmissions")
def getTargetList(parsedCmd)
def __init__(self, butler=None, kwargs)
def computeApproxPixelAreaFields(camera)
def generateTractOutputProducts(self, butler, tract, visitCat, zptCat, atmCat, stdCat, fgcmBuildStarsConfig, fgcmFitCycleConfig)
def _computeReferenceOffsets(self, butler, stdCat)
def _outputZeropoints(self, butler, zptCat, visitCat, offsets, tract=None)
def __call__(self, butler)
def _formatCatalog(self, fgcmStarCat, offsets)
def _getChebyshevBoundedField(self, coefficients, xyMax, offset=0.0, scaling=1.0)
def runDataRef(self, butler)
def _outputStandardStars(self, butler, stdCat, offsets, datasetConfig)
def _computeOffsetOneBand(self, sourceMapper, badStarKey, b, band, stdCat, selected, refFluxFields)
def _outputAtmospheres(self, butler, atmCat, tract=None)