LSSTApplications  20.0.0
LSSTDataManagementBasePackage
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 
92  calibFile = os.path.join(self.root, "calibRegistry.sqlite3")
93 
94  # If the registry file does not exist this indicates a problem.
95  # We check explicitly because sqlite will try to create the
96  # missing file if it can.
97  if not os.path.exists(calibFile):
98  raise RuntimeError("Attempting to convert calibrations but no registry database"
99  f" found in {self.root}")
100  db = sqlite3.connect(calibFile)
101  db.row_factory = sqlite3.Row
102  records = []
103  for datasetType in self._datasetTypes:
104  if "calibration_label" not in datasetType.dimensions:
105  continue
106  fields = ["validStart", "validEnd", "calibDate"]
107  if "detector" in datasetType.dimensions.names:
108  fields.append(self.task.config.ccdKey)
109  else:
110  fields.append(f"NULL AS {self.task.config.ccdKey}")
111  if ("physical_filter" in datasetType.dimensions.names
112  or "abstract_filter" in datasetType.dimensions.names):
113  fields.append("filter")
114  else:
115  fields.append("NULL AS filter")
116  query = f"SELECT DISTINCT {', '.join(fields)} FROM {datasetType.name};"
117  try:
118  results = db.execute(query)
119  except sqlite3.OperationalError as e:
120  if (self.mapper.mappings[datasetType.name].tables is None
121  or len(self.mapper.mappings[datasetType.name].tables) == 0):
122  self.task.log.warn("Could not extract calibration ranges for %s in %s: %r",
123  datasetType.name, self.root, e)
124  continue
125  # Try using one of the alternate table names in the mapper (e.g. cpBias->bias for DECam).
126  name = self.mapper.mappings[datasetType.name].tables[0]
127  query = f"SELECT DISTINCT {', '.join(fields)} FROM {name};"
128  try:
129  results = db.execute(query)
130  except sqlite3.OperationalError as e:
131  self.task.log.warn("Could not extract calibration ranges for %s in %s: %r",
132  datasetType.name, self.root, e)
133  continue
134  for row in results:
135  label = makeCalibrationLabel(datasetType.name, row["calibDate"],
136  ccd=row[self.task.config.ccdKey], filter=row["filter"])
137  # For validity times we use TAI as some gen2 repos have validity
138  # dates very far in the past or future.
139  day = astropy.time.TimeDelta(1, format="jd", scale="tai")
140  records.append({
141  "instrument": self.task.instrument.getName(),
142  "name": label,
143  "datetime_begin": astropy.time.Time(row["validStart"], format="iso", scale="tai"),
144  "datetime_end": astropy.time.Time(row["validEnd"], format="iso", scale="tai") + day
145  })
146  if records:
147  self.task.registry.insertDimensionData("calibration_label", *records)
148 
149  # Class attributes that will be shadowed by public instance attributes;
150  # defined here only for documentation purposes.
151 
152  mapper: CameraMapper
153  """Gen2 mapper associated with this repository.
154  """
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
set
daf::base::PropertySet * set
Definition: fits.cc:912
lsst.obs.base.gen2to3.calibRepoConverter.CalibRepoConverter.__init__
def __init__(self, *CameraMapper mapper, **kwds)
Definition: calibRepoConverter.py:54