LSSTApplications  19.0.0-14-gb0260a2+72efe9b372,20.0.0+7927753e06,20.0.0+8829bf0056,20.0.0+995114c5d2,20.0.0+b6f4b2abd1,20.0.0+bddc4f4cbe,20.0.0-1-g253301a+8829bf0056,20.0.0-1-g2b7511a+0d71a2d77f,20.0.0-1-g5b95a8c+7461dd0434,20.0.0-12-g321c96ea+23efe4bbff,20.0.0-16-gfab17e72e+fdf35455f6,20.0.0-2-g0070d88+ba3ffc8f0b,20.0.0-2-g4dae9ad+ee58a624b3,20.0.0-2-g61b8584+5d3db074ba,20.0.0-2-gb780d76+d529cf1a41,20.0.0-2-ged6426c+226a441f5f,20.0.0-2-gf072044+8829bf0056,20.0.0-2-gf1f7952+ee58a624b3,20.0.0-20-geae50cf+e37fec0aee,20.0.0-25-g3dcad98+544a109665,20.0.0-25-g5eafb0f+ee58a624b3,20.0.0-27-g64178ef+f1f297b00a,20.0.0-3-g4cc78c6+e0676b0dc8,20.0.0-3-g8f21e14+4fd2c12c9a,20.0.0-3-gbd60e8c+187b78b4b8,20.0.0-3-gbecbe05+48431fa087,20.0.0-38-ge4adf513+a12e1f8e37,20.0.0-4-g97dc21a+544a109665,20.0.0-4-gb4befbc+087873070b,20.0.0-4-gf910f65+5d3db074ba,20.0.0-5-gdfe0fee+199202a608,20.0.0-5-gfbfe500+d529cf1a41,20.0.0-6-g64f541c+d529cf1a41,20.0.0-6-g9a5b7a1+a1cd37312e,20.0.0-68-ga3f3dda+5fca18c6a4,20.0.0-9-g4aef684+e18322736b,w.2020.45
LSSTDataManagementBasePackage
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 
25 This task computes a look-up-table for the range in expected atmosphere
26 variation and variation in instrumental throughput (as tracked by the
27 transmission_filter products). By pre-computing linearized integrals,
28 the FGCM fit is orders of magnitude faster for stars with a broad range
29 of colors and observing bands, yielding precision at the 1-2 mmag level.
30 
31 Computing a LUT requires running MODTRAN or with a pre-generated
32 atmosphere table packaged with fgcm.
33 """
34 
35 import sys
36 import traceback
37 
38 import numpy as np
39 
40 import lsst.pex.config as pexConfig
41 import lsst.pipe.base as pipeBase
42 import lsst.afw.table as afwTable
43 import lsst.afw.cameraGeom as afwCameraGeom
44 from lsst.afw.image import Filter
45 from lsst.daf.persistence import NoResults
46 
47 import fgcm
48 
49 __all__ = ['FgcmMakeLutParametersConfig', 'FgcmMakeLutConfig', 'FgcmMakeLutTask',
50  'FgcmMakeLutRunner']
51 
52 
53 class FgcmMakeLutParametersConfig(pexConfig.Config):
54  """Config for parameters if atmosphereTableName not available"""
55  # TODO: When DM-16511 is done, it will be possible to get the
56  # telescope elevation directly from the camera.
57  elevation = pexConfig.Field(
58  doc="Telescope elevation (m)",
59  dtype=float,
60  default=None,
61  )
62  pmbRange = pexConfig.ListField(
63  doc=("Barometric Pressure range (millibar) "
64  "Recommended range depends on the site."),
65  dtype=float,
66  default=None,
67  )
68  pmbSteps = pexConfig.Field(
69  doc="Barometric Pressure number of steps",
70  dtype=int,
71  default=5,
72  )
73  pwvRange = pexConfig.ListField(
74  doc=("Precipitable Water Vapor range (mm) "
75  "Recommended range depends on the site."),
76  dtype=float,
77  default=None,
78  )
79  pwvSteps = pexConfig.Field(
80  doc="Precipitable Water Vapor number of steps",
81  dtype=int,
82  default=15,
83  )
84  o3Range = pexConfig.ListField(
85  doc="Ozone range (dob)",
86  dtype=float,
87  default=[220.0, 310.0],
88  )
89  o3Steps = pexConfig.Field(
90  doc="Ozone number of steps",
91  dtype=int,
92  default=3,
93  )
94  tauRange = pexConfig.ListField(
95  doc="Aerosol Optical Depth range (unitless)",
96  dtype=float,
97  default=[0.002, 0.35],
98  )
99  tauSteps = pexConfig.Field(
100  doc="Aerosol Optical Depth number of steps",
101  dtype=int,
102  default=11,
103  )
104  alphaRange = pexConfig.ListField(
105  doc="Aerosol alpha range (unitless)",
106  dtype=float,
107  default=[0.0, 2.0],
108  )
109  alphaSteps = pexConfig.Field(
110  doc="Aerosol alpha number of steps",
111  dtype=int,
112  default=9,
113  )
114  zenithRange = pexConfig.ListField(
115  doc="Zenith angle range (degree)",
116  dtype=float,
117  default=[0.0, 70.0],
118  )
119  zenithSteps = pexConfig.Field(
120  doc="Zenith angle number of steps",
121  dtype=int,
122  default=21,
123  )
124  # Note that the standard atmosphere parameters depend on the observatory
125  # and elevation, and so these should be set on a per-camera basis.
126  pmbStd = pexConfig.Field(
127  doc=("Standard Atmosphere pressure (millibar); "
128  "Recommended default depends on the site."),
129  dtype=float,
130  default=None,
131  )
132  pwvStd = pexConfig.Field(
133  doc=("Standard Atmosphere PWV (mm); "
134  "Recommended default depends on the site."),
135  dtype=float,
136  default=None,
137  )
138  o3Std = pexConfig.Field(
139  doc="Standard Atmosphere O3 (dob)",
140  dtype=float,
141  default=263.0,
142  )
143  tauStd = pexConfig.Field(
144  doc="Standard Atmosphere aerosol optical depth",
145  dtype=float,
146  default=0.03,
147  )
148  alphaStd = pexConfig.Field(
149  doc="Standard Atmosphere aerosol alpha",
150  dtype=float,
151  default=1.0,
152  )
153  airmassStd = pexConfig.Field(
154  doc=("Standard Atmosphere airmass; "
155  "Recommended default depends on the survey strategy."),
156  dtype=float,
157  default=None,
158  )
159  lambdaNorm = pexConfig.Field(
160  doc="Aerosol Optical Depth normalization wavelength (Angstrom)",
161  dtype=float,
162  default=7750.0,
163  )
164  lambdaStep = pexConfig.Field(
165  doc="Wavelength step for generating atmospheres (nm)",
166  dtype=float,
167  default=0.5,
168  )
169  lambdaRange = pexConfig.ListField(
170  doc="Wavelength range for LUT (Angstrom)",
171  dtype=float,
172  default=[3000.0, 11000.0],
173  )
174 
175 
176 class FgcmMakeLutConfig(pexConfig.Config):
177  """Config for FgcmMakeLutTask"""
178 
179  filterNames = pexConfig.ListField(
180  doc="Filter names to build LUT ('short' names)",
181  dtype=str,
182  default=None,
183  )
184  stdFilterNames = pexConfig.ListField(
185  doc=("Standard filterNames ('short' names). "
186  "Each filter in filterName will be calibrated to a matched "
187  "stdFilterName. In regular usage, one has g->g, r->r, ... "
188  "In the case of HSC, one would have g->g, r->r2, r2->r2, ... "
189  "which allows replacement (or time-variable) filters to be "
190  "properly cross-calibrated."),
191  dtype=str,
192  default=None,
193  )
194  atmosphereTableName = pexConfig.Field(
195  doc="FGCM name or filename of precomputed atmospheres",
196  dtype=str,
197  default=None,
198  optional=True,
199  )
200  parameters = pexConfig.ConfigField(
201  doc="Atmosphere parameters (required if no atmosphereTableName)",
202  dtype=FgcmMakeLutParametersConfig,
203  default=None,
204  check=None)
205 
206  def validate(self):
207  """
208  Validate the config parameters.
209 
210  This method behaves differently from the parent validate in the case
211  that atmosphereTableName is set. In this case, the config values
212  for standard values, step sizes, and ranges are loaded
213  directly from the specified atmosphereTableName.
214  """
215  # check that filterNames and stdFilterNames are okay
216  self._fields['filterNames'].validate(self)
217  self._fields['stdFilterNames'].validate(self)
218 
219  # check if we have an atmosphereTableName, and if valid
220  if self.atmosphereTableName is not None:
221  try:
222  fgcm.FgcmAtmosphereTable.initWithTableName(self.atmosphereTableName)
223  except IOError:
224  raise RuntimeError("Could not find atmosphereTableName: %s" %
225  (self.atmosphereTableName))
226  else:
227  # Validate the parameters
228  self._fields['parameters'].validate(self)
229 
230 
231 class FgcmMakeLutRunner(pipeBase.ButlerInitializedTaskRunner):
232  """Subclass of TaskRunner for fgcmMakeLutTask
233 
234  fgcmMakeLutTask.run() takes one argument, the butler, and
235  does not run on any data in the repository.
236  This runner does not use any parallelization.
237  """
238 
239  @staticmethod
240  def getTargetList(parsedCmd):
241  """
242  Return a list with one element, the butler.
243  """
244  return [parsedCmd.butler]
245 
246  def __call__(self, butler):
247  """
248  Parameters
249  ----------
250  butler: `lsst.daf.persistence.Butler`
251 
252  Returns
253  -------
254  exitStatus: `list` with `pipeBase.Struct`
255  exitStatus (0: success; 1: failure)
256  """
257  task = self.TaskClass(config=self.config, log=self.log)
258 
259  exitStatus = 0
260  if self.doRaise:
261  task.runDataRef(butler)
262  else:
263  try:
264  task.runDataRef(butler)
265  except Exception as e:
266  exitStatus = 1
267  task.log.fatal("Failed: %s" % e)
268  if not isinstance(e, pipeBase.TaskError):
269  traceback.print_exc(file=sys.stderr)
270 
271  task.writeMetadata(butler)
272 
273  # The task does not return any results:
274  return [pipeBase.Struct(exitStatus=exitStatus)]
275 
276  def run(self, parsedCmd):
277  """
278  Run the task, with no multiprocessing
279 
280  Parameters
281  ----------
282  parsedCmd: ArgumentParser parsed command line
283  """
284 
285  resultList = []
286 
287  if self.precall(parsedCmd):
288  targetList = self.getTargetList(parsedCmd)
289  # make sure that we only get 1
290  resultList = self(targetList[0])
291 
292  return resultList
293 
294 
295 class FgcmMakeLutTask(pipeBase.CmdLineTask):
296  """
297  Make Look-Up Table for FGCM.
298 
299  This task computes a look-up-table for the range in expected atmosphere
300  variation and variation in instrumental throughput (as tracked by the
301  transmission_filter products). By pre-computing linearized integrals,
302  the FGCM fit is orders of magnitude faster for stars with a broad range
303  of colors and observing bands, yielding precision at the 1-2 mmag level.
304 
305  Computing a LUT requires running MODTRAN or with a pre-generated
306  atmosphere table packaged with fgcm.
307  """
308 
309  ConfigClass = FgcmMakeLutConfig
310  RunnerClass = FgcmMakeLutRunner
311  _DefaultName = "fgcmMakeLut"
312 
313  def __init__(self, butler=None, **kwargs):
314  """
315  Instantiate an fgcmMakeLutTask.
316 
317  Parameters
318  ----------
319  butler: `lsst.daf.persistence.Butler`
320  """
321 
322  pipeBase.CmdLineTask.__init__(self, **kwargs)
323 
324  # no saving of metadata for now
325  def _getMetadataName(self):
326  return None
327 
328  @pipeBase.timeMethod
329  def runDataRef(self, butler):
330  """
331  Make a Look-Up Table for FGCM
332 
333  Parameters
334  ----------
335  butler: `lsst.daf.persistence.Butler`
336  """
337 
338  self._fgcmMakeLut(butler)
339 
340  def _fgcmMakeLut(self, butler):
341  """
342  Make a FGCM Look-up Table
343 
344  Parameters
345  ----------
346  butler: `lsst.daf.persistence.Butler`
347  """
348 
349  # need the camera for the detectors
350  camera = butler.get('camera')
351 
352  # number of ccds from the length of the camera iterator
353  nCcd = len(camera)
354  self.log.info("Found %d ccds for look-up table" % (nCcd))
355 
356  # Load in optics, etc.
357  self._loadThroughputs(butler, camera)
358 
359  lutConfig = self._createLutConfig(nCcd)
360 
361  # make the lut object
362  self.log.info("Making the LUT maker object")
363  self.fgcmLutMaker = fgcm.FgcmLUTMaker(lutConfig)
364 
365  # generate the throughput dictionary.
366 
367  # these will be in Angstroms
368  # note that lambdaStep is currently in nm, because of historical
369  # reasons in the code. Convert to Angstroms here.
370  throughputLambda = np.arange(self.fgcmLutMaker.lambdaRange[0],
371  self.fgcmLutMaker.lambdaRange[1]+self.fgcmLutMaker.lambdaStep*10,
372  self.fgcmLutMaker.lambdaStep*10.)
373 
374  self.log.info("Built throughput lambda, %.1f-%.1f, step %.2f" %
375  (throughputLambda[0], throughputLambda[-1],
376  throughputLambda[1]-throughputLambda[0]))
377 
378  throughputDict = {}
379  for i, filterName in enumerate(self.config.filterNames):
380  tDict = {}
381  tDict['LAMBDA'] = throughputLambda
382  for ccdIndex, detector in enumerate(camera):
383  tDict[ccdIndex] = self._getThroughputDetector(detector, filterName, throughputLambda)
384  throughputDict[filterName] = tDict
385 
386  # set the throughputs
387  self.fgcmLutMaker.setThroughputs(throughputDict)
388 
389  # make the LUT
390  self.log.info("Making LUT")
391  self.fgcmLutMaker.makeLUT()
392 
393  # and save the LUT
394 
395  # build the index values
396  comma = ','
397  filterNameString = comma.join(self.config.filterNames)
398  stdFilterNameString = comma.join(self.config.stdFilterNames)
399 
400  atmosphereTableName = 'NoTableWasUsed'
401  if self.config.atmosphereTableName is not None:
402  atmosphereTableName = self.config.atmosphereTableName
403 
404  lutSchema = self._makeLutSchema(filterNameString, stdFilterNameString,
405  atmosphereTableName)
406 
407  lutCat = self._makeLutCat(lutSchema, filterNameString,
408  stdFilterNameString, atmosphereTableName)
409  butler.put(lutCat, 'fgcmLookUpTable')
410 
411  def _createLutConfig(self, nCcd):
412  """
413  Create the fgcmLut config dictionary
414 
415  Parameters
416  ----------
417  nCcd: `int`
418  Number of CCDs in the camera
419  """
420 
421  # create the common stub of the lutConfig
422  lutConfig = {}
423  lutConfig['logger'] = self.log
424  lutConfig['filterNames'] = self.config.filterNames
425  lutConfig['stdFilterNames'] = self.config.stdFilterNames
426  lutConfig['nCCD'] = nCcd
427 
428  # atmosphereTable already validated if available
429  if self.config.atmosphereTableName is not None:
430  lutConfig['atmosphereTableName'] = self.config.atmosphereTableName
431  else:
432  # use the regular paramters (also validated if needed)
433  lutConfig['elevation'] = self.config.parameters.elevation
434  lutConfig['pmbRange'] = self.config.parameters.pmbRange
435  lutConfig['pmbSteps'] = self.config.parameters.pmbSteps
436  lutConfig['pwvRange'] = self.config.parameters.pwvRange
437  lutConfig['pwvSteps'] = self.config.parameters.pwvSteps
438  lutConfig['o3Range'] = self.config.parameters.o3Range
439  lutConfig['o3Steps'] = self.config.parameters.o3Steps
440  lutConfig['tauRange'] = self.config.parameters.tauRange
441  lutConfig['tauSteps'] = self.config.parameters.tauSteps
442  lutConfig['alphaRange'] = self.config.parameters.alphaRange
443  lutConfig['alphaSteps'] = self.config.parameters.alphaSteps
444  lutConfig['zenithRange'] = self.config.parameters.zenithRange
445  lutConfig['zenithSteps'] = self.config.parameters.zenithSteps
446  lutConfig['pmbStd'] = self.config.parameters.pmbStd
447  lutConfig['pwvStd'] = self.config.parameters.pwvStd
448  lutConfig['o3Std'] = self.config.parameters.o3Std
449  lutConfig['tauStd'] = self.config.parameters.tauStd
450  lutConfig['alphaStd'] = self.config.parameters.alphaStd
451  lutConfig['airmassStd'] = self.config.parameters.airmassStd
452  lutConfig['lambdaRange'] = self.config.parameters.lambdaRange
453  lutConfig['lambdaStep'] = self.config.parameters.lambdaStep
454  lutConfig['lambdaNorm'] = self.config.parameters.lambdaNorm
455 
456  return lutConfig
457 
458  def _loadThroughputs(self, butler, camera):
459  """Internal method to load throughput data for filters
460 
461  Parameters
462  ----------
463  butler: `lsst.daf.persistence.butler.Butler`
464  A butler with the transmission info
465  camera: `lsst.afw.cameraGeom.Camera`
466  """
467 
468  self._opticsTransmission = butler.get('transmission_optics')
469  self._sensorsTransmission = {}
470  for detector in camera:
471  self._sensorsTransmission[detector.getId()] = butler.get('transmission_sensor',
472  dataId={'ccd': detector.getId()})
473  self._filtersTransmission = {}
474  for filterName in self.config.filterNames:
475  f = Filter(filterName)
476  foundTrans = False
477  # Get all possible aliases, and also try the short filterName
478  aliases = f.getAliases()
479  aliases.extend(filterName)
480  for alias in f.getAliases():
481  try:
482  self._filtersTransmission[filterName] = butler.get('transmission_filter',
483  dataId={'filter': alias})
484  foundTrans = True
485  break
486  except NoResults:
487  pass
488  if not foundTrans:
489  raise ValueError("Could not find transmission for filter %s via any alias." % (filterName))
490 
491  def _getThroughputDetector(self, detector, filterName, throughputLambda):
492  """Internal method to get throughput for a detector.
493 
494  Returns the throughput at the center of the detector for a given filter.
495 
496  Parameters
497  ----------
498  detector: `lsst.afw.cameraGeom._detector.Detector`
499  Detector on camera
500  filterName: `str`
501  Short name for filter
502  throughputLambda: `np.array(dtype=np.float64)`
503  Wavelength steps (Angstrom)
504 
505  Returns
506  -------
507  throughput: `np.array(dtype=np.float64)`
508  Throughput (max 1.0) at throughputLambda
509  """
510 
511  c = detector.getCenter(afwCameraGeom.FOCAL_PLANE)
512  c.scale(1.0/detector.getPixelSize()[0]) # Assumes x and y pixel sizes in arcsec are the same
513 
514  throughput = self._opticsTransmission.sampleAt(position=c,
515  wavelengths=throughputLambda)
516 
517  throughput *= self._sensorsTransmission[detector.getId()].sampleAt(position=c,
518  wavelengths=throughputLambda)
519 
520  throughput *= self._filtersTransmission[filterName].sampleAt(position=c,
521  wavelengths=throughputLambda)
522 
523  # Clip the throughput from 0 to 1
524  throughput = np.clip(throughput, 0.0, 1.0)
525 
526  return throughput
527 
528  def _makeLutSchema(self, filterNameString, stdFilterNameString,
529  atmosphereTableName):
530  """
531  Make the LUT schema
532 
533  Parameters
534  ----------
535  filterNameString: `str`
536  Combined string of all the filterNames
537  stdFilterNameString: `str`
538  Combined string of all the standard filterNames
539  atmosphereTableName: `str`
540  Name of the atmosphere table used to generate LUT
541 
542  Returns
543  -------
544  lutSchema: `afwTable.schema`
545  """
546 
547  lutSchema = afwTable.Schema()
548 
549  lutSchema.addField('tablename', type=str, doc='Atmosphere table name',
550  size=len(atmosphereTableName))
551  lutSchema.addField('elevation', type=float, doc="Telescope elevation used for LUT")
552  lutSchema.addField('filterNames', type=str, doc='filterNames in LUT',
553  size=len(filterNameString))
554  lutSchema.addField('stdFilterNames', type=str, doc='Standard filterNames in LUT',
555  size=len(stdFilterNameString))
556  lutSchema.addField('pmb', type='ArrayD', doc='Barometric Pressure',
557  size=self.fgcmLutMaker.pmb.size)
558  lutSchema.addField('pmbFactor', type='ArrayD', doc='PMB scaling factor',
559  size=self.fgcmLutMaker.pmb.size)
560  lutSchema.addField('pmbElevation', type=np.float64, doc='PMB Scaling at elevation')
561  lutSchema.addField('pwv', type='ArrayD', doc='Preciptable Water Vapor',
562  size=self.fgcmLutMaker.pwv.size)
563  lutSchema.addField('o3', type='ArrayD', doc='Ozone',
564  size=self.fgcmLutMaker.o3.size)
565  lutSchema.addField('tau', type='ArrayD', doc='Aerosol optical depth',
566  size=self.fgcmLutMaker.tau.size)
567  lutSchema.addField('lambdaNorm', type=np.float64, doc='AOD wavelength')
568  lutSchema.addField('alpha', type='ArrayD', doc='Aerosol alpha',
569  size=self.fgcmLutMaker.alpha.size)
570  lutSchema.addField('zenith', type='ArrayD', doc='Zenith angle',
571  size=self.fgcmLutMaker.zenith.size)
572  lutSchema.addField('nCcd', type=np.int32, doc='Number of CCDs')
573 
574  # and the standard values
575  lutSchema.addField('pmbStd', type=np.float64, doc='PMB Standard')
576  lutSchema.addField('pwvStd', type=np.float64, doc='PWV Standard')
577  lutSchema.addField('o3Std', type=np.float64, doc='O3 Standard')
578  lutSchema.addField('tauStd', type=np.float64, doc='Tau Standard')
579  lutSchema.addField('alphaStd', type=np.float64, doc='Alpha Standard')
580  lutSchema.addField('zenithStd', type=np.float64, doc='Zenith angle Standard')
581  lutSchema.addField('lambdaRange', type='ArrayD', doc='Wavelength range',
582  size=2)
583  lutSchema.addField('lambdaStep', type=np.float64, doc='Wavelength step')
584  lutSchema.addField('lambdaStd', type='ArrayD', doc='Standard Wavelength',
585  size=len(self.fgcmLutMaker.filterNames))
586  lutSchema.addField('lambdaStdFilter', type='ArrayD', doc='Standard Wavelength (raw)',
587  size=len(self.fgcmLutMaker.filterNames))
588  lutSchema.addField('i0Std', type='ArrayD', doc='I0 Standard',
589  size=len(self.fgcmLutMaker.filterNames))
590  lutSchema.addField('i1Std', type='ArrayD', doc='I1 Standard',
591  size=len(self.fgcmLutMaker.filterNames))
592  lutSchema.addField('i10Std', type='ArrayD', doc='I10 Standard',
593  size=len(self.fgcmLutMaker.filterNames))
594  lutSchema.addField('i2Std', type='ArrayD', doc='I2 Standard',
595  size=len(self.fgcmLutMaker.filterNames))
596  lutSchema.addField('lambdaB', type='ArrayD', doc='Wavelength for passband (no atm)',
597  size=len(self.fgcmLutMaker.filterNames))
598  lutSchema.addField('atmLambda', type='ArrayD', doc='Atmosphere wavelengths (Angstrom)',
599  size=self.fgcmLutMaker.atmLambda.size)
600  lutSchema.addField('atmStdTrans', type='ArrayD', doc='Standard Atmosphere Throughput',
601  size=self.fgcmLutMaker.atmStdTrans.size)
602 
603  # and the look-up-tables
604  lutSchema.addField('luttype', type=str, size=20, doc='Look-up table type')
605  lutSchema.addField('lut', type='ArrayF', doc='Look-up table for luttype',
606  size=self.fgcmLutMaker.lut['I0'].size)
607 
608  return lutSchema
609 
610  def _makeLutCat(self, lutSchema, filterNameString, stdFilterNameString,
611  atmosphereTableName):
612  """
613  Make the LUT schema
614 
615  Parameters
616  ----------
617  lutSchema: `afwTable.schema`
618  Lut catalog schema
619  filterNameString: `str`
620  Combined string of all the filterNames
621  stdFilterNameString: `str`
622  Combined string of all the standard filterNames
623  atmosphereTableName: `str`
624  Name of the atmosphere table used to generate LUT
625 
626  Returns
627  -------
628  lutCat: `afwTable.BaseCatalog`
629  Lut catalog for persistence
630  """
631 
632  # The somewhat strange format is to make sure that
633  # the rows of the afwTable do not get too large
634  # (see DM-11419)
635 
636  lutCat = afwTable.BaseCatalog(lutSchema)
637  lutCat.table.preallocate(14)
638 
639  # first fill the first index
640  rec = lutCat.addNew()
641 
642  rec['tablename'] = atmosphereTableName
643  rec['elevation'] = self.fgcmLutMaker.atmosphereTable.elevation
644  rec['filterNames'] = filterNameString
645  rec['stdFilterNames'] = stdFilterNameString
646  rec['pmb'][:] = self.fgcmLutMaker.pmb
647  rec['pmbFactor'][:] = self.fgcmLutMaker.pmbFactor
648  rec['pmbElevation'] = self.fgcmLutMaker.pmbElevation
649  rec['pwv'][:] = self.fgcmLutMaker.pwv
650  rec['o3'][:] = self.fgcmLutMaker.o3
651  rec['tau'][:] = self.fgcmLutMaker.tau
652  rec['lambdaNorm'] = self.fgcmLutMaker.lambdaNorm
653  rec['alpha'][:] = self.fgcmLutMaker.alpha
654  rec['zenith'][:] = self.fgcmLutMaker.zenith
655  rec['nCcd'] = self.fgcmLutMaker.nCCD
656 
657  rec['pmbStd'] = self.fgcmLutMaker.pmbStd
658  rec['pwvStd'] = self.fgcmLutMaker.pwvStd
659  rec['o3Std'] = self.fgcmLutMaker.o3Std
660  rec['tauStd'] = self.fgcmLutMaker.tauStd
661  rec['alphaStd'] = self.fgcmLutMaker.alphaStd
662  rec['zenithStd'] = self.fgcmLutMaker.zenithStd
663  rec['lambdaRange'][:] = self.fgcmLutMaker.lambdaRange
664  rec['lambdaStep'] = self.fgcmLutMaker.lambdaStep
665  rec['lambdaStd'][:] = self.fgcmLutMaker.lambdaStd
666  rec['lambdaStdFilter'][:] = self.fgcmLutMaker.lambdaStdFilter
667  rec['i0Std'][:] = self.fgcmLutMaker.I0Std
668  rec['i1Std'][:] = self.fgcmLutMaker.I1Std
669  rec['i10Std'][:] = self.fgcmLutMaker.I10Std
670  rec['i2Std'][:] = self.fgcmLutMaker.I2Std
671  rec['lambdaB'][:] = self.fgcmLutMaker.lambdaB
672  rec['atmLambda'][:] = self.fgcmLutMaker.atmLambda
673  rec['atmStdTrans'][:] = self.fgcmLutMaker.atmStdTrans
674 
675  rec['luttype'] = 'I0'
676  rec['lut'][:] = self.fgcmLutMaker.lut['I0'].flatten()
677 
678  # and add the rest
679  rec = lutCat.addNew()
680  rec['luttype'] = 'I1'
681  rec['lut'][:] = self.fgcmLutMaker.lut['I1'].flatten()
682 
683  derivTypes = ['D_PMB', 'D_LNPWV', 'D_O3', 'D_LNTAU', 'D_ALPHA', 'D_SECZENITH',
684  'D_PMB_I1', 'D_LNPWV_I1', 'D_O3_I1', 'D_LNTAU_I1', 'D_ALPHA_I1',
685  'D_SECZENITH_I1']
686  for derivType in derivTypes:
687  rec = lutCat.addNew()
688  rec['luttype'] = derivType
689  rec['lut'][:] = self.fgcmLutMaker.lutDeriv[derivType].flatten()
690 
691  return lutCat
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask._makeLutSchema
def _makeLutSchema(self, filterNameString, stdFilterNameString, atmosphereTableName)
Definition: fgcmMakeLut.py:528
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutConfig.atmosphereTableName
atmosphereTableName
Definition: fgcmMakeLut.py:194
lsst::afw::image
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Definition: imageAlgorithm.dox:1
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask._opticsTransmission
_opticsTransmission
Definition: fgcmMakeLut.py:468
lsst::log.log.logContinued.info
def info(fmt, *args)
Definition: logContinued.py:201
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask._getThroughputDetector
def _getThroughputDetector(self, detector, filterName, throughputLambda)
Definition: fgcmMakeLut.py:491
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutRunner.run
def run(self, parsedCmd)
Definition: fgcmMakeLut.py:276
lsst::afw::image::Filter
Holds an integer identifier for an LSST filter.
Definition: Filter.h:141
lsst::afw::table::Schema
Defines the fields and offsets for a table.
Definition: Schema.h:50
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask._fgcmMakeLut
def _fgcmMakeLut(self, butler)
Definition: fgcmMakeLut.py:340
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask._filtersTransmission
_filtersTransmission
Definition: fgcmMakeLut.py:473
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask.__init__
def __init__(self, butler=None, **kwargs)
Definition: fgcmMakeLut.py:313
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask
Definition: fgcmMakeLut.py:295
lsst.pex.config
Definition: __init__.py:1
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask._createLutConfig
def _createLutConfig(self, nCcd)
Definition: fgcmMakeLut.py:411
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutRunner.getTargetList
def getTargetList(parsedCmd)
Definition: fgcmMakeLut.py:240
lsst::afw::table
Definition: table.dox:3
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutConfig
Definition: fgcmMakeLut.py:176
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutRunner.__call__
def __call__(self, butler)
Definition: fgcmMakeLut.py:246
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask._makeLutCat
def _makeLutCat(self, lutSchema, filterNameString, stdFilterNameString, atmosphereTableName)
Definition: fgcmMakeLut.py:610
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutConfig.validate
def validate(self)
Definition: fgcmMakeLut.py:206
lsst::afw::cameraGeom
Definition: Amplifier.h:33
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask.runDataRef
def runDataRef(self, butler)
Definition: fgcmMakeLut.py:329
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutParametersConfig
Definition: fgcmMakeLut.py:53
lsst::daf::persistence
Definition: Utils.h:50
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask._sensorsTransmission
_sensorsTransmission
Definition: fgcmMakeLut.py:469
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask._loadThroughputs
def _loadThroughputs(self, butler, camera)
Definition: fgcmMakeLut.py:458
lsst.pipe.base
Definition: __init__.py:1
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutTask.fgcmLutMaker
fgcmLutMaker
Definition: fgcmMakeLut.py:363
lsst::afw::table::CatalogT< BaseRecord >
lsst.fgcmcal.fgcmMakeLut.FgcmMakeLutRunner
Definition: fgcmMakeLut.py:231