lsst.obs.base  19.0.0-31-gb0bf75d
instrument.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 
22 __all__ = ("Instrument", "makeExposureRecordFromObsInfo", "makeVisitRecordFromObsInfo",
23  "addUnboundedCalibrationLabel")
24 
25 import os.path
26 from abc import ABCMeta, abstractmethod
27 
28 from lsst.daf.butler import TIMESPAN_MIN, TIMESPAN_MAX
29 
30 
31 class Instrument(metaclass=ABCMeta):
32  """Base class for instrument-specific logic for the Gen3 Butler.
33 
34  Concrete instrument subclasses should be directly constructable with no
35  arguments.
36  """
37 
38  configPaths = []
39  """Paths to config files to read for specific Tasks.
40 
41  The paths in this list should contain files of the form `task.py`, for
42  each of the Tasks that requires special configuration.
43  """
44 
45  @property
46  @abstractmethod
47  def filterDefinitions(self):
48  """`~lsst.obs.base.FilterDefinitionCollection`, defining the filters
49  for this instrument.
50  """
51  return None
52 
53  def __init__(self, *args, **kwargs):
54  self.filterDefinitions.reset()
55  self.filterDefinitions.defineFilters()
56 
57  @classmethod
58  @abstractmethod
59  def getName(cls):
60  raise NotImplementedError()
61 
62  @abstractmethod
63  def getCamera(self):
64  """Retrieve the cameraGeom representation of this instrument.
65 
66  This is a temporary API that should go away once obs_ packages have
67  a standardized approach to writing versioned cameras to a Gen3 repo.
68  """
69  raise NotImplementedError()
70 
71  @abstractmethod
72  def register(self, registry):
73  """Insert instrument, physical_filter, and detector entries into a
74  `Registry`.
75  """
76  raise NotImplementedError()
77 
78  def _registerFilters(self, registry):
79  """Register the physical and abstract filter Dimension relationships.
80  This should be called in the ``register`` implementation.
81 
82  Parameters
83  ----------
84  registry : `lsst.daf.butler.core.Registry`
85  The registry to add dimensions to.
86  """
87  for filter in self.filterDefinitions:
88  # fix for undefined abstract filters causing trouble in the registry:
89  if filter.abstract_filter is None:
90  abstract_filter = filter.physical_filter
91  else:
92  abstract_filter = filter.abstract_filter
93 
94  registry.insertDimensionData("physical_filter",
95  {"instrument": self.getName(),
96  "name": filter.physical_filter,
97  "abstract_filter": abstract_filter
98  })
99 
100  @abstractmethod
101  def getRawFormatter(self, dataId):
102  """Return the Formatter class that should be used to read a particular
103  raw file.
104 
105  Parameters
106  ----------
107  dataId : `DataCoordinate`
108  Dimension-based ID for the raw file or files being ingested.
109 
110  Returns
111  -------
112  formatter : `Formatter` class
113  Class to be used that reads the file into an
114  `lsst.afw.image.Exposure` instance.
115  """
116  raise NotImplementedError()
117 
118  @abstractmethod
119  def writeCuratedCalibrations(self, butler):
120  """Write human-curated calibration Datasets to the given Butler with
121  the appropriate validity ranges.
122 
123  This is a temporary API that should go away once obs_ packages have
124  a standardized approach to this problem.
125  """
126  raise NotImplementedError()
127 
128  def applyConfigOverrides(self, name, config):
129  """Apply instrument-specific overrides for a task config.
130 
131  Parameters
132  ----------
133  name : `str`
134  Name of the object being configured; typically the _DefaultName
135  of a Task.
136  config : `lsst.pex.config.Config`
137  Config instance to which overrides should be applied.
138  """
139  for root in self.configPaths:
140  path = os.path.join(root, f"{name}.py")
141  if os.path.exists(path):
142  config.load(path)
143 
144 
145 def makeExposureRecordFromObsInfo(obsInfo, universe):
146  """Construct an exposure DimensionRecord from
147  `astro_metadata_translator.ObservationInfo`.
148 
149  Parameters
150  ----------
151  obsInfo : `astro_metadata_translator.ObservationInfo`
152  A `~astro_metadata_translator.ObservationInfo` object corresponding to
153  the exposure.
154  universe : `DimensionUniverse`
155  Set of all known dimensions.
156 
157  Returns
158  -------
159  record : `DimensionRecord`
160  A record containing exposure metadata, suitable for insertion into
161  a `Registry`.
162  """
163  dimension = universe["exposure"]
164  return dimension.RecordClass.fromDict({
165  "instrument": obsInfo.instrument,
166  "id": obsInfo.exposure_id,
167  "name": obsInfo.observation_id,
168  "group": obsInfo.exposure_group,
169  "datetime_begin": obsInfo.datetime_begin,
170  "datetime_end": obsInfo.datetime_end,
171  "exposure_time": obsInfo.exposure_time.to_value("s"),
172  "dark_time": obsInfo.dark_time.to_value("s"),
173  "observation_type": obsInfo.observation_type,
174  "physical_filter": obsInfo.physical_filter,
175  "visit": obsInfo.visit_id,
176  })
177 
178 
179 def makeVisitRecordFromObsInfo(obsInfo, universe, *, region=None):
180  """Construct a visit `DimensionRecord` from
181  `astro_metadata_translator.ObservationInfo`.
182 
183  Parameters
184  ----------
185  obsInfo : `astro_metadata_translator.ObservationInfo`
186  A `~astro_metadata_translator.ObservationInfo` object corresponding to
187  the exposure.
188  universe : `DimensionUniverse`
189  Set of all known dimensions.
190  region : `lsst.sphgeom.Region`, optional
191  Spatial region for the visit.
192 
193  Returns
194  -------
195  record : `DimensionRecord`
196  A record containing visit metadata, suitable for insertion into a
197  `Registry`.
198  """
199  dimension = universe["visit"]
200  return dimension.RecordClass.fromDict({
201  "instrument": obsInfo.instrument,
202  "id": obsInfo.visit_id,
203  "name": obsInfo.observation_id,
204  "datetime_begin": obsInfo.datetime_begin,
205  "datetime_end": obsInfo.datetime_end,
206  "exposure_time": obsInfo.exposure_time.to_value("s"),
207  "physical_filter": obsInfo.physical_filter,
208  "region": region,
209  })
210 
211 
212 def addUnboundedCalibrationLabel(registry, instrumentName):
213  """Add a special 'unbounded' calibration_label dimension entry for the
214  given camera that is valid for any exposure.
215 
216  If such an entry already exists, this function just returns a `DataId`
217  for the existing entry.
218 
219  Parameters
220  ----------
221  registry : `Registry`
222  Registry object in which to insert the dimension entry.
223  instrumentName : `str`
224  Name of the instrument this calibration label is associated with.
225 
226  Returns
227  -------
228  dataId : `DataId`
229  New or existing data ID for the unbounded calibration.
230  """
231  d = dict(instrument=instrumentName, calibration_label="unbounded")
232  try:
233  return registry.expandDataId(d)
234  except LookupError:
235  pass
236  entry = d.copy()
237  entry["datetime_begin"] = TIMESPAN_MIN
238  entry["datetime_end"] = TIMESPAN_MAX
239  registry.insertDimensionData("calibration_label", entry)
240  return registry.expandDataId(d)
def applyConfigOverrides(self, name, config)
Definition: instrument.py:128
def makeExposureRecordFromObsInfo(obsInfo, universe)
Definition: instrument.py:145
def makeVisitRecordFromObsInfo(obsInfo, universe, region=None)
Definition: instrument.py:179
def __init__(self, args, kwargs)
Definition: instrument.py:53
def register(self, registry)
Definition: instrument.py:72
def addUnboundedCalibrationLabel(registry, instrumentName)
Definition: instrument.py:212
def writeCuratedCalibrations(self, butler)
Definition: instrument.py:119