22 __all__ = (
"Translator",
"KeyHandler",
"CopyKeyHandler",
"ConstantKeyHandler",
23 "makeCalibrationLabel")
26 from typing
import Optional, Any, Dict, Tuple, FrozenSet, Iterable, List
27 from abc
import ABCMeta, abstractmethod
29 from lsst.log
import Log
30 from lsst.skymap
import BaseSkyMap
34 filter: Optional[str] =
None) -> str:
35 """Make a Gen3 calibration_label string corresponding to a Gen2 data ID.
39 datasetTypeName : `str`
40 Name of the dataset type this calibration label identifies.
42 Date string used in the Gen2 template.
44 Detector ID used in the Gen2 template.
45 filter : `str`, optional
46 Filter used in the Gen2 template.
51 Calibration label string.
55 elements = [datasetTypeName, calibDate]
57 elements.append(f
"{ccd:03d}")
58 if filter
is not None:
59 elements.append(filter)
60 return "gen2/{}".format(
"_".join(elements))
64 """Base class for Translator helpers that each handle just one Gen3 Data
70 Name of the Gen3 dimension (data ID key) populated by
71 this handler (e.g. "visit" or "abstract_filter").
76 __slots__ = (
"dimension",)
79 skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
80 datasetTypeName: str):
81 """Update a Gen3 data ID dict with a single key-value pair from a Gen2
84 This method is implemented by the base class and is not expected to
85 be re-implemented by subclasses.
90 Gen2 data ID from which to draw key-value pairs from.
92 Gen3 data ID to update in-place.
93 skyMap: `BaseSkyMap`, optional
94 SkyMap that defines the tracts and patches used in the Gen2 data
97 Name of the Gen3 skymap dimension that defines the tracts and
98 patches used in the Gen3 data ID.
99 datasetTypeName: `str`
100 Name of the dataset type.
102 gen3id[self.
dimension] = self.
extract(gen2id, skyMap=skyMap, skyMapName=skyMapName,
103 datasetTypeName=datasetTypeName)
106 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
107 datasetTypeName: str) -> Any:
108 """Extract a Gen3 data ID value from a Gen2 data ID.
113 Gen2 data ID from which to draw key-value pairs from.
114 skyMap: `BaseSkyMap`, optional
115 SkyMap that defines the tracts and patches used in the Gen2 data
118 Name of the Gen3 skymap dimension that defines the tracts and
119 patches used in the Gen3 data ID.
120 datasetTypeName: `str`
121 Name of the dataset type.
123 raise NotImplementedError()
127 """A KeyHandler that adds a constant key-value pair to the Gen3 data ID.
132 Name of the Gen3 dimension (data ID key) populated by
133 this handler (e.g. "visit" or "abstract_filter").
141 __slots__ = (
"value",)
143 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
144 datasetTypeName: str) -> Any:
150 """A KeyHandler that simply copies a value from a Gen3 data ID.
155 Name of the Gen3 dimension produced by this handler.
156 dtype : `type`, optional
157 If not `None`, the type that values for this key must be an
160 def __init__(self, dimension: str, gen2key: Optional[str] =
None,
161 dtype: Optional[type] =
None):
163 self.
gen2key = gen2key
if gen2key
is not None else dimension
166 __slots__ = (
"gen2key",
"dtype")
168 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
169 datasetTypeName: str) -> Any:
172 if self.
dtype is not None:
175 except ValueError
as err:
177 f
"'{r}' is not a valid value for {self.dimension}; "
178 f
"expected {self.dtype.__name__}, got {type(r).__name__}."
184 """A KeyHandler for skymap patches.
191 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
192 datasetTypeName: str) -> Any:
194 tract = gen2id[
"tract"]
195 tractInfo = skyMap[tract]
196 x, y = gen2id[
"patch"].split(
",")
197 patchInfo = tractInfo[int(x), int(y)]
198 return tractInfo.getSequentialPatchIndex(patchInfo)
202 """A KeyHandler for skymaps."""
208 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
209 datasetTypeName: str) -> Any:
215 """A KeyHandler for master calibration datasets.
219 super().
__init__(
"calibration_label")
223 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
224 datasetTypeName: str) -> Any:
227 ccd=gen2id.get(
"ccd"), filter=gen2id.get(
"filter"))
231 """Callable object that translates Gen2 Data IDs to Gen3 Data IDs for a
232 particular DatasetType.
234 Translators should usually be constructed via the `makeMatching` method.
239 A list of KeyHandlers this Translator should use.
240 skyMap : `BaseSkyMap`, optional
241 SkyMap instance used to define any tract or patch Dimensions.
243 Gen3 SkyMap Dimension name to be associated with any tract or patch
245 datasetTypeName : `str`
246 Name of the dataset type whose data IDs this translator handles.
248 def __init__(self, handlers: List[KeyHandler], skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
249 datasetTypeName: str):
255 __slots__ = (
"handlers",
"skyMap",
"skyMapName",
"datasetTypeName")
265 Tuple[FrozenSet[str], KeyHandler, bool]
274 def addRule(cls, handler: KeyHandler, instrument: Optional[str] =
None,
275 datasetTypeName: Optional[str] =
None, gen2keys: Iterable[str] = (),
276 consume: bool =
True):
277 """Add a KeyHandler and an associated matching rule.
281 handler : `KeyHandler`
282 A KeyHandler instance to add to a Translator when this rule
285 Gen3 instrument name the Gen2 repository must be associated with
286 for this rule to match, or None to match any instrument.
287 datasetTypeName : `str`
288 Name of the DatasetType this rule matches, or None to match any
291 Sequence of Gen2 data ID keys that must all be present for this
293 consume : `bool` or `tuple`
294 If True (default), remove all entries in gen2keys from the set of
295 keys being matched to in order to prevent less-specific handlers
297 May also be a `tuple` listing only the keys to consume.
302 consume = frozenset(gen2keys)
304 consume = frozenset(consume)
306 consume = frozenset()
310 rulesForInstrument = cls._rules.setdefault(instrument, {
None: []})
311 rulesForInstrumentAndDatasetType = rulesForInstrument.setdefault(datasetTypeName, [])
312 rulesForInstrumentAndDatasetType.append((frozenset(gen2keys), handler, consume))
315 def makeMatching(cls, datasetTypeName: str, gen2keys: Dict[str, type], instrument: Optional[str] =
None,
316 skyMap: Optional[BaseSkyMap] =
None, skyMapName: Optional[str] =
None):
317 """Construct a Translator appropriate for instances of the given
322 datasetTypeName : `str`
323 Name of the dataset type.
325 Keys of a Gen2 data ID for this dataset.
326 instrument: `str`, optional
327 Name of the Gen3 instrument dimension for translated data IDs.
328 skyMap: `~lsst.skymap.BaseSkyMap`, optional
329 The skymap instance that defines any tract/patch data IDs.
330 `~lsst.skymap.BaseSkyMap` instances.
331 skyMapName : `str`, optional
332 Gen3 SkyMap Dimension name to be associated with any tract or patch
337 translator : `Translator`
338 A translator whose translate() method can be used to transform Gen2
339 data IDs to Gen3 dataIds.
341 if instrument
is not None:
342 rulesForInstrument = cls._rules.get(instrument, {
None: []})
344 rulesForInstrument = {
None: []}
345 rulesForAnyInstrument = cls._rules[
None]
346 candidateRules = itertools.chain(
347 rulesForInstrument.get(datasetTypeName, []),
348 rulesForInstrument[
None],
349 rulesForAnyInstrument.get(datasetTypeName, []),
350 rulesForAnyInstrument[
None],
353 targetKeys = set(gen2keys)
354 for ruleKeys, ruleHandlers, consume
in candidateRules:
355 if ruleKeys.issubset(targetKeys):
356 matchedHandlers.append(ruleHandlers)
357 targetKeys -= consume
358 return Translator(matchedHandlers, skyMap=skyMap, skyMapName=skyMapName,
359 datasetTypeName=datasetTypeName)
361 def __call__(self, gen2id: Dict[str, Any], *, partial: bool =
False, log: Optional[Log] =
None):
362 """Return a Gen3 data ID that corresponds to the given Gen2 data ID.
367 handler.translate(gen2id, gen3id, skyMap=self.
skyMap, skyMapName=self.
skyMapName,
372 log.debug(
"Failed to translate %s from %s.", handler.dimension, gen2id)
380 """The names of the dimensions populated by this Translator
383 return frozenset(h.dimension
for h
in self.
handlers)
390 for coaddName
in (
"deep",
"goodSeeing",
"psfMatched",
"dcr"):
397 Translator.addRule(
CopyKeyHandler(
"tract", dtype=int), gen2keys=(
"tract",))
401 for datasetTypeName
in (
"transmission_sensor",
"transmission_optics",
"transmission_filter"):
403 datasetTypeName=datasetTypeName)
408 Translator.addRule(
CopyKeyHandler(
"htm7", gen2key=
"pixel_id", dtype=int), gen2keys=(
"pixel_id",))