lsst.ip.isr  21.0.0-15-gfb99f82+9e2f6ff707
calibType.py
Go to the documentation of this file.
1 # This file is part of ip_isr.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (https://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 import abc
22 import datetime
23 import os.path
24 import warnings
25 import yaml
26 from astropy.table import Table
27 from astropy.io import fits
28 
29 from lsst.log import Log
30 from lsst.daf.base import PropertyList
31 
32 
33 __all__ = ["IsrCalib", "IsrProvenance"]
34 
35 
36 class IsrCalib(abc.ABC):
37  """Generic calibration type.
38 
39  Subclasses must implement the toDict, fromDict, toTable, fromTable
40  methods that allow the calibration information to be converted
41  from dictionaries and afw tables. This will allow the calibration
42  to be persisted using the base class read/write methods.
43 
44  The validate method is intended to provide a common way to check
45  that the calibration is valid (internally consistent) and
46  appropriate (usable with the intended data). The apply method is
47  intended to allow the calibration to be applied in a consistent
48  manner.
49 
50  Parameters
51  ----------
52  camera : `lsst.afw.cameraGeom.Camera`, optional
53  Camera to extract metadata from.
54  detector : `lsst.afw.cameraGeom.Detector`, optional
55  Detector to extract metadata from.
56  log : `lsst.log.Log`, optional
57  Log for messages.
58  """
59  _OBSTYPE = 'generic'
60  _SCHEMA = 'NO SCHEMA'
61  _VERSION = 0
62 
63  def __init__(self, camera=None, detector=None, log=None, **kwargs):
64  self._instrument_instrument = None
65  self._raftName_raftName = None
66  self._slotName_slotName = None
67  self._detectorName_detectorName = None
68  self._detectorSerial_detectorSerial = None
69  self._detectorId_detectorId = None
70  self._filter_filter = None
71  self._calibId_calibId = None
72  self._metadata_metadata = PropertyList()
73  self.setMetadatasetMetadata(PropertyList())
74  self.calibInfoFromDictcalibInfoFromDict(kwargs)
75 
76  # Define the required attributes for this calibration.
77  self.requiredAttributesrequiredAttributesrequiredAttributesrequiredAttributes = set(['_OBSTYPE', '_SCHEMA', '_VERSION'])
78  self.requiredAttributesrequiredAttributesrequiredAttributesrequiredAttributes.update(['_instrument', '_raftName', '_slotName',
79  '_detectorName', '_detectorSerial', '_detectorId',
80  '_filter', '_calibId', '_metadata'])
81 
82  self.loglog = log if log else Log.getLogger(__name__.partition(".")[2])
83 
84  if detector:
85  self.fromDetectorfromDetector(detector)
86  self.updateMetadataupdateMetadata(camera=camera, detector=detector)
87 
88  def __str__(self):
89  return f"{self.__class__.__name__}(obstype={self._OBSTYPE}, detector={self._detectorName}, )"
90 
91  def __eq__(self, other):
92  """Calibration equivalence.
93 
94  Subclasses will need to check specific sub-properties. The
95  default is only to check common entries.
96  """
97  if not isinstance(other, self.__class__):
98  return False
99 
100  for attr in self._requiredAttributes_requiredAttributes:
101  if getattr(self, attr) != getattr(other, attr):
102  return False
103 
104  return True
105 
106  @property
108  return self._requiredAttributes_requiredAttributes
109 
110  @requiredAttributes.setter
111  def requiredAttributes(self, value):
112  self._requiredAttributes_requiredAttributes = value
113 
114  def getMetadata(self):
115  """Retrieve metadata associated with this calibration.
116 
117  Returns
118  -------
119  meta : `lsst.daf.base.PropertyList`
120  Metadata. The returned `~lsst.daf.base.PropertyList` can be
121  modified by the caller and the changes will be written to
122  external files.
123  """
124  return self._metadata_metadata
125 
126  def setMetadata(self, metadata):
127  """Store a copy of the supplied metadata with this calibration.
128 
129  Parameters
130  ----------
131  metadata : `lsst.daf.base.PropertyList`
132  Metadata to associate with the calibration. Will be copied and
133  overwrite existing metadata.
134  """
135  if metadata is not None:
136  self._metadata_metadata.update(metadata)
137 
138  # Ensure that we have the obs type required by calibration ingest
139  self._metadata_metadata["OBSTYPE"] = self._OBSTYPE_OBSTYPE
140  self._metadata_metadata[self._OBSTYPE_OBSTYPE + "_SCHEMA"] = self._SCHEMA_SCHEMA
141  self._metadata_metadata[self._OBSTYPE_OBSTYPE + "_VERSION"] = self._VERSION_VERSION
142 
143  if isinstance(metadata, dict):
144  self.calibInfoFromDictcalibInfoFromDict(metadata)
145  elif isinstance(metadata, PropertyList):
146  self.calibInfoFromDictcalibInfoFromDict(metadata.toDict())
147 
148  def updateMetadata(self, camera=None, detector=None, filterName=None,
149  setCalibId=False, setCalibInfo=False, setDate=False,
150  **kwargs):
151  """Update metadata keywords with new values.
152 
153  Parameters
154  ----------
155  camera : `lsst.afw.cameraGeom.Camera`, optional
156  Reference camera to use to set _instrument field.
157  detector : `lsst.afw.cameraGeom.Detector`, optional
158  Reference detector to use to set _detector* fields.
159  filterName : `str`, optional
160  Filter name to assign to this calibration.
161  setCalibId : `bool`, optional
162  Construct the _calibId field from other fields.
163  setCalibInfo : `bool`, optional
164  Set calibration parameters from metadata.
165  setDate : `bool`, optional
166  Ensure the metadata CALIBDATE fields are set to the current datetime.
167  kwargs : `dict` or `collections.abc.Mapping`, optional
168  Set of key=value pairs to assign to the metadata.
169  """
170  mdOriginal = self.getMetadatagetMetadata()
171  mdSupplemental = dict()
172 
173  for k, v in kwargs.items():
174  if isinstance(v, fits.card.Undefined):
175  kwargs[k] = None
176 
177  if setCalibInfo:
178  self.calibInfoFromDictcalibInfoFromDict(kwargs)
179 
180  if camera:
181  self._instrument_instrument = camera.getName()
182 
183  if detector:
184  self._detectorName_detectorName = detector.getName()
185  self._detectorSerial_detectorSerial = detector.getSerial()
186  self._detectorId_detectorId = detector.getId()
187  if "_" in self._detectorName_detectorName:
188  (self._raftName_raftName, self._slotName_slotName) = self._detectorName_detectorName.split("_")
189 
190  if filterName:
191  # TOD0 DM-28093: I think this whole comment can go away, if we
192  # always use physicalLabel everywhere in ip_isr.
193  # If set via:
194  # exposure.getInfo().getFilter().getName()
195  # then this will hold the abstract filter.
196  self._filter_filter = filterName
197 
198  if setDate:
199  date = datetime.datetime.now()
200  mdSupplemental['CALIBDATE'] = date.isoformat()
201  mdSupplemental['CALIB_CREATION_DATE'] = date.date().isoformat()
202  mdSupplemental['CALIB_CREATION_TIME'] = date.time().isoformat()
203 
204  if setCalibId:
205  values = []
206  values.append(f"instrument={self._instrument}") if self._instrument_instrument else None
207  values.append(f"raftName={self._raftName}") if self._raftName_raftName else None
208  values.append(f"detectorName={self._detectorName}") if self._detectorName_detectorName else None
209  values.append(f"detector={self._detectorId}") if self._detectorId_detectorId else None
210  values.append(f"filter={self._filter}") if self._filter_filter else None
211 
212  calibDate = mdOriginal.get('CALIBDATE', mdSupplemental.get('CALIBDATE', None))
213  values.append(f"calibDate={calibDate}") if calibDate else None
214 
215  self._calibId_calibId = " ".join(values)
216 
217  self._metadata_metadata["INSTRUME"] = self._instrument_instrument if self._instrument_instrument else None
218  self._metadata_metadata["RAFTNAME"] = self._raftName_raftName if self._raftName_raftName else None
219  self._metadata_metadata["SLOTNAME"] = self._slotName_slotName if self._slotName_slotName else None
220  self._metadata_metadata["DETECTOR"] = self._detectorId_detectorId
221  self._metadata_metadata["DET_NAME"] = self._detectorName_detectorName if self._detectorName_detectorName else None
222  self._metadata_metadata["DET_SER"] = self._detectorSerial_detectorSerial if self._detectorSerial_detectorSerial else None
223  self._metadata_metadata["FILTER"] = self._filter_filter if self._filter_filter else None
224  self._metadata_metadata["CALIB_ID"] = self._calibId_calibId if self._calibId_calibId else None
225 
226  mdSupplemental.update(kwargs)
227  mdOriginal.update(mdSupplemental)
228 
229  def calibInfoFromDict(self, dictionary):
230  """Handle common keywords.
231 
232  This isn't an ideal solution, but until all calibrations
233  expect to find everything in the metadata, they still need to
234  search through dictionaries.
235 
236  Parameters
237  ----------
238  dictionary : `dict` or `lsst.daf.base.PropertyList`
239  Source for the common keywords.
240 
241  Raises
242  ------
243  RuntimeError :
244  Raised if the dictionary does not match the expected OBSTYPE.
245 
246  """
247 
248  def search(haystack, needles):
249  """Search dictionary 'haystack' for an entry in 'needles'
250  """
251  test = [haystack.get(x) for x in needles]
252  test = set([x for x in test if x is not None])
253  if len(test) == 0:
254  if 'metadata' in haystack:
255  return search(haystack['metadata'], needles)
256  else:
257  return None
258  elif len(test) == 1:
259  value = list(test)[0]
260  if value == '':
261  return None
262  else:
263  return value
264  else:
265  raise ValueError(f"Too many values found: {len(test)} {test} {needles}")
266 
267  if 'metadata' in dictionary:
268  metadata = dictionary['metadata']
269 
270  if self._OBSTYPE_OBSTYPE != metadata['OBSTYPE']:
271  raise RuntimeError(f"Incorrect calibration supplied. Expected {self._OBSTYPE}, "
272  f"found {metadata['OBSTYPE']}")
273 
274  self._instrument_instrument = search(dictionary, ['INSTRUME', 'instrument'])
275  self._raftName_raftName = search(dictionary, ['RAFTNAME'])
276  self._slotName_slotName = search(dictionary, ['SLOTNAME'])
277  self._detectorId_detectorId = search(dictionary, ['DETECTOR', 'detectorId'])
278  self._detectorName_detectorName = search(dictionary, ['DET_NAME', 'DETECTOR_NAME', 'detectorName'])
279  self._detectorSerial_detectorSerial = search(dictionary, ['DET_SER', 'DETECTOR_SERIAL', 'detectorSerial'])
280  self._filter_filter = search(dictionary, ['FILTER', 'filterName'])
281  self._calibId_calibId = search(dictionary, ['CALIB_ID'])
282 
283  @classmethod
284  def readText(cls, filename, **kwargs):
285  """Read calibration representation from a yaml/ecsv file.
286 
287  Parameters
288  ----------
289  filename : `str`
290  Name of the file containing the calibration definition.
291  kwargs : `dict` or collections.abc.Mapping`, optional
292  Set of key=value pairs to pass to the ``fromDict`` or
293  ``fromTable`` methods.
294 
295  Returns
296  -------
297  calib : `~lsst.ip.isr.IsrCalibType`
298  Calibration class.
299 
300  Raises
301  ------
302  RuntimeError :
303  Raised if the filename does not end in ".ecsv" or ".yaml".
304  """
305  if filename.endswith((".ecsv", ".ECSV")):
306  data = Table.read(filename, format='ascii.ecsv')
307  return cls.fromTablefromTable([data], **kwargs)
308 
309  elif filename.endswith((".yaml", ".YAML")):
310  with open(filename, 'r') as f:
311  data = yaml.load(f, Loader=yaml.CLoader)
312  return cls.fromDictfromDict(data, **kwargs)
313  else:
314  raise RuntimeError(f"Unknown filename extension: {filename}")
315 
316  def writeText(self, filename, format='auto'):
317  """Write the calibration data to a text file.
318 
319  Parameters
320  ----------
321  filename : `str`
322  Name of the file to write.
323  format : `str`
324  Format to write the file as. Supported values are:
325  ``"auto"`` : Determine filetype from filename.
326  ``"yaml"`` : Write as yaml.
327  ``"ecsv"`` : Write as ecsv.
328  Returns
329  -------
330  used : `str`
331  The name of the file used to write the data. This may
332  differ from the input if the format is explicitly chosen.
333 
334  Raises
335  ------
336  RuntimeError :
337  Raised if filename does not end in a known extension, or
338  if all information cannot be written.
339 
340  Notes
341  -----
342  The file is written to YAML/ECSV format and will include any
343  associated metadata.
344 
345  """
346  if format == 'yaml' or (format == 'auto' and filename.lower().endswith((".yaml", ".YAML"))):
347  outDict = self.toDicttoDict()
348  path, ext = os.path.splitext(filename)
349  filename = path + ".yaml"
350  with open(filename, 'w') as f:
351  yaml.dump(outDict, f)
352  elif format == 'ecsv' or (format == 'auto' and filename.lower().endswith((".ecsv", ".ECSV"))):
353  tableList = self.toTabletoTable()
354  if len(tableList) > 1:
355  # ECSV doesn't support multiple tables per file, so we
356  # can only write the first table.
357  raise RuntimeError(f"Unable to persist {len(tableList)}tables in ECSV format.")
358 
359  table = tableList[0]
360  path, ext = os.path.splitext(filename)
361  filename = path + ".ecsv"
362  table.write(filename, format="ascii.ecsv")
363  else:
364  raise RuntimeError(f"Attempt to write to a file {filename} "
365  "that does not end in '.yaml' or '.ecsv'")
366 
367  return filename
368 
369  @classmethod
370  def readFits(cls, filename, **kwargs):
371  """Read calibration data from a FITS file.
372 
373  Parameters
374  ----------
375  filename : `str`
376  Filename to read data from.
377  kwargs : `dict` or collections.abc.Mapping`, optional
378  Set of key=value pairs to pass to the ``fromTable``
379  method.
380 
381  Returns
382  -------
383  calib : `lsst.ip.isr.IsrCalib`
384  Calibration contained within the file.
385  """
386  tableList = []
387  tableList.append(Table.read(filename, hdu=1))
388  extNum = 2 # Fits indices start at 1, we've read one already.
389  keepTrying = True
390 
391  while keepTrying:
392  with warnings.catch_warnings():
393  warnings.simplefilter("error")
394  try:
395  newTable = Table.read(filename, hdu=extNum)
396  tableList.append(newTable)
397  extNum += 1
398  except Exception:
399  keepTrying = False
400 
401  for table in tableList:
402  for k, v in table.meta.items():
403  if isinstance(v, fits.card.Undefined):
404  table.meta[k] = None
405 
406  return cls.fromTablefromTable(tableList, **kwargs)
407 
408  def writeFits(self, filename):
409  """Write calibration data to a FITS file.
410 
411  Parameters
412  ----------
413  filename : `str`
414  Filename to write data to.
415 
416  Returns
417  -------
418  used : `str`
419  The name of the file used to write the data.
420 
421  """
422  tableList = self.toTabletoTable()
423  with warnings.catch_warnings():
424  warnings.filterwarnings("ignore", category=Warning, module="astropy.io")
425  astropyList = [fits.table_to_hdu(table) for table in tableList]
426  astropyList.insert(0, fits.PrimaryHDU())
427 
428  writer = fits.HDUList(astropyList)
429  writer.writeto(filename, overwrite=True)
430  return filename
431 
432  def fromDetector(self, detector):
433  """Modify the calibration parameters to match the supplied detector.
434 
435  Parameters
436  ----------
437  detector : `lsst.afw.cameraGeom.Detector`
438  Detector to use to set parameters from.
439 
440  Raises
441  ------
442  NotImplementedError
443  This needs to be implemented by subclasses for each
444  calibration type.
445  """
446  raise NotImplementedError("Must be implemented by subclass.")
447 
448  @classmethod
449  def fromDict(cls, dictionary, **kwargs):
450  """Construct a calibration from a dictionary of properties.
451 
452  Must be implemented by the specific calibration subclasses.
453 
454  Parameters
455  ----------
456  dictionary : `dict`
457  Dictionary of properties.
458  kwargs : `dict` or collections.abc.Mapping`, optional
459  Set of key=value options.
460 
461  Returns
462  ------
463  calib : `lsst.ip.isr.CalibType`
464  Constructed calibration.
465 
466  Raises
467  ------
468  NotImplementedError :
469  Raised if not implemented.
470  """
471  raise NotImplementedError("Must be implemented by subclass.")
472 
473  def toDict(self):
474  """Return a dictionary containing the calibration properties.
475 
476  The dictionary should be able to be round-tripped through
477  `fromDict`.
478 
479  Returns
480  -------
481  dictionary : `dict`
482  Dictionary of properties.
483 
484  Raises
485  ------
486  NotImplementedError :
487  Raised if not implemented.
488  """
489  raise NotImplementedError("Must be implemented by subclass.")
490 
491  @classmethod
492  def fromTable(cls, tableList, **kwargs):
493  """Construct a calibration from a dictionary of properties.
494 
495  Must be implemented by the specific calibration subclasses.
496 
497  Parameters
498  ----------
499  tableList : `list` [`lsst.afw.table.Table`]
500  List of tables of properties.
501  kwargs : `dict` or collections.abc.Mapping`, optional
502  Set of key=value options.
503 
504  Returns
505  ------
506  calib : `lsst.ip.isr.CalibType`
507  Constructed calibration.
508 
509  Raises
510  ------
511  NotImplementedError :
512  Raised if not implemented.
513  """
514  raise NotImplementedError("Must be implemented by subclass.")
515 
516  def toTable(self):
517  """Return a list of tables containing the calibration properties.
518 
519  The table list should be able to be round-tripped through
520  `fromDict`.
521 
522  Returns
523  -------
524  tableList : `list` [`lsst.afw.table.Table`]
525  List of tables of properties.
526 
527  Raises
528  ------
529  NotImplementedError :
530  Raised if not implemented.
531  """
532  raise NotImplementedError("Must be implemented by subclass.")
533 
534  def validate(self, other=None):
535  """Validate that this calibration is defined and can be used.
536 
537  Parameters
538  ----------
539  other : `object`, optional
540  Thing to validate against.
541 
542  Returns
543  -------
544  valid : `bool`
545  Returns true if the calibration is valid and appropriate.
546  """
547  return False
548 
549  def apply(self, target):
550  """Method to apply the calibration to the target object.
551 
552  Parameters
553  ----------
554  target : `object`
555  Thing to validate against.
556 
557  Returns
558  -------
559  valid : `bool`
560  Returns true if the calibration was applied correctly.
561 
562  Raises
563  ------
564  NotImplementedError :
565  Raised if not implemented.
566  """
567  raise NotImplementedError("Must be implemented by subclass.")
568 
569 
571  """Class for the provenance of data used to construct calibration.
572 
573  Provenance is not really a calibration, but we would like to
574  record this when constructing the calibration, and it provides an
575  example of the base calibration class.
576 
577  Parameters
578  ----------
579  instrument : `str`, optional
580  Name of the instrument the data was taken with.
581  calibType : `str`, optional
582  Type of calibration this provenance was generated for.
583  detectorName : `str`, optional
584  Name of the detector this calibration is for.
585  detectorSerial : `str`, optional
586  Identifier for the detector.
587 
588  """
589  _OBSTYPE = 'IsrProvenance'
590 
591  def __init__(self, calibType="unknown",
592  **kwargs):
593  self.calibTypecalibType = calibType
594  self.dimensionsdimensions = set()
595  self.dataIdListdataIdList = list()
596 
597  super().__init__(**kwargs)
598 
599  self.requiredAttributesrequiredAttributesrequiredAttributesrequiredAttributes.update(['calibType', 'dimensions', 'dataIdList'])
600 
601  def __str__(self):
602  return f"{self.__class__.__name__}(obstype={self._OBSTYPE}, calibType={self.calibType}, )"
603 
604  def __eq__(self, other):
605  return super().__eq__(other)
606 
607  def updateMetadata(self, setDate=False, **kwargs):
608  """Update calibration metadata.
609 
610  Parameters
611  ----------
612  setDate : `bool, optional
613  Update the CALIBDATE fields in the metadata to the current
614  time. Defaults to False.
615  kwargs : `dict` or `collections.abc.Mapping`, optional
616  Other keyword parameters to set in the metadata.
617  """
618  kwargs['calibType'] = self.calibTypecalibType
619  super().updateMetadata(setDate=setDate, **kwargs)
620 
621  def fromDataIds(self, dataIdList):
622  """Update provenance from dataId List.
623 
624  Parameters
625  ----------
626  dataIdList : `list` [`lsst.daf.butler.DataId`]
627  List of dataIds used in generating this calibration.
628  """
629  for dataId in dataIdList:
630  for key in dataId:
631  if key not in self.dimensionsdimensions:
632  self.dimensionsdimensions.add(key)
633  self.dataIdListdataIdList.append(dataId)
634 
635  @classmethod
636  def fromTable(cls, tableList):
637  """Construct provenance from table list.
638 
639  Parameters
640  ----------
641  tableList : `list` [`lsst.afw.table.Table`]
642  List of tables to construct the provenance from.
643 
644  Returns
645  -------
646  provenance : `lsst.ip.isr.IsrProvenance`
647  The provenance defined in the tables.
648  """
649  table = tableList[0]
650  metadata = table.meta
651  inDict = dict()
652  inDict['metadata'] = metadata
653  inDict['calibType'] = metadata['calibType']
654  inDict['dimensions'] = set()
655  inDict['dataIdList'] = list()
656 
657  schema = dict()
658  for colName in table.columns:
659  schema[colName.lower()] = colName
660  inDict['dimensions'].add(colName.lower())
661  inDict['dimensions'] = sorted(inDict['dimensions'])
662 
663  for row in table:
664  entry = dict()
665  for dim in sorted(inDict['dimensions']):
666  entry[dim] = row[schema[dim]]
667  inDict['dataIdList'].append(entry)
668 
669  return cls.fromDictfromDictfromDict(inDict)
670 
671  @classmethod
672  def fromDict(cls, dictionary):
673  """Construct provenance from a dictionary.
674 
675  Parameters
676  ----------
677  dictionary : `dict`
678  Dictionary of provenance parameters.
679 
680  Returns
681  -------
682  provenance : `lsst.ip.isr.IsrProvenance`
683  The provenance defined in the tables.
684  """
685  calib = cls()
686  if calib._OBSTYPE != dictionary['metadata']['OBSTYPE']:
687  raise RuntimeError(f"Incorrect calibration supplied. Expected {calib._OBSTYPE}, "
688  f"found {dictionary['metadata']['OBSTYPE']}")
689  calib.updateMetadata(setDate=False, setCalibInfo=True, **dictionary['metadata'])
690 
691  # These properties should be in the metadata, but occasionally
692  # are found in the dictionary itself. Check both places,
693  # ending with `None` if neither contains the information.
694  calib.calibType = dictionary['calibType']
695  calib.dimensions = set(dictionary['dimensions'])
696  calib.dataIdList = dictionary['dataIdList']
697 
698  calib.updateMetadata()
699  return calib
700 
701  def toDict(self):
702  """Return a dictionary containing the provenance information.
703 
704  Returns
705  -------
706  dictionary : `dict`
707  Dictionary of provenance.
708  """
709  self.updateMetadataupdateMetadataupdateMetadata()
710 
711  outDict = {}
712 
713  metadata = self.getMetadatagetMetadata()
714  outDict['metadata'] = metadata
715  outDict['detectorName'] = self._detectorName_detectorName
716  outDict['detectorSerial'] = self._detectorSerial_detectorSerial
717  outDict['detectorId'] = self._detectorId_detectorId
718  outDict['instrument'] = self._instrument_instrument
719  outDict['calibType'] = self.calibTypecalibType
720  outDict['dimensions'] = list(self.dimensionsdimensions)
721  outDict['dataIdList'] = self.dataIdListdataIdList
722 
723  return outDict
724 
725  def toTable(self):
726  """Return a list of tables containing the provenance.
727 
728  This seems inefficient and slow, so this may not be the best
729  way to store the data.
730 
731  Returns
732  -------
733  tableList : `list` [`lsst.afw.table.Table`]
734  List of tables containing the provenance information
735 
736  """
737  tableList = []
738  self.updateMetadataupdateMetadataupdateMetadata(setDate=True, setCalibInfo=True)
739 
740  catalog = Table(rows=self.dataIdListdataIdList,
741  names=self.dimensionsdimensions)
742  filteredMetadata = {k: v for k, v in self.getMetadatagetMetadata().toDict().items() if v is not None}
743  catalog.meta = filteredMetadata
744  tableList.append(catalog)
745  return tableList
def calibInfoFromDict(self, dictionary)
Definition: calibType.py:229
def fromTable(cls, tableList, **kwargs)
Definition: calibType.py:492
def validate(self, other=None)
Definition: calibType.py:534
def setMetadata(self, metadata)
Definition: calibType.py:126
def writeFits(self, filename)
Definition: calibType.py:408
def requiredAttributes(self, value)
Definition: calibType.py:111
def readText(cls, filename, **kwargs)
Definition: calibType.py:284
def updateMetadata(self, camera=None, detector=None, filterName=None, setCalibId=False, setCalibInfo=False, setDate=False, **kwargs)
Definition: calibType.py:150
def fromDict(cls, dictionary, **kwargs)
Definition: calibType.py:449
def writeText(self, filename, format='auto')
Definition: calibType.py:316
def __init__(self, camera=None, detector=None, log=None, **kwargs)
Definition: calibType.py:63
def fromDetector(self, detector)
Definition: calibType.py:432
def readFits(cls, filename, **kwargs)
Definition: calibType.py:370
def __init__(self, calibType="unknown", **kwargs)
Definition: calibType.py:592
def updateMetadata(self, setDate=False, **kwargs)
Definition: calibType.py:607
def fromDataIds(self, dataIdList)
Definition: calibType.py:621