lsst.obs.base  20.0.0-37-g38a3e24+799acde9b7
filters.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 <https://www.gnu.org/licenses/>.
21 
22 """Classes to allow obs packages to define the filters used by an Instrument
23 and for use by `lsst.afw.image.Filter`, gen2 dataIds, and gen3 Dimensions.
24 """
25 
26 __all__ = ("FilterDefinition", "FilterDefinitionCollection")
27 
28 import dataclasses
29 import collections.abc
30 
31 import numpy as np
32 
33 import lsst.afw.image.utils
34 
35 
36 class FilterDefinitionCollection(collections.abc.Sequence):
37  """An order-preserving collection of multiple `FilterDefinition`.
38 
39  Parameters
40  ----------
41  filters : `~collections.abc.Sequence`
42  The filters in this collection.
43  """
44 
45  _defined = None
46  """Whether these filters have been defined via
47  `~lsst.afw.image.utils.defineFilter`. If so, set to ``self`` to identify
48  the filter collection that defined them.
49  """
50 
51  def __init__(self, *filters):
52  self._filters = list(filters)
53 
54  def __getitem__(self, key):
55  return self._filters[key]
56 
57  def __len__(self):
58  return len(self._filters)
59 
60  def __str__(self):
61  return "FilterDefinitions(" + ', '.join(str(f) for f in self._filters) + ')'
62 
63  def defineFilters(self):
64  """Define all the filters to `lsst.afw.image.Filter`.
65 
66  `~lsst.afw.image.Filter` objects are singletons, so we protect against
67  filters being defined multiple times.
68 
69  Raises
70  ------
71  RuntimeError
72  Raised if any other `FilterDefinitionCollection` has already called
73  ``defineFilters``.
74  """
75  if self._defined is None:
76  self.reset()
77  for filter in self._filters:
78  filter.defineFilter()
79  FilterDefinitionCollection._defined = self
80  elif self._defined is self:
81  # noop: we've already defined these filters, so do nothing
82  pass
83  else:
84  msg = f"afw Filters were already defined on: {self._defined}"
85  raise RuntimeError(msg)
86 
87  @classmethod
88  def reset(cls):
89  """Reset the afw Filter definitions and clear the `defined` singleton.
90  Use this in unittests that define different filters.
91  """
92  lsst.afw.image.utils.resetFilters()
93  cls._defined = None
94 
95 
96 @dataclasses.dataclass(frozen=True)
98  """The definition of an instrument's filter bandpass.
99 
100  This class is used to interface between the `~lsst.afw.image.Filter` class
101  and the Gen2 `~lsst.daf.persistence.CameraMapper` and Gen3
102  `~lsst.obs.base.Instruments` and ``physical_filter``/``abstract_filter``
103  `~lsst.daf.butler.Dimension`.
104 
105  This class is likely temporary, until we have a better versioned filter
106  definition system that includes complete transmission information.
107  """
108 
109  physical_filter: str
110  """The name of a filter associated with a particular instrument: unique for
111  each piece of glass. This should match the exact filter name used in the
112  observatory's metadata.
113 
114  This name is used to define the ``physical_filter`` gen3 Butler Dimension.
115 
116  If neither ``abstract_filter`` or ``afw_name`` is defined, this is used
117  as the `~lsst.afw.image.Filter` ``name``, otherwise it is added to the
118  list of `~lsst.afw.image.Filter` aliases.
119  """
120 
121  lambdaEff: float
122  """The effective wavelength of this filter (nm)."""
123 
124  abstract_filter: str = None
125  """The generic name of a filter not associated with a particular instrument
126  (e.g. `r` for the SDSS Gunn r-band, which could be on SDSS, LSST, or HSC).
127 
128  Not all filters have an abstract filter: engineering or test filters may
129  not have a genericly-termed filter name.
130 
131  If specified and if `afw_name` is None, this is used as the
132  `~lsst.afw.image.Filter` ``name`` field, otherwise it is added to the list
133  of `~lsst.afw.image.Filter` aliases.
134  """
135 
136  afw_name: str = None
137  """If not None, the name of the `~lsst.afw.image.Filter` object.
138 
139  This is distinct from physical_filter and abstract_filter to maintain
140  backwards compatibility in some obs packages.
141  For example, for HSC there are two distinct ``r`` and ``i`` filters, named
142  ``r/r2`` and ``i/i2``.
143  """
144 
145  lambdaMin: float = np.nan
146  """The minimum wavelength of this filter (nm; defined as 1% throughput)"""
147  lambdaMax: float = np.nan
148  """The maximum wavelength of this filter (nm; defined as 1% throughput)"""
149 
150  alias: set = frozenset()
151  """Alternate names for this filter. These are added to the
152  `~lsst.afw.image.Filter` alias list.
153  """
154 
155  def __post_init__(self):
156  # force alias to be immutable, so that hashing works
157  if not isinstance(self.alias, frozenset):
158  object.__setattr__(self, 'alias', frozenset(self.alias))
159 
160  def __str__(self):
161  txt = f"FilterDefinition(physical_filter='{self.physical_filter}', lambdaEff='{self.lambdaEff}'"
162  if self.abstract_filter is not None:
163  txt += f", abstract_filter='{self.abstract_filter}'"
164  if self.afw_name is not None:
165  txt += f", afw_name='{self.afw_name}'"
166  if not np.isnan(self.lambdaMin):
167  txt += f", lambdaMin='{self.lambdaMin}'"
168  if not np.isnan(self.lambdaMax):
169  txt += f", lambdaMax='{self.lambdaMax}'"
170  if len(self.alias) != 0:
171  txt += f", alias='{self.alias}'"
172  return txt + ")"
173 
174  def defineFilter(self):
175  """Declare the filters via afw.image.Filter.
176  """
177  aliases = set(self.alias)
178  name = self.physical_filter
179  if self.abstract_filter is not None:
180  name = self.abstract_filter
181  aliases.add(self.physical_filter)
182  if self.afw_name is not None:
183  name = self.afw_name
184  aliases.add(self.physical_filter)
185  # Only add `physical_filter/abstract_filter` as an alias if afw_name is defined.ee
186  if self.afw_name is not None:
187  if self.abstract_filter is not None:
188  aliases.add(self.abstract_filter)
189  lsst.afw.image.utils.defineFilter(name,
190  lambdaEff=self.lambdaEff,
191  lambdaMin=self.lambdaMin,
192  lambdaMax=self.lambdaMax,
193  alias=sorted(aliases))
lsst.obs.base.filters.FilterDefinitionCollection.defineFilters
def defineFilters(self)
Definition: filters.py:63
lsst.obs.base.filters.FilterDefinitionCollection._defined
_defined
Definition: filters.py:45
lsst.obs.base.filters.FilterDefinitionCollection.__getitem__
def __getitem__(self, key)
Definition: filters.py:54
lsst.obs.base.filters.FilterDefinitionCollection.__str__
def __str__(self)
Definition: filters.py:60
lsst.obs.base.filters.FilterDefinitionCollection.__len__
def __len__(self)
Definition: filters.py:57
lsst.obs.base.filters.FilterDefinitionCollection.__init__
def __init__(self, *filters)
Definition: filters.py:51
lsst.obs.base.filters.FilterDefinition.set
set
Definition: filters.py:150
lsst.obs.base.filters.FilterDefinition.defineFilter
def defineFilter(self)
Definition: filters.py:174
lsst.obs.base.filters.FilterDefinitionCollection
Definition: filters.py:36
lsst.obs.base.filters.FilterDefinition.__post_init__
def __post_init__(self)
Definition: filters.py:155
lsst.obs.base.filters.FilterDefinitionCollection.reset
def reset(cls)
Definition: filters.py:88
lsst.obs.base.filters.FilterDefinition.__str__
def __str__(self)
Definition: filters.py:160
lsst.obs.base.filters.FilterDefinitionCollection._filters
_filters
Definition: filters.py:52
lsst.obs.base.filters.FilterDefinition
Definition: filters.py:97