Coverage for python/lsst/obs/lsst/filters.py: 100%
54 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-19 14:52 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-19 14:52 +0000
1# This file is part of obs_lsst.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://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/>.
22__all__ = (
23 "LSSTCAM_FILTER_DEFINITIONS",
24 "LATISS_FILTER_DEFINITIONS",
25 "LSSTCAM_IMSIM_FILTER_DEFINITIONS",
26 "TS3_FILTER_DEFINITIONS",
27 "TS8_FILTER_DEFINITIONS",
28 "COMCAM_FILTER_DEFINITIONS",
29)
31from lsst.obs.base import FilterDefinition, FilterDefinitionCollection
32from .translators.lsst import FILTER_DELIMITER
35def addFilter(filter_dict, band, physical_filter):
36 """Define a filter in filter_dict, to be converted to a Filter later"""
38 # index by band but keep distinct physical filters by band
39 # since there can be many physical filters for a single band
40 if band not in filter_dict:
41 filter_dict[band] = {}
43 filter_dict[band][physical_filter] = dict(physical_filter=physical_filter,
44 band=band, alias=[],
45 )
48# The LSST Filters from L. Jones 05/14/2020
49#
50# N.b. DM-26623 requests that these physical names be updated once
51# the camera team has decided upon the final values (CAP-617)
53LsstCamFiltersBaseline = FilterDefinitionCollection(
54 FilterDefinition(physical_filter="empty", band="white",
55 alias={"no_filter", "open"}),
56 FilterDefinition(physical_filter="u", band="u"),
57 FilterDefinition(physical_filter="g", band="g"),
58 FilterDefinition(physical_filter="r", band="r"),
59 FilterDefinition(physical_filter="i", band="i"),
60 FilterDefinition(physical_filter="z", band="z"),
61 FilterDefinition(physical_filter="y", band="y"),
62)
64#
65# Define the filters present in the BOT.
66# According to Tony Johnson the possible physical filters are
67# ColorFWheel [SDSSu,SDSSg,SDSSr,SDSSi,SDSSz,SDSSY,
68# 480nm,650nm,750nm,870nm,950nm,970nm]
69# SpotProjFWheel [grid,spot,empty3,empty4,empty5,empty6]
70# NeutralFWheel [ND_OD1.0,ND_OD0.5,ND_OD0.3,empty,ND_OD2.0,ND_OD0.7]
71# where:
72# ColorFWheel and SpotProjFWheel are mutually exclusive and
73# both appear in FILTER,
74# NeutralFWheel appears in FILTER2
75#
76# Experimentally we also see FILTER2 values of:
77# ['ND_OD0.01', 'ND_OD0.05', 'ND_OD0.4', 'ND_OD3.0', 'ND_OD4.0']
78#
79# The band names are not yet defined, so I'm going to invent them
81# Map the BOT filters to corresponding band explicitly
82BOT_filter_map = {
83 "empty": "white",
84 "SDSSu": "u",
85 "SDSSg": "g",
86 "SDSSr": "r",
87 "SDSSi": "i",
88 "SDSSz": "z",
89 "SDSSY": "y",
90 "480nm": "g",
91 "650nm": "r",
92 "750nm": "i",
93 "870nm": "z",
94 "950nm": "y",
95 "970nm": "y",
96 "grid": "grid",
97 "spot": "spot",
98}
100BOTFilters_dict = {}
101for physical_filter, band in BOT_filter_map.items():
102 if physical_filter == "empty":
103 pass # Already defined above
104 else:
105 addFilter(BOTFilters_dict, band, physical_filter)
107 # Empty ND is removed by metadata translator so is not needed here
108 ndFilters = ["ND_OD0.1", "ND_OD0.3", "ND_OD0.5", "ND_OD0.7", "ND_OD1.0", "ND_OD2.0"]
109 # We found these additional filters in BOT data files:
110 ndFilters += ['ND_OD0.01', 'ND_OD0.05', 'ND_OD0.4', 'ND_OD3.0', 'ND_OD4.0']
112 for nd in ndFilters:
113 # fully qualified physical filter
114 phys_plus_nd = f"{physical_filter}{FILTER_DELIMITER}{nd}"
116 # When one of the filters is empty we can just use the real filter
117 # (e.g. "u" not "u~empty"); but we always need at least one "empty"
118 if band == "white":
119 # Use the ND on its own
120 phys_plus_nd = nd
122 # Use a generic ND modifier for the band
123 ndband = f"{band}{FILTER_DELIMITER}nd"
125 addFilter(BOTFilters_dict, band=ndband, physical_filter=phys_plus_nd)
127BOTFilters = [
128 FilterDefinition(band="unknown", physical_filter="unknown"),
129]
130for band, physical_filters in BOTFilters_dict.items():
131 for physical_filter, filter_defn in physical_filters.items():
132 BOTFilters.append(FilterDefinition(**filter_defn))
134#
135# The filters that we might see in the real LSSTCam (including in SLAC)
136#
137# Note that the filters we'll use on the sky, LsstCamFiltersBaseline, must
138# come first as we're not allocating enough bits in _computeCoaddExposureId
139# for all the BOT composite filters (i.e. "u~ND_OD1.0")
140#
141LSSTCAM_FILTER_DEFINITIONS = FilterDefinitionCollection(
142 *LsstCamFiltersBaseline,
143 *BOTFilters,
144)
146#
147# Filters in SLAC's Test Stand 3
148#
149TS3Filters = [
150 FilterDefinition(band="unknown", physical_filter="unknown"),
151 FilterDefinition(physical_filter="275CutOn"),
152 FilterDefinition(physical_filter="550CutOn")]
154TS3_FILTER_DEFINITIONS = FilterDefinitionCollection(
155 *LsstCamFiltersBaseline,
156 *TS3Filters,
157)
158#
159# Filters in SLAC's Test Stand 8
160#
161TS8Filters = [
162 FilterDefinition(band="unknown", physical_filter="unknown"),
163 FilterDefinition(physical_filter="275CutOn"),
164 FilterDefinition(physical_filter="550CutOn")]
166TS8_FILTER_DEFINITIONS = FilterDefinitionCollection(
167 *LsstCamFiltersBaseline,
168 *TS8Filters,
169)
172# LATISS filters include a grating in the name so we need to construct
173# filters for each combination of filter+grating.
174_latiss_filters = (
175 FilterDefinition(physical_filter="empty",
176 band="white",
177 alias={"no_filter", "open"}),
178 FilterDefinition(physical_filter="blank_bk7_wg05",
179 band="white"),
180 FilterDefinition(physical_filter="KPNO_1111_436nm",
181 band="g"),
182 FilterDefinition(physical_filter="KPNO_373A_677nm",
183 band="r"),
184 FilterDefinition(physical_filter="KPNO_406_828nm",
185 band="z"),
186 FilterDefinition(physical_filter="diffuser",
187 band="diffuser"),
188 FilterDefinition(physical_filter="unknown",
189 band="unknown"),
190 FilterDefinition(physical_filter="BG40",
191 band="g",
192 afw_name="bg"),
193 FilterDefinition(physical_filter="quadnotch1",
194 band="notch"),
195 FilterDefinition(physical_filter="RG610",
196 band="r",
197 afw_name="rg"),
198 FilterDefinition(physical_filter="FELH0600",
199 band="r",
200 afw_name="rg"),
201 FilterDefinition(physical_filter="SDSSg",
202 band="g"),
203 FilterDefinition(physical_filter="SDSSr",
204 band="r"),
205 FilterDefinition(physical_filter="SDSSi",
206 band="i"),
207)
209# Form a new set of filter definitions from all the explicit gratings
210_latiss_gratings = ("ronchi90lpmm", "ronchi170lpmm", "empty", "unknown", "holo4_003")
212# Include the filters without the grating in case someone wants
213# to retrieve a filter by an actual filter name
214_latiss_filter_and_grating = [f for f in _latiss_filters]
216for filter in _latiss_filters:
217 for grating in _latiss_gratings:
218 # The diffuser "filter" was never used with gratings
219 # so skip it
220 if filter.physical_filter == "diffuser":
221 continue
223 # FilterDefinition is a frozen dataclass
224 new_name = FILTER_DELIMITER.join([filter.physical_filter, grating])
226 # Also need to update aliases
227 new_aliases = {FILTER_DELIMITER.join([a, grating]) for a in filter.alias}
229 # For gratings set the band to the band of the filter
230 combo = FilterDefinition(physical_filter=new_name,
231 band=filter.band,
232 afw_name=filter.afw_name,
233 alias=new_aliases)
234 _latiss_filter_and_grating.append(combo)
237LATISS_FILTER_DEFINITIONS = FilterDefinitionCollection(*_latiss_filter_and_grating)
240LSSTCAM_IMSIM_FILTER_DEFINITIONS = FilterDefinitionCollection(
241 # These were computed using throughputs 1.4 and
242 # lsst.sims.photUtils.BandpassSet.
243 FilterDefinition(physical_filter="u_sim_1.4",
244 band="u"),
245 FilterDefinition(physical_filter="g_sim_1.4",
246 band="g"),
247 FilterDefinition(physical_filter="r_sim_1.4",
248 band="r"),
249 FilterDefinition(physical_filter="i_sim_1.4",
250 band="i"),
251 FilterDefinition(physical_filter="z_sim_1.4",
252 band="z"),
253 FilterDefinition(physical_filter="y_sim_1.4",
254 band="y"),
255)
257# ###########################################################################
258#
259# ComCam
260#
261# See https://jira.lsstcorp.org/browse/DM-21706
263ComCamFilters_dict = {}
264for band, sn in [("u", "SN-05"), # incorrect sub thickness
265 ("u", "SN-02"), # not yet coated
266 ("u", "SN-06"), # not yet coated
267 ("g", "SN-07"), # bad cosmetics
268 ("g", "SN-01"),
269 ("r", "SN-03"),
270 ("i", "SN-06"),
271 ("z", "SN-03"),
272 ("z", "SN-02"), # failed specs
273 ("y", "SN-04"),
274 ]:
275 physical_filter = f"{band}_{sn[3:]}"
276 lsstCamFilter = [f for f in LsstCamFiltersBaseline if f.band == band][0]
278 addFilter(ComCamFilters_dict, band, physical_filter)
281ComCamFilters = [
282 FilterDefinition(band="white", physical_filter="empty"),
283 FilterDefinition(band="unknown", physical_filter="unknown"),
284]
285for band, physical_filters in ComCamFilters_dict.items():
286 for physical_filter, filter_defn in physical_filters.items():
287 ComCamFilters.append(FilterDefinition(**filter_defn))
289COMCAM_FILTER_DEFINITIONS = FilterDefinitionCollection(
290 *ComCamFilters,
291)