22 __all__ = (
"Translator",
"KeyHandler",
"CopyKeyHandler",
"ConstantKeyHandler",
23 "CalibKeyHandler",
"AbstractToPhysicalFilterKeyHandler",
"PhysicalToAbstractFilterKeyHandler",
24 "makeCalibrationLabel")
27 from typing
import Optional, Any, Dict, Tuple, FrozenSet, Iterable, List
28 from abc
import ABCMeta, abstractmethod
30 from lsst.log
import Log
31 from lsst.skymap
import BaseSkyMap
35 filter: Optional[str] =
None) -> str:
36 """Make a Gen3 calibration_label string corresponding to a Gen2 data ID. 40 datasetTypeName : `str` 41 Name of the dataset type this calibration label identifies. 43 Date string used in the Gen2 template. 45 Detector ID used in the Gen2 template. 46 filter : `str`, optional 47 Filter used in the Gen2 template. 52 Calibration label string. 56 elements = [datasetTypeName, calibDate]
58 elements.append(f
"{ccd:03d}")
59 if filter
is not None:
60 elements.append(filter)
61 return "gen2/{}".format(
"_".join(elements))
65 """Base class for Translator helpers that each handle just one Gen3 Data 71 Name of the Gen3 dimension (data ID key) populated by 72 this handler (e.g. "visit" or "abstract_filter"). 77 __slots__ = (
"dimension",)
80 skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
81 datasetTypeName: str):
82 """Update a Gen3 data ID dict with a single key-value pair from a Gen2 85 This method is implemented by the base class and is not expected to 86 be re-implemented by subclasses. 91 Gen2 data ID from which to draw key-value pairs from. 93 Gen3 data ID to update in-place. 94 skyMap: `BaseSkyMap`, optional 95 SkyMap that defines the tracts and patches used in the Gen2 data 98 Name of the Gen3 skymap dimension that defines the tracts and 99 patches used in the Gen3 data ID. 100 datasetTypeName: `str` 101 Name of the dataset type. 103 gen3id[self.
dimension] = self.
extract(gen2id, skyMap=skyMap, skyMapName=skyMapName,
104 datasetTypeName=datasetTypeName)
107 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
108 datasetTypeName: str) -> Any:
109 """Extract a Gen3 data ID value from a Gen2 data ID. 114 Gen2 data ID from which to draw key-value pairs from. 115 skyMap: `BaseSkyMap`, optional 116 SkyMap that defines the tracts and patches used in the Gen2 data 119 Name of the Gen3 skymap dimension that defines the tracts and 120 patches used in the Gen3 data ID. 121 datasetTypeName: `str` 122 Name of the dataset type. 124 raise NotImplementedError()
128 """A KeyHandler that adds a constant key-value pair to the Gen3 data ID. 133 Name of the Gen3 dimension (data ID key) populated by 134 this handler (e.g. "visit" or "abstract_filter"). 142 __slots__ = (
"value",)
144 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
145 datasetTypeName: str) -> Any:
151 """A KeyHandler that simply copies a value from a Gen3 data ID. 156 Name of the Gen3 dimension produced by this handler. 157 dtype : `type`, optional 158 If not `None`, the type that values for this key must be an 161 def __init__(self, dimension: str, gen2key: Optional[str] =
None,
162 dtype: Optional[type] =
None):
164 self.
gen2key = gen2key
if gen2key
is not None else dimension
167 __slots__ = (
"gen2key",
"dtype")
169 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
170 datasetTypeName: str) -> Any:
173 if self.
dtype is not None:
176 except ValueError
as err:
178 f
"'{r}' is not a valid value for {self.dimension}; " 179 f
"expected {self.dtype.__name__}, got {type(r).__name__}." 185 """A KeyHandler for skymap patches. 192 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
193 datasetTypeName: str) -> Any:
195 tract = gen2id[
"tract"]
196 tractInfo = skyMap[tract]
197 x, y = gen2id[
"patch"].split(
",")
198 patchInfo = tractInfo[int(x), int(y)]
199 return tractInfo.getSequentialPatchIndex(patchInfo)
203 """A KeyHandler for skymaps.""" 209 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
210 datasetTypeName: str) -> Any:
216 """A KeyHandler for master calibration datasets. 218 __slots__ = (
"ccdKey",)
222 super().
__init__(
"calibration_label")
224 def extract(self, gen2id: dict, skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
225 datasetTypeName: str) -> Any:
228 ccd=gen2id.get(self.
ccdKey), filter=gen2id.get(
"filter"))
232 """KeyHandler for gen2 ``filter`` keys that match ``physical_filter`` 233 keys in gen3 but should be mapped to ``abstract_filter``. 235 Note that multiple physical filter can potentially map to one abstract 236 filter, so be careful to only use this translator on obs packages where 237 there is a one-to-one mapping. 240 __slots__ = (
"_map",)
244 self.
_map = {d.physical_filter: d.abstract_filter
for d
in filterDefinitions
245 if d.physical_filter
is not None}
248 physical = gen2id[
"filter"]
249 return self.
_map.get(physical, physical)
253 """KeyHandler for gen2 ``filter`` keys that match ``abstract_filter`` 254 keys in gen3 but should be mapped to ``physical_filter``. 256 Note that one abstract filter can potentially map to multiple physical 257 filters, so be careful to only use this translator on obs packages where 258 there is a one-to-one mapping. 261 __slots__ = (
"_map",)
265 self.
_map = {d.abstract_filter: d.physical_filter
for d
in filterDefinitions
266 if d.abstract_filter
is not None}
269 abstract = gen2id[
"filter"]
270 return self.
_map.get(abstract, abstract)
274 """Callable object that translates Gen2 Data IDs to Gen3 Data IDs for a 275 particular DatasetType. 277 Translators should usually be constructed via the `makeMatching` method. 282 A list of KeyHandlers this Translator should use. 283 skyMap : `BaseSkyMap`, optional 284 SkyMap instance used to define any tract or patch Dimensions. 286 Gen3 SkyMap Dimension name to be associated with any tract or patch 288 datasetTypeName : `str` 289 Name of the dataset type whose data IDs this translator handles. 291 def __init__(self, handlers: List[KeyHandler], skyMap: Optional[BaseSkyMap], skyMapName: Optional[str],
292 datasetTypeName: str):
298 __slots__ = (
"handlers",
"skyMap",
"skyMapName",
"datasetTypeName")
308 Tuple[FrozenSet[str], KeyHandler, bool]
317 def addRule(cls, handler: KeyHandler, instrument: Optional[str] =
None,
318 datasetTypeName: Optional[str] =
None, gen2keys: Iterable[str] = (),
319 consume: bool =
True):
320 """Add a KeyHandler and an associated matching rule. 324 handler : `KeyHandler` 325 A KeyHandler instance to add to a Translator when this rule 328 Gen3 instrument name the Gen2 repository must be associated with 329 for this rule to match, or None to match any instrument. 330 datasetTypeName : `str` 331 Name of the DatasetType this rule matches, or None to match any 334 Sequence of Gen2 data ID keys that must all be present for this 336 consume : `bool` or `tuple` 337 If True (default), remove all entries in gen2keys from the set of 338 keys being matched to in order to prevent less-specific handlers 340 May also be a `tuple` listing only the keys to consume. 345 consume = frozenset(gen2keys)
347 consume = frozenset(consume)
349 consume = frozenset()
353 rulesForInstrument = cls._rules.setdefault(instrument, {
None: []})
354 rulesForInstrumentAndDatasetType = rulesForInstrument.setdefault(datasetTypeName, [])
355 rulesForInstrumentAndDatasetType.append((frozenset(gen2keys), handler, consume))
358 def makeMatching(cls, datasetTypeName: str, gen2keys: Dict[str, type], instrument: Optional[str] =
None,
359 skyMap: Optional[BaseSkyMap] =
None, skyMapName: Optional[str] =
None):
360 """Construct a Translator appropriate for instances of the given 365 datasetTypeName : `str` 366 Name of the dataset type. 368 Keys of a Gen2 data ID for this dataset. 369 instrument: `str`, optional 370 Name of the Gen3 instrument dimension for translated data IDs. 371 skyMap: `~lsst.skymap.BaseSkyMap`, optional 372 The skymap instance that defines any tract/patch data IDs. 373 `~lsst.skymap.BaseSkyMap` instances. 374 skyMapName : `str`, optional 375 Gen3 SkyMap Dimension name to be associated with any tract or patch 380 translator : `Translator` 381 A translator whose translate() method can be used to transform Gen2 382 data IDs to Gen3 dataIds. 384 if instrument
is not None:
385 rulesForInstrument = cls._rules.get(instrument, {
None: []})
387 rulesForInstrument = {
None: []}
388 rulesForAnyInstrument = cls._rules[
None]
389 candidateRules = itertools.chain(
390 rulesForInstrument.get(datasetTypeName, []),
391 rulesForInstrument[
None],
392 rulesForAnyInstrument.get(datasetTypeName, []),
393 rulesForAnyInstrument[
None],
396 targetKeys = set(gen2keys)
397 for ruleKeys, ruleHandlers, consume
in candidateRules:
398 if ruleKeys.issubset(targetKeys):
399 matchedHandlers.append(ruleHandlers)
400 targetKeys -= consume
401 return Translator(matchedHandlers, skyMap=skyMap, skyMapName=skyMapName,
402 datasetTypeName=datasetTypeName)
404 def __call__(self, gen2id: Dict[str, Any], *, partial: bool =
False, log: Optional[Log] =
None):
405 """Return a Gen3 data ID that corresponds to the given Gen2 data ID. 410 handler.translate(gen2id, gen3id, skyMap=self.
skyMap, skyMapName=self.
skyMapName,
415 log.debug(
"Failed to translate %s from %s.", handler.dimension, gen2id)
423 """The names of the dimensions populated by this Translator 426 return frozenset(h.dimension
for h
in self.
handlers)
433 for coaddName
in (
"deep",
"goodSeeing",
"psfMatched",
"dcr"):
440 Translator.addRule(
CopyKeyHandler(
"tract", dtype=int), gen2keys=(
"tract",))
444 for datasetTypeName
in (
"transmission_sensor",
"transmission_optics",
"transmission_filter"):
446 datasetTypeName=datasetTypeName)
451 Translator.addRule(
CopyKeyHandler(
"htm7", gen2key=
"pixel_id", dtype=int), gen2keys=(
"pixel_id",))
def extract(self, gen2id, args, kwargs)
def extract(self, gen2id, args, kwargs)
def __init__(self, filterDefinitions)
def __init__(self, ccdKey="ccd")
def __init__(self, filterDefinitions)