381 camera = butlerQC.get(inputRefs.camera)
383 if self.config.doOpticsTransmission:
384 opticsHandle = butlerQC.get(inputRefs.transmission_optics)
388 if self.config.doSensorTransmission:
389 sensorHandles = butlerQC.get(inputRefs.transmission_sensor)
390 sensorHandleDict = {sensorHandle.dataId[
'detector']: sensorHandle
for
391 sensorHandle
in sensorHandles}
393 sensorHandleDict = {}
395 if self.config.doFilterDetectorTransmission:
396 filterHandles = butlerQC.get(inputRefs.transmission_filter_detector)
398 (filterHandle.dataId[
"physical_filter"], filterHandle.dataId[
"detector"]): filterHandle
399 for filterHandle
in filterHandles
402 filterHandles = butlerQC.get(inputRefs.transmission_filter)
403 filterHandleDict = {filterHandle.dataId[
'physical_filter']: filterHandle
for
404 filterHandle
in filterHandles}
407 filterHandle.dataId[
"physical_filter"]: filterHandle.dataId[
"band"]
408 for filterHandle
in filterHandles
419 butlerQC.put(struct.fgcmLookUpTable, outputRefs.fgcmLookUpTable)
420 butlerQC.put(struct.fgcmStandardAtmosphere, outputRefs.fgcmStandardAtmosphere)
422 refDict = {passbandRef.dataId[
'physical_filter']: passbandRef
for
423 passbandRef
in outputRefs.fgcmStandardPassbands}
424 for physical_filter, passband
in struct.fgcmStandardPassbands.items():
425 butlerQC.put(passband, refDict[physical_filter])
427 bandRefDict = {passbandRef.dataId[
"band"]: passbandRef
for
428 passbandRef
in outputRefs.standardPassbands}
429 for band, passband
in struct.standardPassbands.items():
430 butlerQC.put(passband, bandRefDict[band])
433 filterHandleDict, filterToBand):
435 Make a FGCM Look-up Table
439 camera : `lsst.afw.cameraGeom.Camera`
440 Camera from the butler.
441 opticsHandle : `lsst.daf.butler.DeferredDatasetHandle`
442 Reference to optics transmission curve.
443 sensorHandleDict : `dict` of [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
444 Dictionary of references to sensor transmission curves. Key will
446 filterHandleDict : `dict` of [`str`, `lsst.daf.butler.DeferredDatasetHandle`]
447 Dictionary of references to filter transmission curves. Key will
448 be physical filter label or tuple of physical filter label and
450 filterToBand : `dict` [`str`: `str`]
451 Mapping of physical filter to band name.
455 retStruct : `lsst.pipe.base.Struct`
456 Output structure with keys:
458 fgcmLookUpTable : `BaseCatalog`
459 The FGCM look-up table.
460 fgcmStandardAtmosphere : `lsst.afw.image.TransmissionCurve`
461 Transmission curve for the FGCM standard atmosphere.
462 fgcmStandardPassbands : `dict` [`str`, `lsst.afw.image.TransmissionCurve`]
463 Dictionary of fgcm standard passbands, with the key as the
464 physical filter name.
465 standardPassbands : `dict` [`str`, `astropy.table.Table`]
466 Dictionary of standard passbands in astropy table format, with
467 the key as the band name.
470 nCcd = countDetectors(camera, self.config.useScienceDetectors)
472 self.log.info(
"Found %d ccds for look-up table" % (nCcd))
483 self.log.info(
"Making the LUT maker object")
491 throughputLambda = np.arange(self.
fgcmLutMaker.lambdaRange[0],
495 self.log.info(
"Built throughput lambda, %.1f-%.1f, step %.2f" %
496 (throughputLambda[0], throughputLambda[-1],
497 throughputLambda[1] - throughputLambda[0]))
500 for i, physicalFilter
in enumerate(self.config.physicalFilters):
502 tDict[
'LAMBDA'] = throughputLambda
503 for ccdIndex, detector
in enumerate(camera):
504 if self.config.useScienceDetectors:
505 if not detector.getType() == afwCameraGeom.DetectorType.SCIENCE:
508 throughputDict[physicalFilter] = tDict
514 self.log.info(
"Making LUT")
521 physicalFilterString = comma.join(self.config.physicalFilters)
524 atmosphereTableName =
'NoTableWasUsed'
525 if self.config.atmosphereTableName
is not None:
526 atmosphereTableName = self.config.atmosphereTableName
528 lutSchema = self.
_makeLutSchema(physicalFilterString, stdPhysicalFilterString,
531 lutCat = self.
_makeLutCat(lutSchema, physicalFilterString,
532 stdPhysicalFilterString, atmosphereTableName)
534 atmStd = TransmissionCurve.makeSpatiallyConstant(
535 throughput=self.
fgcmLutMaker.atmStdTrans.astype(np.float64),
536 wavelengths=self.
fgcmLutMaker.atmLambda.astype(np.float64),
541 fgcmStandardPassbands = {}
542 for i, physical_filter
in enumerate(self.
fgcmLutMaker.filterNames):
544 fgcmStandardPassbands[physical_filter] = TransmissionCurve.makeSpatiallyConstant(
545 throughput=passband.astype(np.float64),
546 wavelengths=self.
fgcmLutMaker.atmLambda.astype(np.float64),
547 throughputAtMin=passband[0],
548 throughputAtMax=passband[-1],
551 standardPassbands = {}
552 for i, physical_filter
in enumerate(self.
fgcmLutMaker.filterNames):
553 if physical_filter != self.
fgcmLutMaker.stdFilterNames[i]:
558 band = filterToBand[physical_filter]
560 passbandTable = Table(
562 "wavelength": (self.
fgcmLutMaker.atmLambda.astype(np.float64)/10.)*units.nm,
563 "throughput": (passband*100.)*units.percent,
566 passbandTable[
"wavelength"].description =
"Wavelength bin centers"
567 standardPassbands[band] = passbandTable
569 retStruct = pipeBase.Struct(
570 fgcmLookUpTable=lutCat,
571 fgcmStandardAtmosphere=atmStd,
572 fgcmStandardPassbands=fgcmStandardPassbands,
573 standardPassbands=standardPassbands,
592 Create the fgcmLut config dictionary
597 Number of CCDs in the camera
602 lutConfig[
'logger'] = self.log
603 lutConfig[
'filterNames'] = self.config.physicalFilters
605 lutConfig[
'nCCD'] = nCcd
608 if self.config.atmosphereTableName
is not None:
609 lutConfig[
'atmosphereTableName'] = self.config.atmosphereTableName
612 lutConfig[
'elevation'] = self.config.parameters.elevation
613 lutConfig[
'pmbRange'] = self.config.parameters.pmbRange
614 lutConfig[
'pmbSteps'] = self.config.parameters.pmbSteps
615 lutConfig[
'pwvRange'] = self.config.parameters.pwvRange
616 lutConfig[
'pwvSteps'] = self.config.parameters.pwvSteps
617 lutConfig[
'o3Range'] = self.config.parameters.o3Range
618 lutConfig[
'o3Steps'] = self.config.parameters.o3Steps
619 lutConfig[
'tauRange'] = self.config.parameters.tauRange
620 lutConfig[
'tauSteps'] = self.config.parameters.tauSteps
621 lutConfig[
'alphaRange'] = self.config.parameters.alphaRange
622 lutConfig[
'alphaSteps'] = self.config.parameters.alphaSteps
623 lutConfig[
'zenithRange'] = self.config.parameters.zenithRange
624 lutConfig[
'zenithSteps'] = self.config.parameters.zenithSteps
625 lutConfig[
'pmbStd'] = self.config.parameters.pmbStd
626 lutConfig[
'pwvStd'] = self.config.parameters.pwvStd
627 lutConfig[
'o3Std'] = self.config.parameters.o3Std
628 lutConfig[
'tauStd'] = self.config.parameters.tauStd
629 lutConfig[
'alphaStd'] = self.config.parameters.alphaStd
630 lutConfig[
'airmassStd'] = self.config.parameters.airmassStd
631 lutConfig[
'lambdaRange'] = self.config.parameters.lambdaRange
632 lutConfig[
'lambdaStep'] = self.config.parameters.lambdaStep
633 lutConfig[
'lambdaNorm'] = self.config.parameters.lambdaNorm
637 if self.config.sensorCorrectionTermDict:
638 lutConfig[
'sensorCTerms'] = {}
639 for key, value
in self.config.sensorCorrectionTermDict.items():
640 lutConfig[
'sensorCTerms'][key] = (
642 dict(value.correctionTermDict),
648 """Internal method to load throughput data for filters
652 camera: `lsst.afw.cameraGeom.Camera`
653 Camera from the butler
654 opticsHandle : `lsst.daf.butler.DeferredDatasetHandle`
655 Reference to optics transmission curve.
656 sensorHandleDict : `dict` of [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
657 Dictionary of references to sensor transmission curves. Key will
659 filterHandleDict : `dict` of [`str`, `lsst.daf.butler.DeferredDatasetHandle`]
660 Dictionary of references to filter transmission curves. Key will
661 be physical filter label.
665 ValueError : Raised if configured filter name does not match any of the
666 available filter transmission curves.
668 if self.config.doOpticsTransmission:
672 throughput=np.ones(100),
673 wavelengths=np.linspace(
674 self.config.parameters.lambdaRange[0],
675 self.config.parameters.lambdaRange[1],
683 for detector
in camera:
684 if self.config.useScienceDetectors:
685 if not detector.getType() == afwCameraGeom.DetectorType.SCIENCE:
687 if self.config.doSensorTransmission:
691 throughput=np.ones(100),
692 wavelengths=np.linspace(
693 self.config.parameters.lambdaRange[0],
694 self.config.parameters.lambdaRange[1],
702 if self.config.doFilterDetectorTransmission:
703 for physicalFilter
in self.config.physicalFilters:
704 for detector
in camera:
705 if self.config.useScienceDetectors:
706 if not detector.getType() == afwCameraGeom.DetectorType.SCIENCE:
708 key = (physicalFilter, detector.getId())
711 for physicalFilter
in self.config.physicalFilters:
715 """Internal method to get throughput for a detector.
717 Returns the throughput at the center of the detector for a given filter.
721 detector: `lsst.afw.cameraGeom._detector.Detector`
723 physicalFilter: `str`
724 Physical filter label
725 throughputLambda: `np.array(dtype=np.float64)`
726 Wavelength steps (Angstrom)
730 throughput: `np.array(dtype=np.float64)`
731 Throughput (max 1.0) at throughputLambda
734 c = detector.getCenter(afwCameraGeom.FOCAL_PLANE)
735 c.scale(1.0/detector.getPixelSize()[0])
738 wavelengths=throughputLambda)
741 wavelengths=throughputLambda)
743 if self.config.doFilterDetectorTransmission:
746 wavelengths=throughputLambda,
751 wavelengths=throughputLambda,
755 throughput = np.clip(throughput, 0.0, 1.0)
760 atmosphereTableName):
766 physicalFilterString: `str`
767 Combined string of all the physicalFilters
768 stdPhysicalFilterString: `str`
769 Combined string of all the standard physicalFilters
770 atmosphereTableName: `str`
771 Name of the atmosphere table used to generate LUT
775 lutSchema: `afwTable.schema`
778 lutSchema = afwTable.Schema()
780 lutSchema.addField(
'tablename', type=str, doc=
'Atmosphere table name',
781 size=len(atmosphereTableName))
782 lutSchema.addField(
'elevation', type=float, doc=
"Telescope elevation used for LUT")
783 lutSchema.addField(
'physicalFilters', type=str, doc=
'physicalFilters in LUT',
784 size=len(physicalFilterString))
785 lutSchema.addField(
'stdPhysicalFilters', type=str, doc=
'Standard physicalFilters in LUT',
786 size=len(stdPhysicalFilterString))
787 lutSchema.addField(
'pmb', type=
'ArrayD', doc=
'Barometric Pressure',
789 lutSchema.addField(
'pmbFactor', type=
'ArrayD', doc=
'PMB scaling factor',
791 lutSchema.addField(
'pmbElevation', type=np.float64, doc=
'PMB Scaling at elevation')
792 lutSchema.addField(
'pwv', type=
'ArrayD', doc=
'Preciptable Water Vapor',
794 lutSchema.addField(
'o3', type=
'ArrayD', doc=
'Ozone',
796 lutSchema.addField(
'tau', type=
'ArrayD', doc=
'Aerosol optical depth',
798 lutSchema.addField(
'lambdaNorm', type=np.float64, doc=
'AOD wavelength')
799 lutSchema.addField(
'alpha', type=
'ArrayD', doc=
'Aerosol alpha',
801 lutSchema.addField(
'zenith', type=
'ArrayD', doc=
'Zenith angle',
803 lutSchema.addField(
'nCcd', type=np.int32, doc=
'Number of CCDs')
806 lutSchema.addField(
'pmbStd', type=np.float64, doc=
'PMB Standard')
807 lutSchema.addField(
'pwvStd', type=np.float64, doc=
'PWV Standard')
808 lutSchema.addField(
'o3Std', type=np.float64, doc=
'O3 Standard')
809 lutSchema.addField(
'tauStd', type=np.float64, doc=
'Tau Standard')
810 lutSchema.addField(
'alphaStd', type=np.float64, doc=
'Alpha Standard')
811 lutSchema.addField(
'zenithStd', type=np.float64, doc=
'Zenith angle Standard')
812 lutSchema.addField(
'lambdaRange', type=
'ArrayD', doc=
'Wavelength range',
814 lutSchema.addField(
'lambdaStep', type=np.float64, doc=
'Wavelength step')
815 lutSchema.addField(
'lambdaStd', type=
'ArrayD', doc=
'Standard Wavelength',
817 lutSchema.addField(
'lambdaStdFilter', type=
'ArrayD', doc=
'Standard Wavelength (raw)',
819 lutSchema.addField(
'i0Std', type=
'ArrayD', doc=
'I0 Standard',
821 lutSchema.addField(
'i1Std', type=
'ArrayD', doc=
'I1 Standard',
823 lutSchema.addField(
'i10Std', type=
'ArrayD', doc=
'I10 Standard',
825 lutSchema.addField(
'i2Std', type=
'ArrayD', doc=
'I2 Standard',
827 lutSchema.addField(
'lambdaB', type=
'ArrayD', doc=
'Wavelength for passband (no atm)',
829 lutSchema.addField(
'atmLambda', type=
'ArrayD', doc=
'Atmosphere wavelengths (Angstrom)',
831 lutSchema.addField(
'atmStdTrans', type=
'ArrayD', doc=
'Standard Atmosphere Throughput',
835 lutSchema.addField(
'luttype', type=str, size=20, doc=
'Look-up table type')
836 lutSchema.addField(
'lut', type=
'ArrayF', doc=
'Look-up table for luttype',
841 def _makeLutCat(self, lutSchema, physicalFilterString, stdPhysicalFilterString,
842 atmosphereTableName):
848 lutSchema: `afwTable.schema`
850 physicalFilterString: `str`
851 Combined string of all the physicalFilters
852 stdPhysicalFilterString: `str`
853 Combined string of all the standard physicalFilters
854 atmosphereTableName: `str`
855 Name of the atmosphere table used to generate LUT
859 lutCat: `afwTable.BaseCatalog`
860 Look-up table catalog for persistence.
867 lutCat = afwTable.BaseCatalog(lutSchema)
868 lutCat.table.preallocate(14)
871 rec = lutCat.addNew()
873 rec[
'tablename'] = atmosphereTableName
874 rec[
'elevation'] = self.
fgcmLutMaker.atmosphereTable.elevation
875 rec[
'physicalFilters'] = physicalFilterString
876 rec[
'stdPhysicalFilters'] = stdPhysicalFilterString
897 rec[
'lambdaStdFilter'][:] = self.
fgcmLutMaker.lambdaStdFilter
906 rec[
'luttype'] =
'I0'
910 rec = lutCat.addNew()
911 rec[
'luttype'] =
'I1'
914 derivTypes = [
'D_PMB',
'D_LNPWV',
'D_O3',
'D_LNTAU',
'D_ALPHA',
'D_SECZENITH',
915 'D_PMB_I1',
'D_LNPWV_I1',
'D_O3_I1',
'D_LNTAU_I1',
'D_ALPHA_I1',
917 for derivType
in derivTypes:
918 rec = lutCat.addNew()
919 rec[
'luttype'] = derivType
920 rec[
'lut'][:] = self.
fgcmLutMaker.lutDeriv[derivType].flatten()