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()
118 def _registerFilters(self, registry):
119 """Register the physical and abstract filter Dimension relationships. 120 This should be called in the ``register`` implementation. 124 registry : `lsst.daf.butler.core.Registry` 125 The registry to add dimensions to. 129 if filter.abstract_filter
is None:
130 abstract_filter = filter.physical_filter
132 abstract_filter = filter.abstract_filter
134 registry.insertDimensionData(
"physical_filter",
136 "name": filter.physical_filter,
137 "abstract_filter": abstract_filter
142 """Return the Formatter class that should be used to read a particular 147 dataId : `DataCoordinate` 148 Dimension-based ID for the raw file or files being ingested. 152 formatter : `Formatter` class 153 Class to be used that reads the file into an 154 `lsst.afw.image.Exposure` instance. 156 raise NotImplementedError()
159 """Write human-curated calibration Datasets to the given Butler with 160 the appropriate validity ranges. 164 butler : `lsst.daf.butler.Butler` 165 Butler to use to store these calibrations. 169 Expected to be called from subclasses. The base method calls 170 ``writeCameraGeom`` and ``writeStandardTextCuratedCalibrations``. 176 """Apply instrument-specific overrides for a task config. 181 Name of the object being configured; typically the _DefaultName 183 config : `lsst.pex.config.Config` 184 Config instance to which overrides should be applied. 187 path = os.path.join(root, f
"{name}.py")
188 if os.path.exists(path):
192 """Write the default camera geometry to the butler repository 193 with an infinite validity range. 197 butler : `lsst.daf.butler.Butler` 198 Butler to receive these calibration datasets. 201 datasetType = DatasetType(
"camera", (
"instrument",
"calibration_label"),
"Camera",
202 universe=butler.registry.dimensions)
203 butler.registry.registerDatasetType(datasetType)
206 butler.put(camera, datasetType, unboundedDataId)
209 """Write the set of standardized curated text calibrations to 214 butler : `lsst.daf.butler.Butler` 215 Butler to receive these calibration datasets. 220 if datasetTypeName
not in StandardCuratedCalibrationDatasetTypes:
221 raise ValueError(f
"DatasetType {datasetTypeName} not in understood list" 222 f
" [{'.'.join(StandardCuratedCalibrationDatasetTypes)}]")
223 definition = StandardCuratedCalibrationDatasetTypes[datasetTypeName]
224 datasetType = DatasetType(datasetTypeName,
225 universe=butler.registry.dimensions,
229 def _writeSpecificCuratedCalibrationDatasets(self, butler, datasetType):
230 """Write standardized curated calibration datasets for this specific 231 dataset type from an obs data package. 235 butler : `lsst.daf.butler.Butler` 236 Gen3 butler in which to put the calibrations. 237 datasetType : `lsst.daf.butler.DatasetType` 238 Dataset type to be put. 242 This method scans the location defined in the ``obsDataPackageDir`` 243 class attribute for curated calibrations corresponding to the 244 supplied dataset type. The directory name in the data package must 245 match the name of the dataset type. They are assumed to use the 246 standard layout and can be read by 247 `~lsst.pipe.tasks.read_curated_calibs.read_all` and provide standard 257 if not os.path.exists(calibPath):
261 butler.registry.registerDatasetType(datasetType)
265 from lsst.pipe.tasks.read_curated_calibs
import read_all
268 calibsDict = read_all(calibPath, camera)[0]
269 endOfTime = TIMESPAN_MAX
270 dimensionRecords = []
272 for det
in calibsDict:
273 times = sorted([k
for k
in calibsDict[det]])
274 calibs = [calibsDict[det][time]
for time
in times]
275 times = [astropy.time.Time(t, format=
"datetime", scale=
"utc")
for t
in times]
277 for calib, beginTime, endTime
in zip(calibs, times[:-1], times[1:]):
278 md = calib.getMetadata()
279 calibrationLabel = f
"{datasetType.name}/{md['CALIBDATE']}/{md['DETECTOR']}" 280 dataId = DataCoordinate.standardize(
281 universe=butler.registry.dimensions,
283 calibration_label=calibrationLabel,
284 detector=md[
"DETECTOR"],
286 datasetRecords.append((calib, dataId))
287 dimensionRecords.append({
289 "name": calibrationLabel,
290 "datetime_begin": beginTime,
291 "datetime_end": endTime,
295 with butler.transaction():
296 butler.registry.insertDimensionData(
"calibration_label", *dimensionRecords)
299 for calib, dataId
in datasetRecords:
300 butler.put(calib, datasetType, dataId)
304 """Construct an exposure DimensionRecord from 305 `astro_metadata_translator.ObservationInfo`. 309 obsInfo : `astro_metadata_translator.ObservationInfo` 310 A `~astro_metadata_translator.ObservationInfo` object corresponding to 312 universe : `DimensionUniverse` 313 Set of all known dimensions. 317 record : `DimensionRecord` 318 A record containing exposure metadata, suitable for insertion into 321 dimension = universe[
"exposure"]
322 return dimension.RecordClass.fromDict({
323 "instrument": obsInfo.instrument,
324 "id": obsInfo.exposure_id,
325 "name": obsInfo.observation_id,
326 "group_name": obsInfo.exposure_group,
327 "datetime_begin": obsInfo.datetime_begin,
328 "datetime_end": obsInfo.datetime_end,
329 "exposure_time": obsInfo.exposure_time.to_value(
"s"),
330 "dark_time": obsInfo.dark_time.to_value(
"s"),
331 "observation_type": obsInfo.observation_type,
332 "physical_filter": obsInfo.physical_filter,
333 "visit": obsInfo.visit_id,
338 """Construct a visit `DimensionRecord` from 339 `astro_metadata_translator.ObservationInfo`. 343 obsInfo : `astro_metadata_translator.ObservationInfo` 344 A `~astro_metadata_translator.ObservationInfo` object corresponding to 346 universe : `DimensionUniverse` 347 Set of all known dimensions. 348 region : `lsst.sphgeom.Region`, optional 349 Spatial region for the visit. 353 record : `DimensionRecord` 354 A record containing visit metadata, suitable for insertion into a 357 dimension = universe[
"visit"]
358 return dimension.RecordClass.fromDict({
359 "instrument": obsInfo.instrument,
360 "id": obsInfo.visit_id,
361 "name": obsInfo.observation_id,
362 "datetime_begin": obsInfo.datetime_begin,
363 "datetime_end": obsInfo.datetime_end,
364 "exposure_time": obsInfo.exposure_time.to_value(
"s"),
365 "physical_filter": obsInfo.physical_filter,
371 """Add a special 'unbounded' calibration_label dimension entry for the 372 given camera that is valid for any exposure. 374 If such an entry already exists, this function just returns a `DataId` 375 for the existing entry. 379 registry : `Registry` 380 Registry object in which to insert the dimension entry. 381 instrumentName : `str` 382 Name of the instrument this calibration label is associated with. 387 New or existing data ID for the unbounded calibration. 389 d = dict(instrument=instrumentName, calibration_label=
"unbounded")
391 return registry.expandDataId(d)
395 entry[
"datetime_begin"] = TIMESPAN_MIN
396 entry[
"datetime_end"] = TIMESPAN_MAX
397 registry.insertDimensionData(
"calibration_label", entry)
398 return registry.expandDataId(d)
def obsDataPackageDir(self)
def applyConfigOverrides(self, name, config)
standardCuratedDatasetTypes
def makeExposureRecordFromObsInfo(obsInfo, universe)
def writeStandardTextCuratedCalibrations(self, butler)
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)