Coverage for python/lsst/sims/catUtils/utils/SNIaLightCurveGenerator.py : 13%

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
1from __future__ import print_function
2from builtins import zip
3import numpy as np
4import warnings
6from lsst.sims.catUtils.mixins import SNIaCatalog, PhotometryBase
7from lsst.sims.catUtils.utils import _baseLightCurveCatalog
8from lsst.sims.catUtils.utils import LightCurveGenerator
10from lsst.sims.catUtils.supernovae import SNObject, SNUniverse
11from lsst.sims.photUtils import PhotometricParameters, calcGamma
12from lsst.sims.photUtils import Sed, calcSNR_m5, BandpassDict
14import time
16__all__ = ["SNIaLightCurveGenerator"]
19class _sniaLightCurveCatalog(_baseLightCurveCatalog, SNIaCatalog, PhotometryBase):
21 column_outputs = ["uniqueId", "snid", "raJ2000", "decJ2000",
22 "cosmologicalDistanceModulus", "redshift", "EBV"]
24 _suppressDimSN = False
27class SNIaLightCurveGenerator(LightCurveGenerator):
28 """
29 This class will find all of the OpSim pointings in a particular region
30 of the sky in a particular filter and then return light curves for all
31 of the supernovae observed in that region of sky.
33 Note: in this case, the method light_curves_from_pointings returns
34 fluxes and flux errors in maggies.
36 Input parameters:
37 -----------------
38 catalogdb is a CatalogDBObject instantiation connecting to the database
39 of supernovae to be observed.
41 opsimdb is the path to the OpSim database of observation.
43 opsimdriver (optional; default 'sqlite') indicates the database driver to
44 be used when connecting to opsimdb.
45 """
47 def __init__(self, *args, **kwargs):
48 self.lsstBandpassDict = BandpassDict.loadTotalBandpassesFromFiles()
49 self._lightCurveCatalogClass = _sniaLightCurveCatalog
50 self._filter_cat = None
51 self._ax_cache = None
52 self._bx_cache = None
53 self._ax_bx_wavelen = None
54 self.phot_params = PhotometricParameters()
55 self.sn_universe = SNUniverse()
56 self.sn_universe.suppressDimSN = False
57 self.z_cutoff = 1.2
58 self._brightness_name = 'flux'
59 super(SNIaLightCurveGenerator, self).__init__(*args, **kwargs)
61 def light_curves_from_pointings(self, pointings, chunk_size=100000, lc_per_field=None,
62 constraint=None):
63 if lc_per_field is not None:
64 warnings.warn("You have set lc_per_field in the SNIaLightCurveGenerator. "
65 "This will limit the number of candidate galaxies queried from the "
66 "CatSim database per field-of-view. Because supernovae are randomly "
67 "populated in those galaxies, there is no guarantee that the galaxies "
68 "queried will have supernovae in them. If the galaxies you actually "
69 "query do not host supernovae, you could get fewer light curves than "
70 "you expect.")
72 return LightCurveGenerator.light_curves_from_pointings(self, pointings,
73 chunk_size=chunk_size,
74 lc_per_field=lc_per_field,
75 constraint=constraint)
77 def _get_query_from_group(self, grp, chunk_size, lc_per_field=None, constraint=None):
78 """
79 Override _get_query_from_group. The probabilistic nature of SNe requires
80 that we always actually do the query with lc_per_field=None (since we can't be
81 guaranteed that any given galaxy, though it contains a SN, will have an SN that
82 is going off during our time of interest).
83 """
84 return LightCurveGenerator._get_query_from_group(self, grp, chunk_size, lc_per_field=None,
85 constraint=constraint)
87 class _filterCatalogClass(_sniaLightCurveCatalog):
88 column_outputs = ["uniqueId", "t0"]
90 def _light_curves_from_query(self, cat_dict, query_result, grp, lc_per_field=None):
92 t_dict = {}
93 gamma_dict = {}
94 m5_dict = {}
95 t_min = None
96 t_max = None
97 for bp_name in cat_dict:
98 self.lsstBandpassDict[bp_name].sbTophi()
100 # generate a 2-D numpy array containing MJDs, m5, and photometric gamma values
101 # for each observation in the given bandpass
102 raw_array = np.array([[obs.mjd.TAI, obs.m5[bp_name],
103 calcGamma(self.lsstBandpassDict[bp_name],
104 obs.m5[obs.bandpass],
105 self.phot_params)]
106 for obs in grp if obs.bandpass == bp_name]).transpose()
108 if len(raw_array) > 0:
110 t_dict[bp_name] = raw_array[0]
112 m5_dict[bp_name] = raw_array[1]
114 gamma_dict[bp_name] = raw_array[2]
116 local_t_min = t_dict[bp_name].min()
117 local_t_max = t_dict[bp_name].max()
118 if t_min is None or local_t_min < t_min:
119 t_min = local_t_min
121 if t_max is None or local_t_max > t_max:
122 t_max = local_t_max
124 snobj = SNObject()
126 cat = cat_dict[list(cat_dict.keys())[0]] # does not need to be associated with a bandpass
128 dummy_sed = Sed()
130 n_actual_sn = 0 # how many SN have we actually delivered?
132 for chunk in query_result:
134 if lc_per_field is not None and n_actual_sn >= lc_per_field:
135 break
137 t_start_chunk = time.time()
138 for sn in cat.iter_catalog(query_cache=[chunk]):
139 sn_rng = self.sn_universe.getSN_rng(sn[1])
140 sn_t0 = self.sn_universe.drawFromT0Dist(sn_rng)
141 if sn[5] <= self.z_cutoff and np.isfinite(sn_t0) and \
142 sn_t0 < t_max + cat.maxTimeSNVisible and \
143 sn_t0 > t_min - cat.maxTimeSNVisible:
145 sn_c = self.sn_universe.drawFromcDist(sn_rng)
146 sn_x1 = self.sn_universe.drawFromx1Dist(sn_rng)
147 sn_x0 = self.sn_universe.drawFromX0Dist(sn_rng, sn_x1, sn_c, sn[4])
149 snobj.set(t0=sn_t0, c=sn_c, x1=sn_x1, x0=sn_x0, z=sn[5])
151 for bp_name in t_dict:
152 t_list = t_dict[bp_name]
153 m5_list = m5_dict[bp_name]
154 gamma_list = gamma_dict[bp_name]
155 bandpass = self.lsstBandpassDict[bp_name]
156 if len(t_list) == 0:
157 continue
159 if snobj.maxtime() >= t_list[0] and snobj.mintime() <= t_list[-1]:
160 active_dexes = np.where(np.logical_and(t_list >= snobj.mintime(),
161 t_list <= snobj.maxtime()))
163 t_active = t_list[active_dexes]
164 m5_active = m5_list[active_dexes]
165 gamma_active = gamma_list[active_dexes]
167 if len(t_active) > 0:
169 wave_ang = bandpass.wavelen*10.0
170 mask = np.logical_and(wave_ang > snobj.minwave(),
171 wave_ang < snobj.maxwave())
173 wave_ang = wave_ang[mask]
174 snobj.set(mwebv=sn[6])
175 sn_ff_buffer = snobj.flux(time=t_active, wave=wave_ang)*10.0
176 flambda_grid = np.zeros((len(t_active), len(bandpass.wavelen)))
177 for ff, ff_sn in zip(flambda_grid, sn_ff_buffer):
178 ff[mask] = np.where(ff_sn > 0.0, ff_sn, 0.0)
180 fnu_grid = flambda_grid*bandpass.wavelen* \
181 bandpass.wavelen*dummy_sed._physParams.nm2m* \
182 dummy_sed._physParams.ergsetc2jansky/dummy_sed._physParams.lightspeed
184 flux_list = \
185 (fnu_grid*bandpass.phi).sum(axis=1)*(bandpass.wavelen[1]-bandpass.wavelen[0])
187 acceptable = np.where(flux_list>0.0)
189 flux_error_list = flux_list[acceptable]/ \
190 calcSNR_m5(dummy_sed.magFromFlux(flux_list[acceptable]),
191 bandpass,
192 m5_active[acceptable], self.phot_params,
193 gamma=gamma_active[acceptable])
195 if len(acceptable) > 0:
197 n_actual_sn += 1
198 if lc_per_field is not None and n_actual_sn > lc_per_field:
199 break
201 if sn[0] not in self.truth_dict:
202 self.truth_dict[sn[0]] = {}
203 self.truth_dict[sn[0]]['t0'] = sn_t0
204 self.truth_dict[sn[0]]['x1'] = sn_x1
205 self.truth_dict[sn[0]]['x0'] = sn_x0
206 self.truth_dict[sn[0]]['c'] = sn_c
207 self.truth_dict[sn[0]]['z'] = sn[5]
208 self.truth_dict[sn[0]]['E(B-V)'] = sn[6]
210 if sn[0] not in self.mjd_dict:
211 self.mjd_dict[sn[0]] = {}
212 self.bright_dict[sn[0]] = {}
213 self.sig_dict[sn[0]] = {}
215 if bp_name not in self.mjd_dict[sn[0]]:
216 self.mjd_dict[sn[0]][bp_name] = []
217 self.bright_dict[sn[0]][bp_name] = []
218 self.sig_dict[sn[0]][bp_name] = []
220 for tt, ff, ee in zip(t_active[acceptable], flux_list[acceptable],
221 flux_error_list[0]):
223 self.mjd_dict[sn[0]][bp_name].append(tt)
224 self.bright_dict[sn[0]][bp_name].append(ff/3631.0)
225 self.sig_dict[sn[0]][bp_name].append(ee/3631.0)
227 print("chunk of ", len(chunk), " took ", time.time()-t_start_chunk)