Coverage for python/lsst/obs/lsst/filters.py: 98%
75 statements
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-23 04:18 -0700
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-23 04:18 -0700
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 )
48EmptyFilter = FilterDefinition(physical_filter="empty", band="white",
49 alias={"no_filter", "open"})
51# Generic filters used by PhoSim and UCDCam
52LsstCamFiltersGeneric = FilterDefinitionCollection(
53 FilterDefinition(physical_filter="u", band="u"),
54 FilterDefinition(physical_filter="g", band="g"),
55 FilterDefinition(physical_filter="r", band="r"),
56 FilterDefinition(physical_filter="i", band="i"),
57 FilterDefinition(physical_filter="z", band="z"),
58 FilterDefinition(physical_filter="y", band="y"),
59)
61# The LSST Filters from Tony Johnson, 05/09/2023, in DM-38882.
62LsstCamFiltersBaseline = FilterDefinitionCollection(
63 FilterDefinition(physical_filter="ph_5", band="white"), # pinhole filter
64 FilterDefinition(physical_filter="ef_43", band="white"), # "empty" filter
65 FilterDefinition(physical_filter="u_24", band="u"),
66 FilterDefinition(physical_filter="g_6", band="g"),
67 FilterDefinition(physical_filter="r_57", band="r"),
68 FilterDefinition(physical_filter="i_39", band="i"),
69 FilterDefinition(physical_filter="z_20", band="z"),
70 FilterDefinition(physical_filter="y_10", band="y"),
71)
73#
74# Define the filters present in the BOT.
75# According to Tony Johnson the possible physical filters are
76# ColorFWheel [SDSSu,SDSSg,SDSSr,SDSSi,SDSSz,SDSSY,
77# 480nm,650nm,750nm,870nm,950nm,970nm]
78# SpotProjFWheel [grid,spot,empty3,empty4,empty5,empty6]
79# NeutralFWheel [ND_OD1.0,ND_OD0.5,ND_OD0.3,empty,ND_OD2.0,ND_OD0.7]
80# where:
81# ColorFWheel and SpotProjFWheel are mutually exclusive and
82# both appear in FILTER,
83# NeutralFWheel appears in FILTER2
84#
85# Experimentally we also see FILTER2 values of:
86# ['ND_OD0.01', 'ND_OD0.05', 'ND_OD0.4', 'ND_OD3.0', 'ND_OD4.0']
87#
88# The band names are not yet defined, so I'm going to invent them
90# Map the BOT filters to corresponding band explicitly
91BOT_filter_map = {
92 "empty": "white",
93 "SDSSu": "u",
94 "SDSSg": "g",
95 "SDSSr": "r",
96 "SDSSi": "i",
97 "SDSSz": "z",
98 "SDSSY": "y",
99 "480nm": "g",
100 "650nm": "r",
101 "750nm": "i",
102 "870nm": "z",
103 "950nm": "y",
104 "970nm": "y",
105 "grid": "grid",
106 "spot": "spot",
107}
109BOTFilters_dict = {}
110for physical_filter, band in BOT_filter_map.items():
111 if physical_filter == "empty":
112 pass # Already defined above
113 else:
114 addFilter(BOTFilters_dict, band, physical_filter)
116 # Empty ND is removed by metadata translator so is not needed here
117 ndFilters = ["ND_OD0.1", "ND_OD0.3", "ND_OD0.5", "ND_OD0.7", "ND_OD1.0", "ND_OD2.0"]
118 # We found these additional filters in BOT data files:
119 ndFilters += ['ND_OD0.01', 'ND_OD0.05', 'ND_OD0.4', 'ND_OD3.0', 'ND_OD4.0']
121 for nd in ndFilters:
122 # fully qualified physical filter
123 phys_plus_nd = f"{physical_filter}{FILTER_DELIMITER}{nd}"
125 # When one of the filters is empty we can just use the real filter
126 # (e.g. "u" not "u~empty"); but we always need at least one "empty"
127 if band == "white":
128 # Use the ND on its own
129 phys_plus_nd = nd
131 # Use a generic ND modifier for the band
132 ndband = f"{band}{FILTER_DELIMITER}nd"
134 addFilter(BOTFilters_dict, band=ndband, physical_filter=phys_plus_nd)
136BOTFilters = [
137 FilterDefinition(band="unknown", physical_filter="unknown"),
138]
139for band, physical_filters in BOTFilters_dict.items():
140 for physical_filter, filter_defn in physical_filters.items():
141 BOTFilters.append(FilterDefinition(**filter_defn))
143#
144# These are the filters used by the CCOB for both TS8 and LSSTCam.
145# For the testing of LSSTCam at SLAC, they will be combined with the
146# real LSSTCam filters, so we include those combinations in the CCOB
147# filter definitions.
148#
149CCOB_filter_map = {
150 "": "white",
151 "HIGH": "white",
152 "LOW": "white",
153 "uv": "u",
154 "blue": "g",
155 "red": "r",
156 "nm750": "i",
157 "nm850": "z",
158 "nm960": "y",
159}
161CCOBFilters = []
162for lsst_filter_def in (EmptyFilter, *LsstCamFiltersBaseline):
163 lsstcam_filter = lsst_filter_def.physical_filter
164 lsstcam_band = lsst_filter_def.band
165 for ccob_filter, ccob_band in CCOB_filter_map.items():
166 if lsstcam_band != "white" and ccob_band != "white" and band != ccob_band:
167 # Skip disallowed filter combinations based on band values.
168 continue
169 if ccob_filter == "":
170 # This would correspond to an entry already in
171 # LSSTCamFilterBaseline
172 continue
173 filters = lsstcam_filter, ccob_filter
174 physical_filter = FILTER_DELIMITER.join(filters).strip(FILTER_DELIMITER)
175 if lsstcam_band == "white":
176 band = ccob_band
177 else:
178 band = lsstcam_band
179 if physical_filter == "": 179 ↛ 180line 179 didn't jump to line 180, because the condition on line 179 was never true
180 physical_filter = "empty"
181 CCOBFilters.append(FilterDefinition(band=band, physical_filter=physical_filter))
183#
184# The filters that we might see in the real LSSTCam (including in SLAC)
185#
186# Note that the filters we'll use on the sky, LsstCamFiltersBaseline, must
187# come first as we're not allocating enough bits in _computeCoaddExposureId
188# for all the BOT composite filters (i.e. "u~ND_OD1.0")
189#
190LSSTCAM_FILTER_DEFINITIONS = FilterDefinitionCollection(
191 EmptyFilter,
192 *LsstCamFiltersBaseline,
193 *BOTFilters,
194 *CCOBFilters,
195)
197GENERIC_FILTER_DEFINITIONS = FilterDefinitionCollection(
198 EmptyFilter,
199 *LsstCamFiltersGeneric,
200)
202#
203# Filters in SLAC's Test Stand 3
204#
205TS3Filters = [
206 FilterDefinition(band="unknown", physical_filter="unknown"),
207 FilterDefinition(physical_filter="275CutOn"),
208 FilterDefinition(physical_filter="550CutOn")]
210TS3_FILTER_DEFINITIONS = FilterDefinitionCollection(
211 EmptyFilter,
212 *LsstCamFiltersGeneric,
213 *TS3Filters,
214)
215#
216# Filters in SLAC's Test Stand 8
217#
218TS8Filters = [
219 FilterDefinition(band="unknown", physical_filter="unknown"),
220 FilterDefinition(physical_filter="275CutOn"),
221 FilterDefinition(physical_filter="550CutOn")]
223TS8_FILTER_DEFINITIONS = FilterDefinitionCollection(
224 EmptyFilter,
225 *LsstCamFiltersGeneric,
226 *LsstCamFiltersBaseline,
227 *TS8Filters,
228 *CCOBFilters,
229)
232# LATISS filters include a grating in the name so we need to construct
233# filters for each combination of filter+grating.
234_latiss_filters = (
235 FilterDefinition(physical_filter="empty",
236 band="white",
237 alias={"no_filter", "open"}),
238 FilterDefinition(physical_filter="blank_bk7_wg05",
239 band="white"),
240 FilterDefinition(physical_filter="KPNO_1111_436nm",
241 band="g"),
242 FilterDefinition(physical_filter="KPNO_373A_677nm",
243 band="r"),
244 FilterDefinition(physical_filter="KPNO_406_828nm",
245 band="z"),
246 FilterDefinition(physical_filter="diffuser",
247 band="diffuser"),
248 FilterDefinition(physical_filter="unknown",
249 band="unknown"),
250 FilterDefinition(physical_filter="BG40",
251 band="g",
252 afw_name="bg"),
253 FilterDefinition(physical_filter="BG40_65mm_1",
254 band="g",
255 afw_name="bg"),
256 FilterDefinition(physical_filter="BG40_65mm_2",
257 band="g",
258 afw_name="bg"),
259 FilterDefinition(physical_filter="quadnotch1",
260 band="notch"),
261 FilterDefinition(physical_filter="RG610",
262 band="r",
263 afw_name="rg"),
264 FilterDefinition(physical_filter="OG550_65mm_1",
265 band="g",
266 afw_name="bg"),
267 FilterDefinition(physical_filter="OG550_65mm_2",
268 band="g",
269 afw_name="bg"),
270 FilterDefinition(physical_filter="FELH0600",
271 band="r",
272 afw_name="rg"),
273 FilterDefinition(physical_filter="SDSSg",
274 band="g"),
275 FilterDefinition(physical_filter="SDSSr",
276 band="r"),
277 FilterDefinition(physical_filter="SDSSi",
278 band="i"),
279 FilterDefinition(physical_filter="SDSSu_65mm",
280 band="u"),
281 FilterDefinition(physical_filter="SDSSg_65mm",
282 band="g"),
283 FilterDefinition(physical_filter="SDSSr_65mm",
284 band="r"),
285 FilterDefinition(physical_filter="SDSSi_65mm",
286 band="i"),
287 FilterDefinition(physical_filter="SDSSz_65mm",
288 band="z"),
289 FilterDefinition(physical_filter="SDSSy_65mm",
290 band="y"),
291)
293# Form a new set of filter definitions from all the explicit gratings
294_latiss_gratings = ("ronchi90lpmm", "ronchi170lpmm", "empty", "unknown", "holo4_003", "blue300lpmm_qn1")
296# Include the filters without the grating in case someone wants
297# to retrieve a filter by an actual filter name
298_latiss_filter_and_grating = [f for f in _latiss_filters]
300for filter in _latiss_filters:
301 for grating in _latiss_gratings:
302 # The diffuser "filter" was never used with gratings
303 # so skip it
304 if filter.physical_filter == "diffuser":
305 continue
307 # FilterDefinition is a frozen dataclass
308 new_name = FILTER_DELIMITER.join([filter.physical_filter, grating])
310 # Also need to update aliases
311 new_aliases = {FILTER_DELIMITER.join([a, grating]) for a in filter.alias}
313 # For gratings set the band to the band of the filter
314 combo = FilterDefinition(physical_filter=new_name,
315 band=filter.band,
316 afw_name=filter.afw_name,
317 alias=new_aliases)
318 _latiss_filter_and_grating.append(combo)
321LATISS_FILTER_DEFINITIONS = FilterDefinitionCollection(*_latiss_filter_and_grating)
324LSSTCAM_IMSIM_FILTER_DEFINITIONS = FilterDefinitionCollection(
325 # These were computed using throughputs 1.4 and
326 # lsst.sims.photUtils.BandpassSet.
327 FilterDefinition(physical_filter="u_sim_1.4",
328 band="u"),
329 FilterDefinition(physical_filter="g_sim_1.4",
330 band="g"),
331 FilterDefinition(physical_filter="r_sim_1.4",
332 band="r"),
333 FilterDefinition(physical_filter="i_sim_1.4",
334 band="i"),
335 FilterDefinition(physical_filter="z_sim_1.4",
336 band="z"),
337 FilterDefinition(physical_filter="y_sim_1.4",
338 band="y"),
339)
341# ###########################################################################
342#
343# ComCam
344#
345# See https://jira.lsstcorp.org/browse/DM-21706
347ComCamFilters_dict = {}
348for band, sn in [("u", "SN-05"), # incorrect sub thickness
349 ("u", "SN-02"), # not yet coated
350 ("u", "SN-06"), # not yet coated
351 ("g", "SN-07"), # bad cosmetics
352 ("g", "SN-01"),
353 ("r", "SN-03"),
354 ("i", "SN-06"),
355 ("z", "SN-03"),
356 ("z", "SN-02"), # failed specs
357 ("y", "SN-04"),
358 ]:
359 physical_filter = f"{band}_{sn[3:]}"
360 lsstCamFilter = [f for f in LsstCamFiltersBaseline if f.band == band][0]
362 addFilter(ComCamFilters_dict, band, physical_filter)
365ComCamFilters = [
366 FilterDefinition(band="white", physical_filter="empty"),
367 FilterDefinition(band="unknown", physical_filter="unknown"),
368]
369for band, physical_filters in ComCamFilters_dict.items():
370 for physical_filter, filter_defn in physical_filters.items():
371 ComCamFilters.append(FilterDefinition(**filter_defn))
373COMCAM_FILTER_DEFINITIONS = FilterDefinitionCollection(
374 *ComCamFilters,
375)