Hide keyboard shortcuts

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 

5 

6from lsst.sims.catUtils.mixins import SNIaCatalog, PhotometryBase 

7from lsst.sims.catUtils.utils import _baseLightCurveCatalog 

8from lsst.sims.catUtils.utils import LightCurveGenerator 

9 

10from lsst.sims.catUtils.supernovae import SNObject, SNUniverse 

11from lsst.sims.photUtils import PhotometricParameters, calcGamma 

12from lsst.sims.photUtils import Sed, calcSNR_m5, BandpassDict 

13 

14import time 

15 

16__all__ = ["SNIaLightCurveGenerator"] 

17 

18 

19class _sniaLightCurveCatalog(_baseLightCurveCatalog, SNIaCatalog, PhotometryBase): 

20 

21 column_outputs = ["uniqueId", "snid", "raJ2000", "decJ2000", 

22 "cosmologicalDistanceModulus", "redshift", "EBV"] 

23 

24 _suppressDimSN = False 

25 

26 

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. 

32 

33 Note: in this case, the method light_curves_from_pointings returns 

34 fluxes and flux errors in maggies. 

35 

36 Input parameters: 

37 ----------------- 

38 catalogdb is a CatalogDBObject instantiation connecting to the database 

39 of supernovae to be observed. 

40 

41 opsimdb is the path to the OpSim database of observation. 

42 

43 opsimdriver (optional; default 'sqlite') indicates the database driver to 

44 be used when connecting to opsimdb. 

45 """ 

46 

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) 

60 

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.") 

71 

72 return LightCurveGenerator.light_curves_from_pointings(self, pointings, 

73 chunk_size=chunk_size, 

74 lc_per_field=lc_per_field, 

75 constraint=constraint) 

76 

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) 

86 

87 class _filterCatalogClass(_sniaLightCurveCatalog): 

88 column_outputs = ["uniqueId", "t0"] 

89 

90 def _light_curves_from_query(self, cat_dict, query_result, grp, lc_per_field=None): 

91 

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() 

99 

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() 

107 

108 if len(raw_array) > 0: 

109 

110 t_dict[bp_name] = raw_array[0] 

111 

112 m5_dict[bp_name] = raw_array[1] 

113 

114 gamma_dict[bp_name] = raw_array[2] 

115 

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 

120 

121 if t_max is None or local_t_max > t_max: 

122 t_max = local_t_max 

123 

124 snobj = SNObject() 

125 

126 cat = cat_dict[list(cat_dict.keys())[0]] # does not need to be associated with a bandpass 

127 

128 dummy_sed = Sed() 

129 

130 n_actual_sn = 0 # how many SN have we actually delivered? 

131 

132 for chunk in query_result: 

133 

134 if lc_per_field is not None and n_actual_sn >= lc_per_field: 

135 break 

136 

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: 

144 

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]) 

148 

149 snobj.set(t0=sn_t0, c=sn_c, x1=sn_x1, x0=sn_x0, z=sn[5]) 

150 

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 

158 

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())) 

162 

163 t_active = t_list[active_dexes] 

164 m5_active = m5_list[active_dexes] 

165 gamma_active = gamma_list[active_dexes] 

166 

167 if len(t_active) > 0: 

168 

169 wave_ang = bandpass.wavelen*10.0 

170 mask = np.logical_and(wave_ang > snobj.minwave(), 

171 wave_ang < snobj.maxwave()) 

172 

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) 

179 

180 fnu_grid = flambda_grid*bandpass.wavelen* \ 

181 bandpass.wavelen*dummy_sed._physParams.nm2m* \ 

182 dummy_sed._physParams.ergsetc2jansky/dummy_sed._physParams.lightspeed 

183 

184 flux_list = \ 

185 (fnu_grid*bandpass.phi).sum(axis=1)*(bandpass.wavelen[1]-bandpass.wavelen[0]) 

186 

187 acceptable = np.where(flux_list>0.0) 

188 

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]) 

194 

195 if len(acceptable) > 0: 

196 

197 n_actual_sn += 1 

198 if lc_per_field is not None and n_actual_sn > lc_per_field: 

199 break 

200 

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] 

209 

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]] = {} 

214 

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] = [] 

219 

220 for tt, ff, ee in zip(t_active[acceptable], flux_list[acceptable], 

221 flux_error_list[0]): 

222 

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) 

226 

227 print("chunk of ", len(chunk), " took ", time.time()-t_start_chunk) 

228