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

1import numpy as np 

2import lsst.sims.maf.metrics as metrics 

3import healpy as hp 

4from lsst.sims.maf.stackers import snStacker 

5import numpy.lib.recfunctions as rf 

6 

7__all__ = ['SNSLMetric'] 

8 

9 

10class SNSLMetric(metrics.BaseMetric): 

11 def __init__(self, metricName='SNSLMetric', 

12 mjdCol='observationStartMJD', RaCol='fieldRA', DecCol='fieldDec', 

13 filterCol='filter', exptimeCol='visitExposureTime', 

14 nightCol='night', obsidCol='observationId', nexpCol='numExposures', 

15 vistimeCol='visitTime', m5Col='fiveSigmaDepth', season=[-1], night_collapse=False, 

16 uniqueBlocks=False, season_gap=80., **kwargs): 

17 """ 

18 Strongly Lensed SN metric 

19 

20 The number of is given by: 

21 

22 N (lensed SNe Ia with well measured time delay) = 45.7 * survey_area / 

23 (20000 deg^2) * cumulative_season_length / (2.5 years) / (2.15 * 

24 exp(0.37 * gap_median_all_filter)) 

25 

26 where: 

27 survey_area: survey area (in deg2) 

28 cumulative_season_length: cumulative season length (in years) 

29 gap_median_all_filter: median gap (all filters) 

30 

31 Parameters 

32 -------------- 

33 metricName : str, opt 

34 metric name 

35 Default : SNCadenceMetric 

36 mjdCol : str, opt 

37 mjd column name 

38 Default : observationStartMJD, 

39 RaCol : str,opt 

40 Right Ascension column name 

41 Default : fieldRa 

42 DecCol : str,opt 

43 Declinaison column name 

44 Default : fieldDec 

45 filterCol : str,opt 

46 filter column name 

47 Default: filter 

48 exptimeCol : str,opt 

49 exposure time column name 

50 Default : visitExposureTime 

51 nightCol : str,opt 

52 night column name 

53 Default : night 

54 obsidCol : str,opt 

55 observation id column name 

56 Default : observationId 

57 nexpCol : str,opt 

58 number of exposure column name 

59 Default : numExposures 

60 vistimeCol : str,opt 

61 visit time column name 

62 Default : visitTime 

63 season: int (list) or -1, opt 

64 season to process (default: -1: all seasons) 

65 

66 

67 """ 

68 self.mjdCol = mjdCol 

69 self.filterCol = filterCol 

70 self.RaCol = RaCol 

71 self.DecCol = DecCol 

72 self.exptimeCol = exptimeCol 

73 self.nightCol = nightCol 

74 self.obsidCol = obsidCol 

75 self.nexpCol = nexpCol 

76 self.vistimeCol = vistimeCol 

77 self.seasonCol = 'season' 

78 self.m5Col = m5Col 

79 self.season_gap = season_gap 

80 

81 cols = [self.nightCol, self.filterCol, self.mjdCol, self.obsidCol, 

82 self.nexpCol, self.vistimeCol, self.exptimeCol] 

83 

84 super(SNSLMetric, self).__init__( 

85 col=cols, metricName=metricName, units='N SL', **kwargs) 

86 self.badVal = 0 

87 self.season = season 

88 self.bands = 'ugrizy' 

89 

90 self.night_collapse = night_collapse 

91 

92 def run(self, dataSlice, slicePoint=None): 

93 """ 

94 Runs the metric for each dataSlice 

95 

96 Parameters 

97 --------------- 

98 dataSlice: simulation data 

99 slicePoint: slicePoint(default None) 

100 

101 Returns 

102 ----------- 

103 number of SL time delay supernovae 

104 

105 """ 

106 dataSlice.sort(order=self.mjdCol) 

107 # get the pixel area 

108 area = hp.nside2pixarea(slicePoint['nside'], degrees=True) 

109 

110 if len(dataSlice) == 0: 

111 return self.badVal 

112 

113 # Collapse down by night if requested 

114 if self.night_collapse: 

115 # Or maybe this should be specific per filter? 

116 key = np.char.array(dataSlice[self.nightCol].astype(str)) + np.char.array(dataSlice[self.filterCol].astype(str)) 

117 u_key, indx = np.unique(key, return_index=True) 

118 # Normally we would want to co-add depths, increase the number of exposures, average mjdCol. But none of that gets used later. 

119 dataSlice = dataSlice[indx] 

120 # Need to resort I think 

121 dataSlice.sort(order=self.mjdCol) 

122 

123 dataSlice, season_id = self.seasonCalc(dataSlice) 

124 

125 seasons = self.season 

126 

127 if self.season == [-1]: 

128 seasons = np.unique(season_id) 

129 

130 season_lengths = [] 

131 median_gaps = [] 

132 for season in seasons: 

133 idx = np.where(season_id == season)[0] 

134 slice_sel = dataSlice[idx] 

135 if len(slice_sel) < 5: 

136 continue 

137 mjds_season = dataSlice[self.mjdCol][idx] 

138 cadence = mjds_season[1:]-mjds_season[:-1] 

139 season_lengths.append(mjds_season[-1]-mjds_season[0]) 

140 median_gaps.append(np.median(cadence)) 

141 

142 # get the cumulative season length 

143 

144 cumul_season_length = np.sum(season_lengths) 

145 

146 if cumul_season_length == 0: 

147 return self.badVal 

148 # get gaps 

149 gap_median = np.mean(median_gaps) 

150 

151 # estimate the number of lensed supernovae 

152 cumul_season = cumul_season_length/(12.*30.) 

153 

154 N_lensed_SNe_Ia = 45.7 * area / 20000. * cumul_season /\ 

155 2.5 / (2.15 * np.exp(0.37 * gap_median)) 

156 

157 return N_lensed_SNe_Ia 

158 

159 def seasonCalc(self, obs): 

160 """ 

161 Method to estimate seasons 

162 

163 Parameters 

164 -------------- 

165 obs: numpy array 

166 array of observations 

167 season_gap: float, opt 

168 minimal gap required to define a season (default: 80 days) 

169 mjdCol: str, opt 

170 col name for MJD infos (default: observationStartMJD) 

171 

172 Returns 

173 ---------- 

174 original numpy array sorted and season  

175 

176 """ 

177 

178 # check wether season has already been estimated 

179 if 'season' in obs.dtype.names: 

180 return obs, obs['season'] 

181 

182 obs.sort(order=self.mjdCol) 

183 season = np.zeros(obs.size, dtype=int) 

184 

185 if len(obs) == 1: 

186 return obs, season 

187 

188 diff = obs[self.mjdCol][1:]-obs[self.mjdCol][:-1] 

189 flag = np.where(diff > self.season_gap)[0] 

190 for i, indx in enumerate(flag): 

191 season[indx+1:] = i+1 

192 

193 return obs, season