lsst.obs.base  19.0.0-28-g99824a6
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  for filter in self.filterDefinitions:
87  # fix for undefined abstract filters causing trouble in the registry:
88  if filter.abstract_filter is None:
89  abstract_filter = filter.physical_filter
90  else:
91  abstract_filter = filter.abstract_filter
92 
93  registry.insertDimensionData("physical_filter",
94  {"instrument": self.getName(),
95  "name": filter.physical_filter,
96  "abstract_filter": abstract_filter
97  })
98 
99  @abstractmethod
100  def getRawFormatter(self, dataId):
101  """Return the Formatter class that should be used to read a particular
102  raw file.
103 
104  Parameters
105  ----------
106  dataId : `DataCoordinate`
107  Dimension-based ID for the raw file or files being ingested.
108 
109  Returns
110  -------
111  formatter : `Formatter` class
112  Class to be used that reads the file into an
113  `lsst.afw.image.Exposure` instance.
114  """
115  raise NotImplementedError()
116 
117  @abstractmethod
118  def writeCuratedCalibrations(self, butler):
119  """Write human-curated calibration Datasets to the given Butler with
120  the appropriate validity ranges.
121 
122  This is a temporary API that should go away once obs_ packages have
123  a standardized approach to this problem.
124  """
125  raise NotImplementedError()
126 
127  def applyConfigOverrides(self, name, config):
128  """Apply instrument-specific overrides for a task config.
129 
130  Parameters
131  ----------
132  name : `str`
133  Name of the object being configured; typically the _DefaultName
134  of a Task.
135  config : `lsst.pex.config.Config`
136  Config instance to which overrides should be applied.
137  """
138  for root in self.configPaths:
139  path = os.path.join(root, f"{name}.py")
140  if os.path.exists(path):
141  config.load(path)
142 
143 
144 def makeExposureRecordFromObsInfo(obsInfo, universe):
145  """Construct an exposure DimensionRecord from
146  `astro_metadata_translator.ObservationInfo`.
147 
148  Parameters
149  ----------
150  obsInfo : `astro_metadata_translator.ObservationInfo`
151  A `~astro_metadata_translator.ObservationInfo` object corresponding to
152  the exposure.
153  universe : `DimensionUniverse`
154  Set of all known dimensions.
155 
156  Returns
157  -------
158  record : `DimensionRecord`
159  A record containing exposure metadata, suitable for insertion into
160  a `Registry`.
161  """
162  dimension = universe["exposure"]
163  return dimension.RecordClass.fromDict({
164  "instrument": obsInfo.instrument,
165  "id": obsInfo.exposure_id,
166  "name": obsInfo.observation_id,
167  "datetime_begin": obsInfo.datetime_begin.to_datetime(),
168  "datetime_end": obsInfo.datetime_end.to_datetime(),
169  "exposure_time": obsInfo.exposure_time.to_value("s"),
170  "dark_time": obsInfo.dark_time.to_value("s"),
171  "observation_type": obsInfo.observation_type,
172  "physical_filter": obsInfo.physical_filter,
173  "visit": obsInfo.visit_id,
174  })
175 
176 
177 def makeVisitRecordFromObsInfo(obsInfo, universe, *, region=None):
178  """Construct a visit `DimensionRecord` from
179  `astro_metadata_translator.ObservationInfo`.
180 
181  Parameters
182  ----------
183  obsInfo : `astro_metadata_translator.ObservationInfo`
184  A `~astro_metadata_translator.ObservationInfo` object corresponding to
185  the exposure.
186  universe : `DimensionUniverse`
187  Set of all known dimensions.
188  region : `lsst.sphgeom.Region`, optional
189  Spatial region for the visit.
190 
191  Returns
192  -------
193  record : `DimensionRecord`
194  A record containing visit metadata, suitable for insertion into a
195  `Registry`.
196  """
197  dimension = universe["visit"]
198  return dimension.RecordClass.fromDict({
199  "instrument": obsInfo.instrument,
200  "id": obsInfo.visit_id,
201  "name": obsInfo.observation_id,
202  "datetime_begin": obsInfo.datetime_begin.to_datetime(),
203  "datetime_end": obsInfo.datetime_end.to_datetime(),
204  "exposure_time": obsInfo.exposure_time.to_value("s"),
205  "physical_filter": obsInfo.physical_filter,
206  "region": region,
207  })
208 
209 
210 def addUnboundedCalibrationLabel(registry, instrumentName):
211  """Add a special 'unbounded' calibration_label dimension entry for the
212  given camera that is valid for any exposure.
213 
214  If such an entry already exists, this function just returns a `DataId`
215  for the existing entry.
216 
217  Parameters
218  ----------
219  registry : `Registry`
220  Registry object in which to insert the dimension entry.
221  instrumentName : `str`
222  Name of the instrument this calibration label is associated with.
223 
224  Returns
225  -------
226  dataId : `DataId`
227  New or existing data ID for the unbounded calibration.
228  """
229  d = dict(instrument=instrumentName, calibration_label="unbounded")
230  try:
231  return registry.expandDataId(d)
232  except LookupError:
233  pass
234  entry = d.copy()
235  entry["datetime_begin"] = datetime.min
236  entry["datetime_end"] = datetime.max
237  registry.insertDimensionData("calibration_label", entry)
238  return registry.expandDataId(d)
def applyConfigOverrides(self, name, config)
Definition: instrument.py:127
def makeExposureRecordFromObsInfo(obsInfo, universe)
Definition: instrument.py:144
def makeVisitRecordFromObsInfo(obsInfo, universe, region=None)
Definition: instrument.py:177
def __init__(self, args, kwargs)
Definition: instrument.py:52
def register(self, registry)
Definition: instrument.py:71
def addUnboundedCalibrationLabel(registry, instrumentName)
Definition: instrument.py:210
def writeCuratedCalibrations(self, butler)
Definition: instrument.py:118