Coverage for src/galsbi/ucat/filters_util.py: 99%
182 statements
« prev ^ index » next coverage.py v7.17.2, created at 2026-11-09 18:05 +0000
« prev ^ index » next coverage.py v7.17.2, created at 2026-11-09 18:05 +0000
1# Copyright (C) 2018 ETH Zurich, Institute for Particle Physics and Astrophysics
3"""
4Created Sept 2021
5author: Tomasz Kacprzak
7This script manipulates filter information. It can create the filters_collection.h5
8file, which contains all listed filters we use.
9"""
11import os
12from collections import OrderedDict
14import h5py
15import numpy as np
16from cosmic_toolbox import logger
17from scipy.integrate import simpson as simps
19LOGGER = logger.get_logger(__file__)
22def create_filter_collection(dirpath_res):
23 """
24 Create a filter collection file
25 Currently supported instruments: DECam, VISTA, GenericBessel
26 """
28 def add_decam_filters(filters_collect):
29 # data downloaded from:
30 # https://noirlab.edu/science/programs/ctio/filters/Dark-Energy-Camera
31 filepath_filters = os.path.join(
32 dirpath_res, "filters/filters_DECam/DECam_filters_with_atm.txt"
33 )
34 # LAMBDA g r i z Y atm
35 filter_data = np.loadtxt(filepath_filters).T
36 atm = filter_data[-1]
37 lam = filter_data[0]
38 for i, b in enumerate(["g", "r", "i", "z", "Y"]):
39 filters_collect[f"DECam_{b}"] = dict(amp=filter_data[i + 1], lam=lam)
40 filters_collect[f"DECamNoAtm_{b}"] = dict(
41 amp=filter_data[i + 1] / atm, lam=lam
42 )
43 LOGGER.info(f"added DECam {b}-band")
45 b = "u"
46 decam_u_lam, decam_u_amp = np.loadtxt(
47 os.path.join(dirpath_res, "filters/filters_DECam/DECam_2014_u.res")
48 ).T
49 filters_collect[f"DECam_{b}"] = dict(amp=decam_u_amp, lam=decam_u_lam)
50 LOGGER.info(f"added DECam {b}-band")
52 def add_bessel_filters(filters_collect):
53 # generic Bessel filters from from
54 # http://adsabs.harvard.edu/abs/1990PASP..102.1181B
55 # http://svo2.cab.inta-csic.es/svo/theory/fps3/index.php?mode=browse&gname=Generic&asttype=
56 bands = ["B", "I", "R", "U", "V"]
58 for b in bands:
59 lam, amp = np.loadtxt(
60 os.path.join(
61 dirpath_res,
62 f"filters/filters_Bessell/Generic_Bessell.{b}.dat"
63 )
64 ).T
65 filters_collect[f"GenericBessel_{b}"] = dict(amp=amp, lam=lam)
66 LOGGER.info(f"added GenericBessel {b}-band")
68 bands = ["J", "H", "K", "L", "M"]
69 for b in bands:
70 lam, amp = np.loadtxt(
71 os.path.join(
72 dirpath_res,
73 f"filters/filters_Bessell/Generic_Bessell_JHKLM.{b}.dat"
74 )
75 ).T
76 filters_collect[f"GenericBessel_{b}"] = dict(amp=amp, lam=lam)
77 LOGGER.info(f"added GenericBessel {b}-band")
79 def add_johnson_filters(filters_collect):
80 # generic Bessel filters from from
81 # http://adsabs.harvard.edu/abs/1990PASP..102.1181B
82 # http://svo2.cab.inta-csic.es/svo/theory/fps3/index.php?mode=browse&gname=Generic&asttype=
83 bands = ["U", "B", "V", "R", "I", "J", "M"]
85 for b in bands:
86 lam, amp = np.loadtxt(
87 os.path.join(
88 dirpath_res,
89 f"filters/filters_Johnson/Generic_Johnson.{b}.dat"
90 )
91 ).T
92 filters_collect[f"GenericJohnson_{b}"] = dict(amp=amp, lam=lam)
93 LOGGER.info(f"added GenericJohnson {b}-band")
95 def add_cosmos_filters(filters_collect):
96 # https://ftp.iap.fr/pub/from_users/hjmcc/COSMOS2015/COSMOS2015_Laigle+.header
97 # https://cosmos.astro.caltech.edu/page/photom
98 filters = {}
99 filters["Vircam_Ks"] = dict(
100 filename="K_uv.res", telescope="VISTA"
101 ) # Ks_MAG_AUTO
102 filters["Vircam_Y"] = dict(filename="Y_uv.res", telescope="VISTA") # Y_MAG_AUTO
103 filters["Vircam_H"] = dict(filename="H_uv.res", telescope="VISTA") # H_MAG_AUTO
104 filters["Vircam_J"] = dict(filename="J_uv.res", telescope="VISTA") # J_MAG_AUTO
105 filters["SuprimeCam_B"] = dict(
106 filename="B_subaru.res", telescope="SUBARU"
107 ) # B_MAG_AUTO
108 filters["SuprimeCam_V"] = dict(
109 filename="V_subaru.res", telescope="SUBARU"
110 ) # V_MAG_AUTO
111 filters["SuprimeCam_ip"] = dict(
112 filename="i_subaru.res", telescope="SUBARU"
113 ) # ip_MAG_AUTO
114 filters["SuprimeCam_r"] = dict(
115 filename="r_subaru.res", telescope="SUBARU"
116 ) # r_MAG_AUTO
117 filters["SuprimeCam_zp"] = dict(
118 filename="z_subaru.res", telescope="SUBARU"
119 ) # zp_MAG_AUTO
120 filters["SuprimeCam_zpp"] = dict(
121 filename="suprime_FDCCD_z.res", telescope="SUBARU"
122 ) # zpp_MAG_AUTO
123 filters["MegaPrime_u"] = dict(
124 filename="u_megaprime_sagem.res", telescope="CFHT"
125 ) # u_MAG_AUTO
126 filters["SuprimeCam_IA427"] = dict(
127 filename="IA427.SuprimeCam.pb", telescope="SUBARU"
128 ) # IB427_MAG_AUTO
129 filters["SuprimeCam_IB464"] = dict(
130 filename="IA464.SuprimeCam.pb", telescope="SUBARU"
131 ) # IB464_MAG_AUTO
132 filters["SuprimeCam_IA484"] = dict(
133 filename="IA484.SuprimeCam.pb", telescope="SUBARU"
134 ) # IA484_MAG_AUTO
135 filters["SuprimeCam_IA505"] = dict(
136 filename="IA505.SuprimeCam.pb", telescope="SUBARU"
137 ) # IB505_MAG_AUTO
138 filters["SuprimeCam_IA527"] = dict(
139 filename="IA527.SuprimeCam.pb", telescope="SUBARU"
140 ) # IA527_MAG_AUTO
141 filters["SuprimeCam_IA574"] = dict(
142 filename="IA574.SuprimeCam.pb", telescope="SUBARU"
143 ) # IB574_MAG_AUTO
144 filters["SuprimeCam_IA624"] = dict(
145 filename="IA624.SuprimeCam.pb", telescope="SUBARU"
146 ) # IA624_MAG_AUTO
147 filters["SuprimeCam_IA679"] = dict(
148 filename="IA679.SuprimeCam.pb", telescope="SUBARU"
149 ) # IA679_MAG_AUTO
150 filters["SuprimeCam_IA709"] = dict(
151 filename="IA709.SuprimeCam.pb", telescope="SUBARU"
152 ) # IB709_MAG_AUTO
153 filters["SuprimeCam_IA738"] = dict(
154 filename="IA738.SuprimeCam.pb", telescope="SUBARU"
155 ) # IA738_MAG_AUTO
156 filters["SuprimeCam_IA767"] = dict(
157 filename="IA767.SuprimeCam.pb", telescope="SUBARU"
158 ) # IA767_MAG_AUTO
159 filters["SuprimeCam_IA827"] = dict(
160 filename="IA827.SuprimeCam.pb", telescope="SUBARU"
161 ) # IB827_MAG_AUTO
162 filters["SuprimeCam_NB711"] = dict(
163 filename="NB711.SuprimeCam.pb", telescope="SUBARU"
164 ) # NB711_MAG_AUTO
165 filters["SuprimeCam_NB816"] = dict(
166 filename="NB816.SuprimeCam.pb", telescope="SUBARU"
167 ) # NB816_MAG_AUTO
168 filters["wircam_H"] = dict(
169 filename="wircam_H.res", telescope="SUBARU"
170 ) # Hw_MAG_AUTO
171 filters["wircam_Ks"] = dict(
172 filename="wircam_Ks.res", telescope="SUBARU"
173 ) # Ksw_MAG_AUTO
174 filters["irac_ch1"] = dict(
175 filename="irac_ch1.res", telescope="SPITZER"
176 ) # SPLASH_1_MAG
177 filters["irac_ch2"] = dict(
178 filename="irac_ch2.res", telescope="SPITZER"
179 ) # SPLASH_2_MAG
180 filters["irac_ch3"] = dict(
181 filename="irac_ch3.res", telescope="SPITZER"
182 ) # SPLASH_3_MAG
183 filters["irac_ch4"] = dict(
184 filename="irac_ch4.res", telescope="SPITZER"
185 ) # SPLASH_4_MAG
186 filters["mips_24"] = dict(filename="mips24.res", telescope="SPITZER") # MAG_24
187 filters["GALEX_FUV"] = dict(
188 filename="galex1500.res", telescope="GALEX"
189 ) # MAG_GALEX_FUV
190 filters["GALEX_NUV"] = dict(
191 filename="galex2500.res", telescope="GALEX"
192 ) # MAG_GALEX_NUV
194 for f in filters:
195 lam, amp = np.loadtxt(
196 os.path.join(
197 dirpath_res, "filters/filters_cosmos2015", filters[f]["filename"]
198 )
199 ).T
200 filters_collect[f] = dict(amp=amp, lam=lam)
201 LOGGER.info(f"added Cosmos2015 {f}")
203 def add_hsc_filters(filters_collect):
204 # https://hsc-release.mtk.nao.ac.jp/doc/index.php/survey__pdr3/
205 # https://hsc-release.mtk.nao.ac.jp/doc/wp-content/uploads/2021/08/hsc_responses_all_rev4.zip
207 bands = ["g", "r", "r2", "i", "i2", "z", "y"]
208 for b in bands:
209 path = os.path.join(
210 dirpath_res, "filters/filters_hsc_v2018", f"hsc_{b}_v2018.dat"
211 )
212 lam, amp = np.loadtxt(path, comments="#").T
213 filters_collect[f"HyperSuprimeCam_{b}"] = dict(amp=amp, lam=lam)
214 LOGGER.info(f"added HyperSuprimeCam_{b}")
216 def add_sdss_filters(filters_collect):
217 # http://svo2.cab.inta-csic.es/svo/theory/fps3/index.php?mode=browse&gname=SLOAN
218 bands = ["u", "g", "r", "i", "z"]
219 for b in bands:
220 path = os.path.join(dirpath_res, f"filters/filters_sdss/SLOAN_SDSS.{b}.dat")
221 lam, amp = np.loadtxt(path).T
222 filters_collect[f"SDSS_{b}"] = dict(amp=amp, lam=lam)
223 LOGGER.info(f"added SDSS_{b}")
225 def add_vista_filters(filters_collect):
226 # http://svo2.cab.inta-csic.es/svo/theory/fps3/index.php?mode=browse&gname=Paranal&gname2=VISTA
227 bands = ["Z", "Y", "J", "H", "Ks"]
228 for b in bands:
229 path = os.path.join(
230 dirpath_res, f"filters/filters_vista/Paranal_VISTA.{b}.dat"
231 )
232 lam, amp = np.loadtxt(path).T
233 filters_collect[f"VISTA_{b}"] = dict(amp=amp, lam=lam)
234 LOGGER.info(f"added VISTA_{b}")
235 for b in bands:
236 path = os.path.join(
237 dirpath_res,
238 f"filters/filters_vista/Paranal_VISTA.{b}_filter.dat"
239 )
240 lam, amp = np.loadtxt(path).T
241 filters_collect[f"VISTA_NoAtm{b}"] = dict(amp=amp, lam=lam)
242 LOGGER.info(f"added VISTA_NoAtm{b}")
244 def add_omegacam_filters(filters_collect):
245 # https://svo2.cab.inta-csic.es/svo/theory/fps3/index.php?mode=browse&gname=Paranal&gname2=OmegaCAM
246 bands = ["u", "g", "r", "i", "z"]
247 for b in bands:
248 path = os.path.join(
249 dirpath_res,
250 f"filters/filters_OmegaCAM/Paranal_OmegaCAM.{b}_SDSS.dat"
251 )
252 lam, amp = np.loadtxt(path).T
253 filters_collect[f"OmegaCAM_{b}"] = dict(amp=amp, lam=lam)
254 LOGGER.info(f"added OmegaCAM_{b} filter+CCD")
256 def add_lsstcam_filters(filters_collect):
257 # https://github.com/lsst/throughputs/tree/main/baseline
258 bands = ["u", "g", "r", "i", "z", "y"]
259 for b in bands:
260 path = os.path.join(
261 dirpath_res,
262 f"filters/filters_LSSTCam/total_{b}.dat"
263 )
264 lam, amp = np.loadtxt(path).T
265 filters_collect[f"LSSTCam_{b}"] = dict(amp=amp, lam=lam)
266 LOGGER.info(f"added LSSTCam_{b} mirrors+lenses+filter+detector+atmosphere")
268 def add_euclid_filters(filters_collect):
269 # https://svo2.cab.inta-csic.es/svo/theory/fps3/index.php?mode=browse&gname=Euclid
270 path = os.path.join(dirpath_res, "filters/filters_Euclid/Euclid_VIS.vis.dat")
271 lam, amp = np.loadtxt(path).T
272 filters_collect["Euclid_VIS"] = dict(amp=amp, lam=lam)
273 LOGGER.info("added Euclid_VIS filter+instrument")
274 bands_ir = ["Y", "J", "H"]
275 for b in bands_ir:
276 path = os.path.join(
277 dirpath_res,
278 f"filters/filters_Euclid/Euclid_NISP.{b}.dat"
279 )
280 lam, amp = np.loadtxt(path).T
281 filters_collect[f"Euclid_NISP_{b}"] = dict(amp=amp, lam=lam)
282 LOGGER.info(f"added Euclid_NISP_{b} filter+CCD+instrument+optics")
284 LOGGER.info("============ creating filter collection")
285 filters_collect = {}
286 add_decam_filters(filters_collect)
287 add_bessel_filters(filters_collect)
288 add_johnson_filters(filters_collect)
289 add_cosmos_filters(filters_collect)
290 add_hsc_filters(filters_collect)
291 add_sdss_filters(filters_collect)
292 add_vista_filters(filters_collect)
293 add_omegacam_filters(filters_collect)
294 add_lsstcam_filters(filters_collect)
295 add_euclid_filters(filters_collect)
297 return filters_collect
300def store_filter_collection(filepath_collection, filters_collect):
301 # store collection
302 n_filters = len(filters_collect)
303 with h5py.File(filepath_collection, "w") as f:
304 for b in filters_collect:
305 for k in filters_collect[b]:
306 f[f"{b}/{k}"] = filters_collect[b][k]
308 LOGGER.info(f"wrote {filepath_collection} with {n_filters} filters")
309 LOGGER.info(filters_collect.keys())
312def load_filters(filepath_filters, filter_names=None, lam_scale=1):
313 filters = OrderedDict()
314 with h5py.File(filepath_filters, "r") as f:
315 filter_names = list(f.keys()) if filter_names is None else filter_names
316 for b in filter_names:
317 amp = np.array(f[b]["amp"])
318 lam = np.array(f[b]["lam"]) * lam_scale
319 integ = simps(x=lam, y=amp / lam)
320 LOGGER.debug(f"filter {b} integral={integ:2.5e}")
321 filters[b] = dict(amp=amp, lam=lam, integ=integ)
322 return filters
325class UseShortFilterNames:
326 """
327 Interface between short and long filter names
328 """
330 def __init__(self, func, filters_full_names):
331 self.func = func
332 self.filters_full_names = filters_full_names
333 self.filters_full_names_rev = {ff: fs for fs, ff in filters_full_names.items()}
335 def __call__(self, **kw):
336 if "filter_names" in kw:
337 kw["filter_names"] = [
338 self.filters_full_names[f] for f in kw["filter_names"]
339 ]
341 out = self.func(**kw)
343 if not isinstance(out, dict):
344 return out
345 else:
346 out_ = {}
347 for k in out:
348 if k in self.filters_full_names_rev:
349 out_[self.filters_full_names_rev[k]] = out[k]
350 return out_
352 def __getattr__(self, name):
353 return getattr(self.func, name)
356def get_default_full_filter_names(filters):
357 filters_full_names = {f: f for f in filters}
359 return filters_full_names
362# 'Ks_MAG_AUTO' Ks_uv.res
363# 'Y_MAG_AUTO' Y_uv.res
364# 'H_MAG_AUTO' H_uv.res
365# 'J_MAG_AUTO' J_uv.res
366# 'B_MAG_AUTO' B_subaru.res
367# 'V_MAG_AUTO' V_subaru.res
368# 'ip_MAG_AUTO' i_Subaru.res
369# 'r_MAG_AUTO' r_subaru.res
370# 'zp_MAG_AUTO' z_subaru.res
371# 'zpp_MAG_AUTO' suprime_FDCCD_z.res
372# 'u_MAG_AUTO' u_megaprime_sagem.res
373# 'IB427_MAG_AUTO' IB427.SuprimeCam.pb
374# 'IB464_MAG_AUTO' IB464.SuprimeCam.pb
375# 'IA484_MAG_AUTO' IA484.SuprimeCam.pb
376# 'IB505_MAG_AUTO' IB505.SuprimeCam.pb
377# 'IA527_MAG_AUTO' IA527.SuprimeCam.pb
378# 'IB574_MAG_AUTO' IB574.SuprimeCam.pb
379# 'IA624_MAG_AUTO' IA624.SuprimeCam.pb
380# 'IA679_MAG_AUTO' IA679.SuprimeCam.pb
381# 'IB709_MAG_AUTO' IB709.SuprimeCam.pb
382# 'IA738_MAG_AUTO' IA738.SuprimeCam.pb
383# 'IA767_MAG_AUTO' IA767.SuprimeCam.pb
384# 'IB827_MAG_AUTO' IB827.SuprimeCam.pb
385# 'NB711_MAG_AUTO' NB711.SuprimeCam.pb
386# 'NB816_MAG_AUTO' NB816.SuprimeCam.pb
387# 'Hw_MAG_AUTO' wircam_H.res
388# 'Ksw_MAG_AUTO' wircam_Ks.res
389# 'yHSC_MAG_AUTO' y_HSC.txt
390# 'SPLASH_1_MAG', irac_ch1.res
391# 'SPLASH_2_MAG', irac_ch2.res
392# 'SPLASH_3_MAG', irac_ch3.res
393# 'SPLASH_4_MAG', irac_ch4.res
394# 'MAG_24', mips24.res
395# 'MAG_GALEX_FUV' galex1500.res
396# 'MAG_GALEX_NUV' galex2500.res