22 __all__ = (
"Instrument",
"makeExposureRecordFromObsInfo",
"makeVisitRecordFromObsInfo",
23 "addUnboundedCalibrationLabel")
26 from abc
import ABCMeta, abstractmethod
29 from lsst.daf.butler
import TIMESPAN_MIN, TIMESPAN_MAX, DatasetType, DataCoordinate
34 StandardCuratedCalibrationDatasetTypes = {
35 "defects": {
"dimensions": (
"instrument",
"detector",
"calibration_label"),
36 "storageClass":
"Defects"},
37 "qe_curve": {
"dimensions": (
"instrument",
"detector",
"calibration_label"),
38 "storageClass":
"QECurve"},
43 """Base class for instrument-specific logic for the Gen3 Butler. 45 Concrete instrument subclasses should be directly constructable with no 50 """Paths to config files to read for specific Tasks. 52 The paths in this list should contain files of the form `task.py`, for 53 each of the Tasks that requires special configuration. 57 """Instrument specific name to use when locating a policy or configuration 58 file in the file system.""" 61 """Name of the package containing the text curated calibration files. 62 Usually a obs _data package. If `None` no curated calibration files 63 will be read. (`str`)""" 65 standardCuratedDatasetTypes = tuple(StandardCuratedCalibrationDatasetTypes)
66 """The dataset types expected to be obtained from the obsDataPackage. 67 These dataset types are all required to have standard definitions and 68 must be known to the base class. Clearing this list will prevent 69 any of these calibrations from being stored. If a dataset type is not 70 known to a specific instrument it can still be included in this list 71 since the data package is the source of truth. 77 """`~lsst.obs.base.FilterDefinitionCollection`, defining the filters 90 raise NotImplementedError()
94 """Retrieve the cameraGeom representation of this instrument. 96 This is a temporary API that should go away once obs_ packages have 97 a standardized approach to writing versioned cameras to a Gen3 repo. 99 raise NotImplementedError()
103 """Insert instrument, physical_filter, and detector entries into a 106 raise NotImplementedError()
120 """Given an instrument name and a butler, retrieve a corresponding 121 instantiated instrument object. 126 Name of the instrument (must match the name property of 127 an instrument class). 128 registry : `lsst.daf.butler.Registry` 129 Butler registry to query to find the information. 133 instrument : `Instrument` 134 An instance of the relevant `Instrument`. 138 The instrument must be registered in the corresponding butler. 143 Raised if the instrument is not known to the supplied registry. 145 Raised if the class could not be imported. This could mean 146 that the relevant obs package has not been setup. 148 Raised if the class name retrieved is not a string. 150 dimensions = list(registry.queryDimensions(
"instrument", dataId={
"instrument": name}))
151 cls = dimensions[0].records[
"instrument"].class_name
152 if not isinstance(cls, str):
153 raise TypeError(f
"Unexpected class name retrieved from {name} instrument dimension (got {cls})")
154 instrument = doImport(cls)
157 def _registerFilters(self, registry):
158 """Register the physical and abstract filter Dimension relationships. 159 This should be called in the ``register`` implementation. 163 registry : `lsst.daf.butler.core.Registry` 164 The registry to add dimensions to. 168 if filter.abstract_filter
is None:
169 abstract_filter = filter.physical_filter
171 abstract_filter = filter.abstract_filter
173 registry.insertDimensionData(
"physical_filter",
175 "name": filter.physical_filter,
176 "abstract_filter": abstract_filter
181 """Return the Formatter class that should be used to read a particular 186 dataId : `DataCoordinate` 187 Dimension-based ID for the raw file or files being ingested. 191 formatter : `Formatter` class 192 Class to be used that reads the file into an 193 `lsst.afw.image.Exposure` instance. 195 raise NotImplementedError()
198 """Write human-curated calibration Datasets to the given Butler with 199 the appropriate validity ranges. 203 butler : `lsst.daf.butler.Butler` 204 Butler to use to store these calibrations. 208 Expected to be called from subclasses. The base method calls 209 ``writeCameraGeom`` and ``writeStandardTextCuratedCalibrations``. 215 """Apply instrument-specific overrides for a task config. 220 Name of the object being configured; typically the _DefaultName 222 config : `lsst.pex.config.Config` 223 Config instance to which overrides should be applied. 226 path = os.path.join(root, f
"{name}.py")
227 if os.path.exists(path):
231 """Write the default camera geometry to the butler repository 232 with an infinite validity range. 236 butler : `lsst.daf.butler.Butler` 237 Butler to receive these calibration datasets. 240 datasetType = DatasetType(
"camera", (
"instrument",
"calibration_label"),
"Camera",
241 universe=butler.registry.dimensions)
242 butler.registry.registerDatasetType(datasetType)
245 butler.put(camera, datasetType, unboundedDataId)
248 """Write the set of standardized curated text calibrations to 253 butler : `lsst.daf.butler.Butler` 254 Butler to receive these calibration datasets. 259 if datasetTypeName
not in StandardCuratedCalibrationDatasetTypes:
260 raise ValueError(f
"DatasetType {datasetTypeName} not in understood list" 261 f
" [{'.'.join(StandardCuratedCalibrationDatasetTypes)}]")
262 definition = StandardCuratedCalibrationDatasetTypes[datasetTypeName]
263 datasetType = DatasetType(datasetTypeName,
264 universe=butler.registry.dimensions,
268 def _writeSpecificCuratedCalibrationDatasets(self, butler, datasetType):
269 """Write standardized curated calibration datasets for this specific 270 dataset type from an obs data package. 274 butler : `lsst.daf.butler.Butler` 275 Gen3 butler in which to put the calibrations. 276 datasetType : `lsst.daf.butler.DatasetType` 277 Dataset type to be put. 281 This method scans the location defined in the ``obsDataPackageDir`` 282 class attribute for curated calibrations corresponding to the 283 supplied dataset type. The directory name in the data package must 284 match the name of the dataset type. They are assumed to use the 285 standard layout and can be read by 286 `~lsst.pipe.tasks.read_curated_calibs.read_all` and provide standard 296 if not os.path.exists(calibPath):
300 butler.registry.registerDatasetType(datasetType)
304 from lsst.pipe.tasks.read_curated_calibs
import read_all
307 calibsDict = read_all(calibPath, camera)[0]
308 endOfTime = TIMESPAN_MAX
309 dimensionRecords = []
311 for det
in calibsDict:
312 times = sorted([k
for k
in calibsDict[det]])
313 calibs = [calibsDict[det][time]
for time
in times]
314 times = [astropy.time.Time(t, format=
"datetime", scale=
"utc")
for t
in times]
316 for calib, beginTime, endTime
in zip(calibs, times[:-1], times[1:]):
317 md = calib.getMetadata()
318 calibrationLabel = f
"{datasetType.name}/{md['CALIBDATE']}/{md['DETECTOR']}" 319 dataId = DataCoordinate.standardize(
320 universe=butler.registry.dimensions,
322 calibration_label=calibrationLabel,
323 detector=md[
"DETECTOR"],
325 datasetRecords.append((calib, dataId))
326 dimensionRecords.append({
328 "name": calibrationLabel,
329 "datetime_begin": beginTime,
330 "datetime_end": endTime,
334 with butler.transaction():
335 butler.registry.insertDimensionData(
"calibration_label", *dimensionRecords)
338 for calib, dataId
in datasetRecords:
339 butler.put(calib, datasetType, dataId)
343 """Construct an exposure DimensionRecord from 344 `astro_metadata_translator.ObservationInfo`. 348 obsInfo : `astro_metadata_translator.ObservationInfo` 349 A `~astro_metadata_translator.ObservationInfo` object corresponding to 351 universe : `DimensionUniverse` 352 Set of all known dimensions. 356 record : `DimensionRecord` 357 A record containing exposure metadata, suitable for insertion into 360 dimension = universe[
"exposure"]
361 return dimension.RecordClass.fromDict({
362 "instrument": obsInfo.instrument,
363 "id": obsInfo.exposure_id,
364 "name": obsInfo.observation_id,
365 "group_name": obsInfo.exposure_group,
366 "datetime_begin": obsInfo.datetime_begin,
367 "datetime_end": obsInfo.datetime_end,
368 "exposure_time": obsInfo.exposure_time.to_value(
"s"),
369 "dark_time": obsInfo.dark_time.to_value(
"s"),
370 "observation_type": obsInfo.observation_type,
371 "physical_filter": obsInfo.physical_filter,
372 "visit": obsInfo.visit_id,
377 """Construct a visit `DimensionRecord` from 378 `astro_metadata_translator.ObservationInfo`. 382 obsInfo : `astro_metadata_translator.ObservationInfo` 383 A `~astro_metadata_translator.ObservationInfo` object corresponding to 385 universe : `DimensionUniverse` 386 Set of all known dimensions. 387 region : `lsst.sphgeom.Region`, optional 388 Spatial region for the visit. 392 record : `DimensionRecord` 393 A record containing visit metadata, suitable for insertion into a 396 dimension = universe[
"visit"]
397 return dimension.RecordClass.fromDict({
398 "instrument": obsInfo.instrument,
399 "id": obsInfo.visit_id,
400 "name": obsInfo.observation_id,
401 "datetime_begin": obsInfo.datetime_begin,
402 "datetime_end": obsInfo.datetime_end,
403 "exposure_time": obsInfo.exposure_time.to_value(
"s"),
404 "physical_filter": obsInfo.physical_filter,
410 """Add a special 'unbounded' calibration_label dimension entry for the 411 given camera that is valid for any exposure. 413 If such an entry already exists, this function just returns a `DataId` 414 for the existing entry. 418 registry : `Registry` 419 Registry object in which to insert the dimension entry. 420 instrumentName : `str` 421 Name of the instrument this calibration label is associated with. 426 New or existing data ID for the unbounded calibration. 428 d = dict(instrument=instrumentName, calibration_label=
"unbounded")
430 return registry.expandDataId(d)
434 entry[
"datetime_begin"] = TIMESPAN_MIN
435 entry[
"datetime_end"] = TIMESPAN_MAX
436 registry.insertDimensionData(
"calibration_label", entry)
437 return registry.expandDataId(d)
def obsDataPackageDir(self)
def applyConfigOverrides(self, name, config)
standardCuratedDatasetTypes
def makeExposureRecordFromObsInfo(obsInfo, universe)
def writeStandardTextCuratedCalibrations(self, butler)
def fromName(cls, name, registry)
def getRawFormatter(self, dataId)
def makeVisitRecordFromObsInfo(obsInfo, universe, region=None)
def filterDefinitions(self)
def __init__(self, args, kwargs)
def writeCameraGeom(self, butler)
def register(self, registry)
def addUnboundedCalibrationLabel(registry, instrumentName)
def _writeSpecificCuratedCalibrationDatasets(self, butler, datasetType)
def writeCuratedCalibrations(self, butler)