lsst.obs.base  19.0.0-56-g64d9981+3
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  translatorFactory=self.task.translatorFactory,
82  )
83  self._datasetTypes.add(target.datasetType)
84  return target
85 
87  # Docstring inherited from RepoConverter.
88  # This has only been tested on HSC, and it's not clear how general it
89  # is. The catch is that it needs to generate calibration_label strings
90  # consistent with those produced by the Translator system.
91  db = sqlite3.connect(os.path.join(self.root, "calibRegistry.sqlite3"))
92  db.row_factory = sqlite3.Row
93  records = []
94  for datasetType in self._datasetTypes:
95  if "calibration_label" not in datasetType.dimensions:
96  continue
97  fields = ["validStart", "validEnd", "calibDate"]
98  if "detector" in datasetType.dimensions.names:
99  fields.append(self.task.config.ccdKey)
100  else:
101  fields.append(f"NULL AS {self.task.config.ccdKey}")
102  if ("physical_filter" in datasetType.dimensions.names
103  or "abstract_filter" in datasetType.dimensions.names):
104  fields.append("filter")
105  else:
106  fields.append("NULL AS filter")
107  query = f"SELECT DISTINCT {', '.join(fields)} FROM {datasetType.name};"
108  try:
109  results = db.execute(query)
110  except sqlite3.OperationalError as e:
111  if (self.mapper.mappings[datasetType.name].tables is None
112  or len(self.mapper.mappings[datasetType.name].tables) == 0):
113  self.task.log.warn("Could not extract calibration ranges for %s in %s: %r",
114  datasetType.name, self.root, e)
115  continue
116  # Try using one of the alternate table names in the mapper (e.g. cpBias->bias for DECam).
117  name = self.mapper.mappings[datasetType.name].tables[0]
118  query = f"SELECT DISTINCT {', '.join(fields)} FROM {name};"
119  try:
120  results = db.execute(query)
121  except sqlite3.OperationalError as e:
122  self.task.log.warn("Could not extract calibration ranges for %s in %s: %r",
123  datasetType.name, self.root, e)
124  continue
125  for row in results:
126  label = makeCalibrationLabel(datasetType.name, row["calibDate"],
127  ccd=row[self.task.config.ccdKey], filter=row["filter"])
128  # For validity times we use TAI as some gen2 repos have validity
129  # dates very far in the past or future.
130  day = astropy.time.TimeDelta(1, format="jd", scale="tai")
131  records.append({
132  "instrument": self.task.instrument.getName(),
133  "name": label,
134  "datetime_begin": astropy.time.Time(row["validStart"], format="iso", scale="tai"),
135  "datetime_end": astropy.time.Time(row["validEnd"], format="iso", scale="tai") + day
136  })
137  if records:
138  self.task.registry.insertDimensionData("calibration_label", *records)
139 
140  # Class attributes that will be shadowed by public instance attributes;
141  # defined here only for documentation purposes.
142 
143  mapper: CameraMapper
144  """Gen2 mapper associated with this repository.
145  """
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:36
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:86
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