lsst.obs.base  19.0.0-51-gb87bce2+1
calibRepoConverter.py
Go to the documentation of this file.
1 # This file is part of obs_base.
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 <http://www.gnu.org/licenses/>.
21 from __future__ import annotations
22 
23 __all__ = ["CalibRepoConverter"]
24 
25 import os
26 import sqlite3
27 import astropy.time
28 from typing import TYPE_CHECKING, Dict, Iterator, Tuple, Optional
29 
30 from .repoConverter import RepoConverter
31 from .repoWalker import RepoWalker
32 from .translators import makeCalibrationLabel
33 
34 if TYPE_CHECKING:
35  from lsst.daf.butler import StorageClass, FormatterParameter
36  from .repoWalker.scanner import PathElementHandler
37  from ..cameraMapper import CameraMapper
38  from ..mapping import Mapping as CameraMapperMapping # disambiguate from collections.abc.Mapping
39 
40 
42  """A specialization of `RepoConverter` for calibration repositories.
43 
44  Parameters
45  ----------
46  mapper : `CameraMapper`
47  Gen2 mapper for the data repository. The root associated with the
48  mapper is ignored and need not match the root of the repository.
49  kwds
50  Additional keyword arguments are forwarded to (and required by)
51  `RepoConverter`.
52  """
53 
54  def __init__(self, *, mapper: CameraMapper, **kwds):
55  super().__init__(**kwds)
56  self.mapper = mapper
57  self._datasetTypes = set()
58 
59  def isDatasetTypeSpecial(self, datasetTypeName: str) -> bool:
60  # Docstring inherited from RepoConverter.
61  return datasetTypeName in self.task.config.curatedCalibrations
62 
63  def iterMappings(self) -> Iterator[Tuple[str, CameraMapperMapping]]:
64  # Docstring inherited from RepoConverter.
65  yield from self.mapper.calibrations.items()
66 
67  def makeRepoWalkerTarget(self, datasetTypeName: str, template: str, keys: Dict[str, type],
68  storageClass: StorageClass, formatter: FormatterParameter = None,
69  targetHandler: Optional[PathElementHandler] = None,
70  ) -> RepoWalker.Target:
71  # Docstring inherited from RepoConverter.
72  target = RepoWalker.Target(
73  datasetTypeName=datasetTypeName,
74  storageClass=storageClass,
75  template=template,
76  keys=keys,
77  instrument=self.task.instrument.getName(),
78  universe=self.task.registry.dimensions,
79  formatter=formatter,
80  targetHandler=targetHandler,
81  )
82  self._datasetTypes.add(target.datasetType)
83  return target
84 
86  # Docstring inherited from RepoConverter.
87  # This has only been tested on HSC, and it's not clear how general it
88  # is. The catch is that it needs to generate calibration_label strings
89  # consistent with those produced by the Translator system.
90  db = sqlite3.connect(os.path.join(self.root, "calibRegistry.sqlite3"))
91  db.row_factory = sqlite3.Row
92  records = []
93  for datasetType in self._datasetTypes:
94  if "calibration_label" not in datasetType.dimensions:
95  continue
96  fields = ["validStart", "validEnd", "calibDate"]
97  if "detector" in datasetType.dimensions.names:
98  fields.append(self.task.config.ccdKey)
99  else:
100  fields.append(f"NULL AS {self.task.config.ccdKey}")
101  if ("physical_filter" in datasetType.dimensions.names
102  or "abstract_filter" in datasetType.dimensions.names):
103  fields.append("filter")
104  else:
105  fields.append("NULL AS filter")
106  query = f"SELECT DISTINCT {', '.join(fields)} FROM {datasetType.name};"
107  try:
108  results = db.execute(query)
109  except sqlite3.OperationalError as e:
110  if (self.mapper.mappings[datasetType.name].tables is None
111  or len(self.mapper.mappings[datasetType.name].tables) == 0):
112  self.task.log.warn("Could not extract calibration ranges for %s in %s: %r",
113  datasetType.name, self.root, e)
114  continue
115  # Try using one of the alternate table names in the mapper (e.g. cpBias->bias for DECam).
116  name = self.mapper.mappings[datasetType.name].tables[0]
117  query = f"SELECT DISTINCT {', '.join(fields)} FROM {name};"
118  try:
119  results = db.execute(query)
120  except sqlite3.OperationalError as e:
121  self.task.log.warn("Could not extract calibration ranges for %s in %s: %r",
122  datasetType.name, self.root, e)
123  continue
124  for row in results:
125  label = makeCalibrationLabel(datasetType.name, row["calibDate"],
126  ccd=row[self.task.config.ccdKey], filter=row["filter"])
127  # For validity times we use TAI as some gen2 repos have validity
128  # dates very far in the past or future.
129  day = astropy.time.TimeDelta(1, format="jd", scale="tai")
130  records.append({
131  "instrument": self.task.instrument.getName(),
132  "name": label,
133  "datetime_begin": astropy.time.Time(row["validStart"], format="iso", scale="tai"),
134  "datetime_end": astropy.time.Time(row["validEnd"], format="iso", scale="tai") + day
135  })
136  if records:
137  self.task.registry.insertDimensionData("calibration_label", *records)
138 
139  # Class attributes that will be shadowed by public instance attributes;
140  # defined here only for documentation purposes.
141 
142  mapper: CameraMapper
143  """Gen2 mapper associated with this repository.
144  """
lsst.obs.base.gen2to3.repoConverter.RepoConverter.root
root
Definition: repoConverter.py:207
lsst.obs.base.gen2to3.translators.makeCalibrationLabel
str makeCalibrationLabel(str datasetTypeName, str calibDate, Optional[int] ccd=None, Optional[str] filter=None)
Definition: translators.py:34
lsst.obs.base.gen2to3.repoConverter.RepoConverter.task
task
Definition: repoConverter.py:206
lsst.obs.base.gen2to3.calibRepoConverter.CalibRepoConverter.insertDimensionData
def insertDimensionData(self)
Definition: calibRepoConverter.py:85
lsst.obs.base.gen2to3.calibRepoConverter.CalibRepoConverter._datasetTypes
_datasetTypes
Definition: calibRepoConverter.py:57
lsst.obs.base.gen2to3.calibRepoConverter.CalibRepoConverter.iterMappings
Iterator[Tuple[str, CameraMapperMapping]] iterMappings(self)
Definition: calibRepoConverter.py:63
lsst.obs.base.gen2to3.calibRepoConverter.CalibRepoConverter.isDatasetTypeSpecial
bool isDatasetTypeSpecial(self, str datasetTypeName)
Definition: calibRepoConverter.py:59
lsst.obs.base.gen2to3.calibRepoConverter.CalibRepoConverter
Definition: calibRepoConverter.py:41
lsst.obs.base.gen2to3.calibRepoConverter.CalibRepoConverter.mapper
mapper
Definition: calibRepoConverter.py:56
lsst.obs.base.gen2to3.repoConverter.RepoConverter
Definition: repoConverter.py:178
lsst.obs.base.gen2to3.calibRepoConverter.CalibRepoConverter.makeRepoWalkerTarget
RepoWalker.Target makeRepoWalkerTarget(self, str datasetTypeName, str template, Dict[str, type] keys, StorageClass storageClass, FormatterParameter formatter=None, Optional[PathElementHandler] targetHandler=None)
Definition: calibRepoConverter.py:67
lsst.obs.base.gen2to3.calibRepoConverter.CalibRepoConverter.__init__
def __init__(self, *CameraMapper mapper, **kwds)
Definition: calibRepoConverter.py:54