Coverage for python/lsst/obs/lsst/filters.py : 100%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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__ = ()
24import re
25from lsst.obs.base import FilterDefinition, FilterDefinitionCollection
26from .translators.lsst import FILTER_DELIMITER
29def addFilter(filter_dict, band, physical_filter, lambdaEff=0.0):
30 """Define a filter in filter_dict, to be converted to a Filter later"""
32 if band not in filter_dict:
33 filter_dict[band] = dict(physical_filter=physical_filter,
34 lambdaEff=lambdaEff, alias=[])
35 else:
36 assert filter_dict[band]["lambdaEff"] == lambdaEff
38 filter_dict[band]["alias"].append(physical_filter)
41# The LSST Filters from L. Jones 05/14/2020 - "Edges" = 5% of peak throughput
42# See https://github.com/rhiannonlynne/notebooks/blob/master/Filter%20Characteristics.ipynb # noqa: W505
43#
44# N.b. DM-26623 requests that these physical names be updated once
45# the camera team has decided upon the final values (CAP-617)
47LsstCamFiltersBaseline = FilterDefinitionCollection(
48 FilterDefinition(physical_filter="NONE", band="NONE",
49 lambdaEff=0.0,
50 alias={"no_filter", "OPEN"}),
51 FilterDefinition(physical_filter="u", band="u",
52 lambdaEff=368.48, lambdaMin=320.00, lambdaMax=408.60),
53 FilterDefinition(physical_filter="g", band="g",
54 lambdaEff=480.20, lambdaMin=386.40, lambdaMax=567.00),
55 FilterDefinition(physical_filter="r", band="r",
56 lambdaEff=623.12, lambdaMin=537.00, lambdaMax=706.00),
57 FilterDefinition(physical_filter="i", band="i",
58 lambdaEff=754.17, lambdaMin=676.00, lambdaMax=833.00),
59 FilterDefinition(physical_filter="z", band="z",
60 lambdaEff=869.05, lambdaMin=803.00, lambdaMax=938.60),
61 FilterDefinition(physical_filter="y", band="y",
62 lambdaEff=973.64, lambdaMin=908.40, lambdaMax=1099.00),
63)
65#
66# Define the filters present in the BOT.
67# According to Tony Johnson the possible physical filters are
68# ColorFWheel [SDSSu,SDSSg,SDSSr,SDSSi,SDSSz,SDSSY,
69# 480nm,650nm,750nm,870nm,950nm,970nm]
70# SpotProjFWheel [grid,spot,empty3,empty4,empty5,empty6]
71# NeutralFWheel [ND_OD1.0,ND_OD0.5,ND_OD0.3,empty,ND_OD2.0,ND_OD0.7]
72# where:
73# ColorFWheel and SpotProjFWheel are mutually exclusive and
74# both appear in FILTER,
75# NeutralFWheel appears in FILTER2
76#
77# Experimentally we also see FILTER2 values of:
78# ['ND_OD0.01', 'ND_OD0.05', 'ND_OD0.4', 'ND_OD3.0', 'ND_OD4.0']
79#
80# The band names are not yet defined, so I'm going to invent them
83BOTFilters_dict = {}
84for physical_filter in [
85 "empty",
86 "SDSSu",
87 "SDSSg",
88 "SDSSr",
89 "SDSSi",
90 "SDSSz",
91 "SDSSY",
92 "480nm",
93 "650nm",
94 "750nm",
95 "870nm",
96 "950nm",
97 "970nm",
98 "grid",
99 "spot",
100 "empty3",
101 "empty4",
102 "empty5",
103 "empty6",
104]:
105 mat = re.search(r"^SDSS(.)$", physical_filter)
106 if mat:
107 band = mat.group(1).lower()
109 lsstCamFilter = [f for f in LsstCamFiltersBaseline if f.band == band][0]
110 lambdaEff = lsstCamFilter.lambdaEff
111 else:
112 if re.search(r"^empty[3-6]$", physical_filter):
113 band = "empty"
114 else:
115 band = physical_filter
116 lambdaEff = 0.0
118 addFilter(BOTFilters_dict, band, physical_filter, lambdaEff=lambdaEff)
120 ndFilters = ["empty", "ND_OD0.1", "ND_OD0.3", "ND_OD0.5", "ND_OD0.7", "ND_OD1.0", "ND_OD2.0"]
121 # We found these additional filters in BOT data files:
122 ndFilters += ['ND_OD0.01', 'ND_OD0.05', 'ND_OD0.4', 'ND_OD3.0', 'ND_OD4.0']
124 for nd in ndFilters:
125 pf = f"{physical_filter}{FILTER_DELIMITER}{nd}" # fully qualified physical filter
127 # When one of the filters is empty we can just use the real filter
128 # (e.g. "u" not "u~empty"); but we always need at least one "empty"
129 #
130 # Don't use . in band names, it's just asking for trouble
131 # if they ever end up in filenames
132 if nd == "empty":
133 if band == "empty":
134 af = "empty"
135 else:
136 af = f"{band}"
137 elif band == "empty":
138 pf = nd
139 af = f"{nd.replace('.', '_')}"
140 else:
141 af = f"{band}{FILTER_DELIMITER}{nd.replace('.', '_')}"
143 addFilter(BOTFilters_dict, band=af, physical_filter=pf, lambdaEff=lambdaEff)
145BOTFilters = [
146 FilterDefinition(band="unknown", physical_filter="UNKNOWN", lambdaEff=0.0),
147]
148for band, filt in BOTFilters_dict.items():
149 BOTFilters.append(FilterDefinition(band=band,
150 physical_filter=filt["physical_filter"],
151 lambdaEff=filt["lambdaEff"],
152 alias=filt["alias"]))
153#
154# The filters that we might see in the real LSSTCam (including in SLAC)
155#
156# Note that the filters we'll use on the sky, LsstCamFiltersBaseline, must
157# come first as we're not allocating enough bits in _computeCoaddExposureId
158# for all the BOT composite filters (i.e. "u~ND_OD1.0")
159#
160LSSTCAM_FILTER_DEFINITIONS = FilterDefinitionCollection(
161 *LsstCamFiltersBaseline,
162 *BOTFilters,
163)
165#
166# Filters in SLAC's Test Stand 3
167#
168TS3Filters = [
169 FilterDefinition(band="unknown", physical_filter="UNKNOWN", lambdaEff=0.0),
170 FilterDefinition(physical_filter="275CutOn", lambdaEff=0.0),
171 FilterDefinition(physical_filter="550CutOn", lambdaEff=0.0)]
173TS3_FILTER_DEFINITIONS = FilterDefinitionCollection(
174 *LsstCamFiltersBaseline,
175 *TS3Filters,
176)
177#
178# Filters in SLAC's Test Stand 8
179#
180TS8Filters = [
181 FilterDefinition(band="unknown", physical_filter="UNKNOWN", lambdaEff=0.0),
182 FilterDefinition(physical_filter="275CutOn", lambdaEff=0.0),
183 FilterDefinition(physical_filter="550CutOn", lambdaEff=0.0)]
185TS8_FILTER_DEFINITIONS = FilterDefinitionCollection(
186 *LsstCamFiltersBaseline,
187 *TS8Filters,
188)
191# LATISS filters include a grating in the name so we need to construct
192# filters for each combination of filter+grating.
193_latiss_filters = (
194 FilterDefinition(physical_filter="NONE",
195 lambdaEff=0.0,
196 alias={"no_filter", "OPEN"}),
197 FilterDefinition(physical_filter="blank_bk7_wg05",
198 lambdaEff=0.0),
199 FilterDefinition(physical_filter="KPNO_1111_436nm",
200 band="g",
201 lambdaEff=436.0, lambdaMin=386.0, lambdaMax=486.0),
202 FilterDefinition(physical_filter="KPNO_373A_677nm",
203 band="r",
204 lambdaEff=677.0, lambdaMin=624.0, lambdaMax=730.0),
205 FilterDefinition(physical_filter="KPNO_406_828nm",
206 band="z",
207 lambdaEff=828.0, lambdaMin=738.5, lambdaMax=917.5),
208 FilterDefinition(physical_filter="diffuser",
209 lambdaEff=0.0),
210 FilterDefinition(physical_filter="EMPTY",
211 lambdaEff=0.0),
212 FilterDefinition(physical_filter="UNKNOWN",
213 lambdaEff=0.0),
214 FilterDefinition(physical_filter="BG40",
215 # band="g", # afw only allows one g filter
216 lambdaEff=472.0, lambdaMin=334.5, lambdaMax=609.5),
217 FilterDefinition(physical_filter="quadnotch1",
218 lambdaEff=0.0),
219 FilterDefinition(physical_filter="RG610",
220 lambdaEff=0.0),
221)
223# Form a new set of filter definitions from all the explicit filters
224_latiss_gratings = ("ronchi90lpmm", "ronchi170lpmm", "EMPTY", "NONE", "UNKNOWN")
226# Include the filters without the grating in case someone wants
227# to retrieve a filter by an actual filter name
228_latiss_filter_and_grating = [f for f in _latiss_filters]
230for filter in _latiss_filters:
231 for grating in _latiss_gratings:
232 # FilterDefinition is a frozen dataclass
233 new_name = FILTER_DELIMITER.join([filter.physical_filter, grating])
235 # Also need to update aliases
236 new_aliases = {FILTER_DELIMITER.join([a, grating]) for a in filter.alias}
238 combo = FilterDefinition(physical_filter=new_name,
239 lambdaEff=filter.lambdaEff,
240 lambdaMin=filter.lambdaMin,
241 lambdaMax=filter.lambdaMax,
242 alias=new_aliases)
243 _latiss_filter_and_grating.append(combo)
246LATISS_FILTER_DEFINITIONS = FilterDefinitionCollection(*_latiss_filter_and_grating)
249LSST_IMSIM_FILTER_DEFINITIONS = FilterDefinitionCollection(
250 # These were computed using throughputs 1.4 and
251 # lsst.sims.photUtils.BandpassSet.
252 FilterDefinition(physical_filter="u_sim_1.4",
253 band="u",
254 lambdaEff=367.070, lambdaMin=308.0, lambdaMax=408.6),
255 FilterDefinition(physical_filter="g_sim_1.4",
256 band="g",
257 lambdaEff=482.685, lambdaMin=386.5, lambdaMax=567.0),
258 FilterDefinition(physical_filter="r_sim_1.4",
259 band="r",
260 lambdaEff=622.324, lambdaMin=537.0, lambdaMax=706.0),
261 FilterDefinition(physical_filter="i_sim_1.4",
262 band="i",
263 lambdaEff=754.598, lambdaMin=676.0, lambdaMax=833.0),
264 FilterDefinition(physical_filter="z_sim_1.4",
265 band="z",
266 lambdaEff=869.090, lambdaMin=803.0, lambdaMax=938.6),
267 FilterDefinition(physical_filter="y_sim_1.4",
268 band="y",
269 lambdaEff=971.028, lambdaMin=908.4, lambdaMax=1096.3)
270)
272# ###########################################################################
273#
274# ComCam
275#
276# See https://jira.lsstcorp.org/browse/DM-21706
278ComCamFilters_dict = {}
279for band, sn in [("u", "SN-05"),
280 ("u", "SN-02"), # not yet coated
281 ("u", "SN-06"), # not yet coated
282 ("g", "SN-07"),
283 ("g", "SN-01"),
284 ("r", "SN-03"),
285 ("i", "SN-06"),
286 ("z", "SN-03"),
287 ("z", "SN-02"),
288 ("y", "SN-04"),
289 ]:
290 physical_filter = f"{band}_{sn[3:]}"
291 lsstCamFilter = [f for f in LsstCamFiltersBaseline if f.band == band][0]
292 lambdaEff = lsstCamFilter.lambdaEff
294 addFilter(ComCamFilters_dict, band, physical_filter, lambdaEff=lambdaEff)
297ComCamFilters = [
298 FilterDefinition(band="unknown", physical_filter="UNKNOWN", lambdaEff=0.0),
299]
300for band, filt in ComCamFilters_dict.items():
301 ComCamFilters.append(FilterDefinition(band=band,
302 physical_filter=filt["physical_filter"],
303 lambdaEff=filt["lambdaEff"],
304 alias=filt["alias"]))
306COMCAM_FILTER_DEFINITIONS = FilterDefinitionCollection(
307 *ComCamFilters,
308)