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