LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
fgcmMakeLut.py
Go to the documentation of this file.
1# See COPYRIGHT file at the top of the source tree.
2#
3# This file is part of fgcmcal.
4#
5# Developed for the LSST Data Management System.
6# This product includes software developed by the LSST Project
7# (https://www.lsst.org).
8# See the COPYRIGHT file at the top-level directory of this distribution
9# for details of code ownership.
10#
11# This program is free software: you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation, either version 3 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program. If not, see <https://www.gnu.org/licenses/>.
23"""Make a look-up-table (LUT) for FGCM calibration.
24
25This task computes a look-up-table for the range in expected atmosphere
26variation and variation in instrumental throughput (as tracked by the
27transmission_filter products). By pre-computing linearized integrals,
28the FGCM fit is orders of magnitude faster for stars with a broad range
29of colors and observing bands, yielding precision at the 1-2 mmag level.
30
31Computing a LUT requires running MODTRAN or with a pre-generated
32atmosphere table packaged with fgcm.
33"""
34
35import numpy as np
36
37import lsst.pex.config as pexConfig
38import lsst.pipe.base as pipeBase
39from lsst.pipe.base import connectionTypes
40import lsst.afw.table as afwTable
41import lsst.afw.cameraGeom as afwCameraGeom
42from .utilities import lookupStaticCalibrations
43
44import fgcm
45
46__all__ = ['FgcmMakeLutParametersConfig', 'FgcmMakeLutConfig', 'FgcmMakeLutTask']
47
48
49class FgcmMakeLutConnections(pipeBase.PipelineTaskConnections,
50 dimensions=('instrument',),
51 defaultTemplates={}):
52 camera = connectionTypes.PrerequisiteInput(
53 doc="Camera instrument",
54 name="camera",
55 storageClass="Camera",
56 dimensions=("instrument",),
57 lookupFunction=lookupStaticCalibrations,
58 isCalibration=True,
59 )
60
61 transmission_optics = connectionTypes.PrerequisiteInput(
62 doc="Optics transmission curve information",
63 name="transmission_optics",
64 storageClass="TransmissionCurve",
65 dimensions=("instrument",),
66 lookupFunction=lookupStaticCalibrations,
67 isCalibration=True,
68 deferLoad=True,
69 )
70
71 transmission_sensor = connectionTypes.PrerequisiteInput(
72 doc="Sensor transmission curve information",
73 name="transmission_sensor",
74 storageClass="TransmissionCurve",
75 dimensions=("instrument", "detector",),
76 lookupFunction=lookupStaticCalibrations,
77 isCalibration=True,
78 deferLoad=True,
79 multiple=True,
80 )
81
82 transmission_filter = connectionTypes.PrerequisiteInput(
83 doc="Filter transmission curve information",
84 name="transmission_filter",
85 storageClass="TransmissionCurve",
86 dimensions=("band", "instrument", "physical_filter",),
87 lookupFunction=lookupStaticCalibrations,
88 isCalibration=True,
89 deferLoad=True,
90 multiple=True,
91 )
92
93 fgcmLookUpTable = connectionTypes.Output(
94 doc=("Atmosphere + instrument look-up-table for FGCM throughput and "
95 "chromatic corrections."),
96 name="fgcmLookUpTable",
97 storageClass="Catalog",
98 dimensions=("instrument",),
99 )
100
101
102class FgcmMakeLutParametersConfig(pexConfig.Config):
103 """Config for parameters if atmosphereTableName not available"""
104 # TODO: When DM-16511 is done, it will be possible to get the
105 # telescope elevation directly from the camera.
106 elevation = pexConfig.Field(
107 doc="Telescope elevation (m)",
108 dtype=float,
109 default=None,
110 )
111 pmbRange = pexConfig.ListField(
112 doc=("Barometric Pressure range (millibar) "
113 "Recommended range depends on the site."),
114 dtype=float,
115 default=None,
116 )
117 pmbSteps = pexConfig.Field(
118 doc="Barometric Pressure number of steps",
119 dtype=int,
120 default=5,
121 )
122 pwvRange = pexConfig.ListField(
123 doc=("Precipitable Water Vapor range (mm) "
124 "Recommended range depends on the site."),
125 dtype=float,
126 default=None,
127 )
128 pwvSteps = pexConfig.Field(
129 doc="Precipitable Water Vapor number of steps",
130 dtype=int,
131 default=15,
132 )
133 o3Range = pexConfig.ListField(
134 doc="Ozone range (dob)",
135 dtype=float,
136 default=[220.0, 310.0],
137 )
138 o3Steps = pexConfig.Field(
139 doc="Ozone number of steps",
140 dtype=int,
141 default=3,
142 )
143 tauRange = pexConfig.ListField(
144 doc="Aerosol Optical Depth range (unitless)",
145 dtype=float,
146 default=[0.002, 0.35],
147 )
148 tauSteps = pexConfig.Field(
149 doc="Aerosol Optical Depth number of steps",
150 dtype=int,
151 default=11,
152 )
153 alphaRange = pexConfig.ListField(
154 doc="Aerosol alpha range (unitless)",
155 dtype=float,
156 default=[0.0, 2.0],
157 )
158 alphaSteps = pexConfig.Field(
159 doc="Aerosol alpha number of steps",
160 dtype=int,
161 default=9,
162 )
163 zenithRange = pexConfig.ListField(
164 doc="Zenith angle range (degree)",
165 dtype=float,
166 default=[0.0, 70.0],
167 )
168 zenithSteps = pexConfig.Field(
169 doc="Zenith angle number of steps",
170 dtype=int,
171 default=21,
172 )
173 # Note that the standard atmosphere parameters depend on the observatory
174 # and elevation, and so these should be set on a per-camera basis.
175 pmbStd = pexConfig.Field(
176 doc=("Standard Atmosphere pressure (millibar); "
177 "Recommended default depends on the site."),
178 dtype=float,
179 default=None,
180 )
181 pwvStd = pexConfig.Field(
182 doc=("Standard Atmosphere PWV (mm); "
183 "Recommended default depends on the site."),
184 dtype=float,
185 default=None,
186 )
187 o3Std = pexConfig.Field(
188 doc="Standard Atmosphere O3 (dob)",
189 dtype=float,
190 default=263.0,
191 )
192 tauStd = pexConfig.Field(
193 doc="Standard Atmosphere aerosol optical depth",
194 dtype=float,
195 default=0.03,
196 )
197 alphaStd = pexConfig.Field(
198 doc="Standard Atmosphere aerosol alpha",
199 dtype=float,
200 default=1.0,
201 )
202 airmassStd = pexConfig.Field(
203 doc=("Standard Atmosphere airmass; "
204 "Recommended default depends on the survey strategy."),
205 dtype=float,
206 default=None,
207 )
208 lambdaNorm = pexConfig.Field(
209 doc="Aerosol Optical Depth normalization wavelength (Angstrom)",
210 dtype=float,
211 default=7750.0,
212 )
213 lambdaStep = pexConfig.Field(
214 doc="Wavelength step for generating atmospheres (nm)",
215 dtype=float,
216 default=0.5,
217 )
218 lambdaRange = pexConfig.ListField(
219 doc="Wavelength range for LUT (Angstrom)",
220 dtype=float,
221 default=[3000.0, 11000.0],
222 )
223
224
225class FgcmMakeLutConfig(pipeBase.PipelineTaskConfig,
226 pipelineConnections=FgcmMakeLutConnections):
227 """Config for FgcmMakeLutTask"""
228 physicalFilters = pexConfig.ListField(
229 doc="List of physicalFilter labels to generate look-up table.",
230 dtype=str,
231 default=[],
232 )
233 stdPhysicalFilterOverrideMap = pexConfig.DictField(
234 doc=("Override mapping from physical filter labels to 'standard' physical "
235 "filter labels. The 'standard' physical filter defines the transmission "
236 "curve that the FGCM standard bandpass will be based on. "
237 "Any filter not listed here will be mapped to "
238 "itself (e.g. g->g or HSC-G->HSC-G). Use this override for cross-"
239 "filter calibration such as HSC-R->HSC-R2 and HSC-I->HSC-I2."),
240 keytype=str,
241 itemtype=str,
242 default={},
243 )
244 atmosphereTableName = pexConfig.Field(
245 doc="FGCM name or filename of precomputed atmospheres",
246 dtype=str,
247 default=None,
248 optional=True,
249 )
250 parameters = pexConfig.ConfigField(
251 doc="Atmosphere parameters (required if no atmosphereTableName)",
252 dtype=FgcmMakeLutParametersConfig,
253 default=None,
254 check=None)
255
256 def validate(self):
257 """
258 Validate the config parameters.
259
260 This method behaves differently from the parent validate in the case
261 that atmosphereTableName is set. In this case, the config values
262 for standard values, step sizes, and ranges are loaded
263 directly from the specified atmosphereTableName.
264 """
265 # check that filterNames and stdFilterNames are okay
266 self._fields['physicalFilters'].validate(self)
267 self._fields['stdPhysicalFilterOverrideMap'].validate(self)
268
269 if self.atmosphereTableNameatmosphereTableName is None:
270 # Validate the parameters
271 self._fields['parameters'].validate(self)
272
273
274class FgcmMakeLutTask(pipeBase.PipelineTask):
275 """
276 Make Look-Up Table for FGCM.
277
278 This task computes a look-up-table for the range in expected atmosphere
279 variation and variation in instrumental throughput (as tracked by the
280 transmission_filter products). By pre-computing linearized integrals,
281 the FGCM fit is orders of magnitude faster for stars with a broad range
282 of colors and observing bands, yielding precision at the 1-2 mmag level.
283
284 Computing a LUT requires running MODTRAN or with a pre-generated
285 atmosphere table packaged with fgcm.
286 """
287
288 ConfigClass = FgcmMakeLutConfig
289 _DefaultName = "fgcmMakeLut"
290
291 def __init__(self, butler=None, initInputs=None, **kwargs):
292 super().__init__(**kwargs)
293
294 def runQuantum(self, butlerQC, inputRefs, outputRefs):
295 camera = butlerQC.get(inputRefs.camera)
296
297 opticsHandle = butlerQC.get(inputRefs.transmission_optics)
298
299 sensorHandles = butlerQC.get(inputRefs.transmission_sensor)
300 sensorHandleDict = {sensorHandle.dataId.byName()['detector']: sensorHandle for
301 sensorHandle in sensorHandles}
302
303 filterHandles = butlerQC.get(inputRefs.transmission_filter)
304 filterHandleDict = {filterHandle.dataId['physical_filter']: filterHandle for
305 filterHandle in filterHandles}
306
307 lutCat = self._fgcmMakeLut_fgcmMakeLut(camera,
308 opticsHandle,
309 sensorHandleDict,
310 filterHandleDict)
311 butlerQC.put(lutCat, outputRefs.fgcmLookUpTable)
312
313 def _fgcmMakeLut(self, camera, opticsHandle, sensorHandleDict,
314 filterHandleDict):
315 """
316 Make a FGCM Look-up Table
317
318 Parameters
319 ----------
321 Camera from the butler.
322 opticsHandle : `lsst.daf.butler.DeferredDatasetHandle`
323 Reference to optics transmission curve.
324 sensorHandleDict : `dict` of [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
325 Dictionary of references to sensor transmission curves. Key will
326 be detector id.
327 filterHandleDict : `dict` of [`str`, `lsst.daf.butler.DeferredDatasetHandle`]
328 Dictionary of references to filter transmission curves. Key will
329 be physical filter label.
330
331 Returns
332 -------
333 fgcmLookUpTable : `BaseCatalog`
334 The FGCM look-up table.
335 """
336 # number of ccds from the length of the camera iterator
337 nCcd = len(camera)
338 self.log.info("Found %d ccds for look-up table" % (nCcd))
339
340 # Load in optics, etc.
341 self._loadThroughputs_loadThroughputs(camera,
342 opticsHandle,
343 sensorHandleDict,
344 filterHandleDict)
345
346 lutConfig = self._createLutConfig_createLutConfig(nCcd)
347
348 # make the lut object
349 self.log.info("Making the LUT maker object")
350 self.fgcmLutMakerfgcmLutMaker = fgcm.FgcmLUTMaker(lutConfig)
351
352 # generate the throughput dictionary.
353
354 # these will be in Angstroms
355 # note that lambdaStep is currently in nm, because of historical
356 # reasons in the code. Convert to Angstroms here.
357 throughputLambda = np.arange(self.fgcmLutMakerfgcmLutMaker.lambdaRange[0],
358 self.fgcmLutMakerfgcmLutMaker.lambdaRange[1]+self.fgcmLutMakerfgcmLutMaker.lambdaStep*10,
359 self.fgcmLutMakerfgcmLutMaker.lambdaStep*10.)
360
361 self.log.info("Built throughput lambda, %.1f-%.1f, step %.2f" %
362 (throughputLambda[0], throughputLambda[-1],
363 throughputLambda[1] - throughputLambda[0]))
364
365 throughputDict = {}
366 for i, physicalFilter in enumerate(self.config.physicalFilters):
367 tDict = {}
368 tDict['LAMBDA'] = throughputLambda
369 for ccdIndex, detector in enumerate(camera):
370 tDict[ccdIndex] = self._getThroughputDetector_getThroughputDetector(detector, physicalFilter, throughputLambda)
371 throughputDict[physicalFilter] = tDict
372
373 # set the throughputs
374 self.fgcmLutMakerfgcmLutMaker.setThroughputs(throughputDict)
375
376 # make the LUT
377 self.log.info("Making LUT")
378 self.fgcmLutMakerfgcmLutMaker.makeLUT()
379
380 # and save the LUT
381
382 # build the index values
383 comma = ','
384 physicalFilterString = comma.join(self.config.physicalFilters)
385 stdPhysicalFilterString = comma.join(self._getStdPhysicalFilterList_getStdPhysicalFilterList())
386
387 atmosphereTableName = 'NoTableWasUsed'
388 if self.config.atmosphereTableName is not None:
389 atmosphereTableName = self.config.atmosphereTableName
390
391 lutSchema = self._makeLutSchema_makeLutSchema(physicalFilterString, stdPhysicalFilterString,
392 atmosphereTableName)
393
394 lutCat = self._makeLutCat_makeLutCat(lutSchema, physicalFilterString,
395 stdPhysicalFilterString, atmosphereTableName)
396 return lutCat
397
398 def _getStdPhysicalFilterList(self):
399 """Get the standard physical filter lists from config.physicalFilters
400 and config.stdPhysicalFilterOverrideMap
401
402 Returns
403 -------
404 stdPhysicalFilters : `list`
405 """
406 override = self.config.stdPhysicalFilterOverrideMap
407 return [override.get(physicalFilter, physicalFilter) for
408 physicalFilter in self.config.physicalFilters]
409
410 def _createLutConfig(self, nCcd):
411 """
412 Create the fgcmLut config dictionary
413
414 Parameters
415 ----------
416 nCcd: `int`
417 Number of CCDs in the camera
418 """
419
420 # create the common stub of the lutConfig
421 lutConfig = {}
422 lutConfig['logger'] = self.log
423 lutConfig['filterNames'] = self.config.physicalFilters
424 lutConfig['stdFilterNames'] = self._getStdPhysicalFilterList_getStdPhysicalFilterList()
425 lutConfig['nCCD'] = nCcd
426
427 # atmosphereTable already validated if available
428 if self.config.atmosphereTableName is not None:
429 lutConfig['atmosphereTableName'] = self.config.atmosphereTableName
430 else:
431 # use the regular paramters (also validated if needed)
432 lutConfig['elevation'] = self.config.parameters.elevation
433 lutConfig['pmbRange'] = self.config.parameters.pmbRange
434 lutConfig['pmbSteps'] = self.config.parameters.pmbSteps
435 lutConfig['pwvRange'] = self.config.parameters.pwvRange
436 lutConfig['pwvSteps'] = self.config.parameters.pwvSteps
437 lutConfig['o3Range'] = self.config.parameters.o3Range
438 lutConfig['o3Steps'] = self.config.parameters.o3Steps
439 lutConfig['tauRange'] = self.config.parameters.tauRange
440 lutConfig['tauSteps'] = self.config.parameters.tauSteps
441 lutConfig['alphaRange'] = self.config.parameters.alphaRange
442 lutConfig['alphaSteps'] = self.config.parameters.alphaSteps
443 lutConfig['zenithRange'] = self.config.parameters.zenithRange
444 lutConfig['zenithSteps'] = self.config.parameters.zenithSteps
445 lutConfig['pmbStd'] = self.config.parameters.pmbStd
446 lutConfig['pwvStd'] = self.config.parameters.pwvStd
447 lutConfig['o3Std'] = self.config.parameters.o3Std
448 lutConfig['tauStd'] = self.config.parameters.tauStd
449 lutConfig['alphaStd'] = self.config.parameters.alphaStd
450 lutConfig['airmassStd'] = self.config.parameters.airmassStd
451 lutConfig['lambdaRange'] = self.config.parameters.lambdaRange
452 lutConfig['lambdaStep'] = self.config.parameters.lambdaStep
453 lutConfig['lambdaNorm'] = self.config.parameters.lambdaNorm
454
455 return lutConfig
456
457 def _loadThroughputs(self, camera, opticsHandle, sensorHandleDict, filterHandleDict):
458 """Internal method to load throughput data for filters
459
460 Parameters
461 ----------
463 Camera from the butler
464 opticsHandle : `lsst.daf.butler.DeferredDatasetHandle`
465 Reference to optics transmission curve.
466 sensorHandleDict : `dict` of [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
467 Dictionary of references to sensor transmission curves. Key will
468 be detector id.
469 filterHandleDict : `dict` of [`str`, `lsst.daf.butler.DeferredDatasetHandle`]
470 Dictionary of references to filter transmission curves. Key will
471 be physical filter label.
472
473 Raises
474 ------
475 ValueError : Raised if configured filter name does not match any of the
476 available filter transmission curves.
477 """
478 self._opticsTransmission_opticsTransmission = opticsHandle.get()
479
480 self._sensorsTransmission_sensorsTransmission = {}
481 for detector in camera:
482 self._sensorsTransmission_sensorsTransmission[detector.getId()] = sensorHandleDict[detector.getId()].get()
483
484 self._filtersTransmission_filtersTransmission = {}
485 for physicalFilter in self.config.physicalFilters:
486 self._filtersTransmission_filtersTransmission[physicalFilter] = filterHandleDict[physicalFilter].get()
487
488 def _getThroughputDetector(self, detector, physicalFilter, throughputLambda):
489 """Internal method to get throughput for a detector.
490
491 Returns the throughput at the center of the detector for a given filter.
492
493 Parameters
494 ----------
495 detector: `lsst.afw.cameraGeom._detector.Detector`
496 Detector on camera
497 physicalFilter: `str`
498 Physical filter label
499 throughputLambda: `np.array(dtype=np.float64)`
500 Wavelength steps (Angstrom)
501
502 Returns
503 -------
504 throughput: `np.array(dtype=np.float64)`
505 Throughput (max 1.0) at throughputLambda
506 """
507
508 c = detector.getCenter(afwCameraGeom.FOCAL_PLANE)
509 c.scale(1.0/detector.getPixelSize()[0]) # Assumes x and y pixel sizes in arcsec are the same
510
511 throughput = self._opticsTransmission_opticsTransmission.sampleAt(position=c,
512 wavelengths=throughputLambda)
513
514 throughput *= self._sensorsTransmission_sensorsTransmission[detector.getId()].sampleAt(position=c,
515 wavelengths=throughputLambda)
516
517 throughput *= self._filtersTransmission_filtersTransmission[physicalFilter].sampleAt(position=c,
518 wavelengths=throughputLambda)
519
520 # Clip the throughput from 0 to 1
521 throughput = np.clip(throughput, 0.0, 1.0)
522
523 return throughput
524
525 def _makeLutSchema(self, physicalFilterString, stdPhysicalFilterString,
526 atmosphereTableName):
527 """
528 Make the LUT schema
529
530 Parameters
531 ----------
532 physicalFilterString: `str`
533 Combined string of all the physicalFilters
534 stdPhysicalFilterString: `str`
535 Combined string of all the standard physicalFilters
536 atmosphereTableName: `str`
537 Name of the atmosphere table used to generate LUT
538
539 Returns
540 -------
541 lutSchema: `afwTable.schema`
542 """
543
544 lutSchema = afwTable.Schema()
545
546 lutSchema.addField('tablename', type=str, doc='Atmosphere table name',
547 size=len(atmosphereTableName))
548 lutSchema.addField('elevation', type=float, doc="Telescope elevation used for LUT")
549 lutSchema.addField('physicalFilters', type=str, doc='physicalFilters in LUT',
550 size=len(physicalFilterString))
551 lutSchema.addField('stdPhysicalFilters', type=str, doc='Standard physicalFilters in LUT',
552 size=len(stdPhysicalFilterString))
553 lutSchema.addField('pmb', type='ArrayD', doc='Barometric Pressure',
554 size=self.fgcmLutMakerfgcmLutMaker.pmb.size)
555 lutSchema.addField('pmbFactor', type='ArrayD', doc='PMB scaling factor',
556 size=self.fgcmLutMakerfgcmLutMaker.pmb.size)
557 lutSchema.addField('pmbElevation', type=np.float64, doc='PMB Scaling at elevation')
558 lutSchema.addField('pwv', type='ArrayD', doc='Preciptable Water Vapor',
559 size=self.fgcmLutMakerfgcmLutMaker.pwv.size)
560 lutSchema.addField('o3', type='ArrayD', doc='Ozone',
561 size=self.fgcmLutMakerfgcmLutMaker.o3.size)
562 lutSchema.addField('tau', type='ArrayD', doc='Aerosol optical depth',
563 size=self.fgcmLutMakerfgcmLutMaker.tau.size)
564 lutSchema.addField('lambdaNorm', type=np.float64, doc='AOD wavelength')
565 lutSchema.addField('alpha', type='ArrayD', doc='Aerosol alpha',
566 size=self.fgcmLutMakerfgcmLutMaker.alpha.size)
567 lutSchema.addField('zenith', type='ArrayD', doc='Zenith angle',
568 size=self.fgcmLutMakerfgcmLutMaker.zenith.size)
569 lutSchema.addField('nCcd', type=np.int32, doc='Number of CCDs')
570
571 # and the standard values
572 lutSchema.addField('pmbStd', type=np.float64, doc='PMB Standard')
573 lutSchema.addField('pwvStd', type=np.float64, doc='PWV Standard')
574 lutSchema.addField('o3Std', type=np.float64, doc='O3 Standard')
575 lutSchema.addField('tauStd', type=np.float64, doc='Tau Standard')
576 lutSchema.addField('alphaStd', type=np.float64, doc='Alpha Standard')
577 lutSchema.addField('zenithStd', type=np.float64, doc='Zenith angle Standard')
578 lutSchema.addField('lambdaRange', type='ArrayD', doc='Wavelength range',
579 size=2)
580 lutSchema.addField('lambdaStep', type=np.float64, doc='Wavelength step')
581 lutSchema.addField('lambdaStd', type='ArrayD', doc='Standard Wavelength',
582 size=len(self.fgcmLutMakerfgcmLutMaker.filterNames))
583 lutSchema.addField('lambdaStdFilter', type='ArrayD', doc='Standard Wavelength (raw)',
584 size=len(self.fgcmLutMakerfgcmLutMaker.filterNames))
585 lutSchema.addField('i0Std', type='ArrayD', doc='I0 Standard',
586 size=len(self.fgcmLutMakerfgcmLutMaker.filterNames))
587 lutSchema.addField('i1Std', type='ArrayD', doc='I1 Standard',
588 size=len(self.fgcmLutMakerfgcmLutMaker.filterNames))
589 lutSchema.addField('i10Std', type='ArrayD', doc='I10 Standard',
590 size=len(self.fgcmLutMakerfgcmLutMaker.filterNames))
591 lutSchema.addField('i2Std', type='ArrayD', doc='I2 Standard',
592 size=len(self.fgcmLutMakerfgcmLutMaker.filterNames))
593 lutSchema.addField('lambdaB', type='ArrayD', doc='Wavelength for passband (no atm)',
594 size=len(self.fgcmLutMakerfgcmLutMaker.filterNames))
595 lutSchema.addField('atmLambda', type='ArrayD', doc='Atmosphere wavelengths (Angstrom)',
596 size=self.fgcmLutMakerfgcmLutMaker.atmLambda.size)
597 lutSchema.addField('atmStdTrans', type='ArrayD', doc='Standard Atmosphere Throughput',
598 size=self.fgcmLutMakerfgcmLutMaker.atmStdTrans.size)
599
600 # and the look-up-tables
601 lutSchema.addField('luttype', type=str, size=20, doc='Look-up table type')
602 lutSchema.addField('lut', type='ArrayF', doc='Look-up table for luttype',
603 size=self.fgcmLutMakerfgcmLutMaker.lut['I0'].size)
604
605 return lutSchema
606
607 def _makeLutCat(self, lutSchema, physicalFilterString, stdPhysicalFilterString,
608 atmosphereTableName):
609 """
610 Make the LUT schema
611
612 Parameters
613 ----------
614 lutSchema: `afwTable.schema`
615 Lut catalog schema
616 physicalFilterString: `str`
617 Combined string of all the physicalFilters
618 stdPhysicalFilterString: `str`
619 Combined string of all the standard physicalFilters
620 atmosphereTableName: `str`
621 Name of the atmosphere table used to generate LUT
622
623 Returns
624 -------
625 lutCat: `afwTable.BaseCatalog`
626 Lut catalog for persistence
627 """
628
629 # The somewhat strange format is to make sure that
630 # the rows of the afwTable do not get too large
631 # (see DM-11419)
632
633 lutCat = afwTable.BaseCatalog(lutSchema)
634 lutCat.table.preallocate(14)
635
636 # first fill the first index
637 rec = lutCat.addNew()
638
639 rec['tablename'] = atmosphereTableName
640 rec['elevation'] = self.fgcmLutMakerfgcmLutMaker.atmosphereTable.elevation
641 rec['physicalFilters'] = physicalFilterString
642 rec['stdPhysicalFilters'] = stdPhysicalFilterString
643 rec['pmb'][:] = self.fgcmLutMakerfgcmLutMaker.pmb
644 rec['pmbFactor'][:] = self.fgcmLutMakerfgcmLutMaker.pmbFactor
645 rec['pmbElevation'] = self.fgcmLutMakerfgcmLutMaker.pmbElevation
646 rec['pwv'][:] = self.fgcmLutMakerfgcmLutMaker.pwv
647 rec['o3'][:] = self.fgcmLutMakerfgcmLutMaker.o3
648 rec['tau'][:] = self.fgcmLutMakerfgcmLutMaker.tau
649 rec['lambdaNorm'] = self.fgcmLutMakerfgcmLutMaker.lambdaNorm
650 rec['alpha'][:] = self.fgcmLutMakerfgcmLutMaker.alpha
651 rec['zenith'][:] = self.fgcmLutMakerfgcmLutMaker.zenith
652 rec['nCcd'] = self.fgcmLutMakerfgcmLutMaker.nCCD
653
654 rec['pmbStd'] = self.fgcmLutMakerfgcmLutMaker.pmbStd
655 rec['pwvStd'] = self.fgcmLutMakerfgcmLutMaker.pwvStd
656 rec['o3Std'] = self.fgcmLutMakerfgcmLutMaker.o3Std
657 rec['tauStd'] = self.fgcmLutMakerfgcmLutMaker.tauStd
658 rec['alphaStd'] = self.fgcmLutMakerfgcmLutMaker.alphaStd
659 rec['zenithStd'] = self.fgcmLutMakerfgcmLutMaker.zenithStd
660 rec['lambdaRange'][:] = self.fgcmLutMakerfgcmLutMaker.lambdaRange
661 rec['lambdaStep'] = self.fgcmLutMakerfgcmLutMaker.lambdaStep
662 rec['lambdaStd'][:] = self.fgcmLutMakerfgcmLutMaker.lambdaStd
663 rec['lambdaStdFilter'][:] = self.fgcmLutMakerfgcmLutMaker.lambdaStdFilter
664 rec['i0Std'][:] = self.fgcmLutMakerfgcmLutMaker.I0Std
665 rec['i1Std'][:] = self.fgcmLutMakerfgcmLutMaker.I1Std
666 rec['i10Std'][:] = self.fgcmLutMakerfgcmLutMaker.I10Std
667 rec['i2Std'][:] = self.fgcmLutMakerfgcmLutMaker.I2Std
668 rec['lambdaB'][:] = self.fgcmLutMakerfgcmLutMaker.lambdaB
669 rec['atmLambda'][:] = self.fgcmLutMakerfgcmLutMaker.atmLambda
670 rec['atmStdTrans'][:] = self.fgcmLutMakerfgcmLutMaker.atmStdTrans
671
672 rec['luttype'] = 'I0'
673 rec['lut'][:] = self.fgcmLutMakerfgcmLutMaker.lut['I0'].flatten()
674
675 # and add the rest
676 rec = lutCat.addNew()
677 rec['luttype'] = 'I1'
678 rec['lut'][:] = self.fgcmLutMakerfgcmLutMaker.lut['I1'].flatten()
679
680 derivTypes = ['D_PMB', 'D_LNPWV', 'D_O3', 'D_LNTAU', 'D_ALPHA', 'D_SECZENITH',
681 'D_PMB_I1', 'D_LNPWV_I1', 'D_O3_I1', 'D_LNTAU_I1', 'D_ALPHA_I1',
682 'D_SECZENITH_I1']
683 for derivType in derivTypes:
684 rec = lutCat.addNew()
685 rec['luttype'] = derivType
686 rec['lut'][:] = self.fgcmLutMakerfgcmLutMaker.lutDeriv[derivType].flatten()
687
688 return lutCat
An immutable representation of a camera.
Definition: Camera.h:43
Defines the fields and offsets for a table.
Definition: Schema.h:51
def _makeLutCat(self, lutSchema, physicalFilterString, stdPhysicalFilterString, atmosphereTableName)
Definition: fgcmMakeLut.py:608
def _fgcmMakeLut(self, camera, opticsHandle, sensorHandleDict, filterHandleDict)
Definition: fgcmMakeLut.py:314
def _loadThroughputs(self, camera, opticsHandle, sensorHandleDict, filterHandleDict)
Definition: fgcmMakeLut.py:457
def _makeLutSchema(self, physicalFilterString, stdPhysicalFilterString, atmosphereTableName)
Definition: fgcmMakeLut.py:526
def _getThroughputDetector(self, detector, physicalFilter, throughputLambda)
Definition: fgcmMakeLut.py:488
def runQuantum(self, butlerQC, inputRefs, outputRefs)
Definition: fgcmMakeLut.py:294
def __init__(self, butler=None, initInputs=None, **kwargs)
Definition: fgcmMakeLut.py:291