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

1"""Search for GRB-GW coincidences with ligo-raven.""" 

2import ligo.raven.search 

3from celery import group 

4from celery.utils.log import get_task_logger 

5from ligo.raven import gracedb_events 

6 

7from ..import app 

8from . import external_skymaps 

9from . import gracedb 

10from . import legacy_gracedb 

11 

12log = get_task_logger(__name__) 

13 

14 

15@app.task(shared=False) 

16def calculate_coincidence_far(superevent, exttrig, tl, th): 

17 """Compute coincidence FAR for external trigger and superevent 

18 coincidence by calling ligo.raven.search.calc_signif_gracedb, 

19 using sky map info if available. 

20 

21 Parameters 

22 ---------- 

23 superevent: dict 

24 superevent dictionary 

25 exttrig: dict 

26 external event dictionary 

27 tl: float 

28 start of coincident time window 

29 th: float 

30 end of coincident time window 

31 

32 """ 

33 superevent_id = superevent['superevent_id'] 

34 exttrig_id = exttrig['graceid'] 

35 

36 # Don't compute coinc FAR for SNEWS coincidences 

37 if exttrig['pipeline'] == 'SNEWS': 

38 return 

39 

40 if {'EXT_SKYMAP_READY', 'SKYMAP_READY'}.issubset(exttrig['labels']): 

41 # if both sky maps available, calculate spatial coinc far 

42 se_skymap = external_skymaps.get_skymap_filename( 

43 superevent_id) 

44 ext_skymap = external_skymaps.get_skymap_filename( 

45 exttrig_id) 

46 

47 return ligo.raven.search.calc_signif_gracedb( 

48 superevent_id, exttrig_id, tl, th, 

49 grb_search=exttrig['search'], 

50 se_fitsfile=se_skymap, ext_fitsfile=ext_skymap, 

51 incl_sky=True, gracedb=legacy_gracedb.client, 

52 far_grb=exttrig['far']) 

53 else: 

54 return ligo.raven.search.calc_signif_gracedb( 

55 superevent_id, exttrig_id, tl, th, 

56 grb_search=exttrig['search'], 

57 incl_sky=False, gracedb=legacy_gracedb.client, 

58 far_grb=exttrig['far']) 

59 

60 

61@app.task(shared=False) 

62def coincidence_search(gracedb_id, alert_object, group=None, pipelines=[], 

63 searches=None): 

64 """Perform ligo-raven search for coincidences. Determines time window to 

65 use. If events found, launches raven pipeline. 

66 

67 Parameters 

68 ---------- 

69 gracedb_id: str 

70 ID of the trigger used by GraceDB 

71 alert_object: dict 

72 lvalert['object'] 

73 group: str 

74 Burst or CBC 

75 pipelines: list 

76 list of external trigger pipeline names 

77 

78 """ 

79 tl, th = _time_window(gracedb_id, group, pipelines, searches) 

80 

81 ( 

82 search.si(gracedb_id, alert_object, tl, th, group, pipelines, 

83 searches) 

84 | 

85 raven_pipeline.s(gracedb_id, alert_object, tl, th, group) 

86 ).delay() 

87 

88 

89def _time_window(gracedb_id, group, pipelines, searches): 

90 """Determine the time window to use given the parameters of the search. 

91 

92 Parameters 

93 ---------- 

94 gracedb_id: str 

95 ID of the trigger used by GraceDB 

96 group: str 

97 Burst or CBC 

98 pipelines: list 

99 list of external trigger pipeline names 

100 searches: list 

101 list of external trigger search names 

102 

103 """ 

104 tl_cbc, th_cbc = app.conf['raven_coincidence_windows']['GRB_CBC'] 

105 tl_subfermi, th_subfermi = \ 

106 app.conf['raven_coincidence_windows']['GRB_CBC_SubFermi'] 

107 tl_subswift, th_subswift = \ 

108 app.conf['raven_coincidence_windows']['GRB_CBC_SubSwift'] 

109 tl_burst, th_burst = app.conf['raven_coincidence_windows']['GRB_Burst'] 

110 tl_snews, th_snews = app.conf['raven_coincidence_windows']['SNEWS'] 

111 

112 if 'SNEWS' in pipelines: 

113 tl, th = tl_snews, th_snews 

114 elif group == 'CBC': 

115 if not {'SubGRB', 'SubGRBTargeted'}.isdisjoint(searches): 

116 if 'Fermi' in pipelines: 

117 tl, th = tl_subfermi, th_subfermi 

118 elif 'Swift' in pipelines: 

119 tl, th = tl_subswift, th_subswift 

120 else: 

121 tl, th = tl_cbc, th_cbc 

122 elif group == 'Burst': 

123 tl, th = tl_burst, th_burst 

124 else: 

125 raise ValueError('Invalid RAVEN search request for {0}'.format( 

126 gracedb_id)) 

127 if 'S' in gracedb_id: 

128 # If triggering on a superevent, need to reverse the time window 

129 tl, th = -th, -tl 

130 

131 return tl, th 

132 

133 

134@app.task(shared=False) 

135def search(gracedb_id, alert_object, tl=-5, th=5, group=None, 

136 pipelines=[], searches=[]): 

137 """Perform ligo-raven search for coincidences. 

138 

139 Parameters 

140 ---------- 

141 gracedb_id: str 

142 ID of the trigger used by GraceDB 

143 alert_object: dict 

144 lvalert['object'] 

145 tl: int 

146 number of seconds to search before 

147 th: int 

148 number of seconds to search after 

149 group: str 

150 Burst or CBC 

151 pipelines: list 

152 list of external trigger pipelines for performing coincidence search 

153 against 

154 

155 Returns 

156 ------- 

157 list with the dictionaries of related gracedb events 

158 

159 """ 

160 if alert_object.get('superevent_id'): 

161 event = gracedb_events.SE(gracedb_id, gracedb=legacy_gracedb.client) 

162 group = None 

163 else: 

164 event = gracedb_events.ExtTrig(gracedb_id, 

165 gracedb=legacy_gracedb.client) 

166 pipelines = [] 

167 return ligo.raven.search.search(event, tl, th, 

168 gracedb=legacy_gracedb.client, 

169 group=group, pipelines=pipelines, 

170 searches=searches) 

171 

172 

173@app.task(shared=False) 

174def raven_pipeline(raven_search_results, gracedb_id, alert_object, tl, th, 

175 gw_group): 

176 """Executes much of the full raven pipeline, including adding 

177 the external trigger to the superevent, calculating the 

178 coincidence false alarm rate, and applying 'EM_COINC' to the 

179 appropriate events. Also a preimlinary alert will be triggered 

180 if the coincidence passes threshold. 

181 

182 Parameters 

183 ---------- 

184 raven_search_results: list 

185 list of dictionaries of each related gracedb trigger 

186 gracedb_id: str 

187 ID of either a superevent or external trigger 

188 alert_object: dict 

189 lvalert['object'], either a superevent or an external event 

190 gw_group: str 

191 Burst or CBC 

192 

193 """ 

194 if not raven_search_results: 

195 return 

196 if alert_object.get('group') == 'External': 

197 raven_search_results = preferred_superevent(raven_search_results) 

198 for result in raven_search_results: 

199 if alert_object.get('group') == 'External': 

200 superevent_id = result['superevent_id'] 

201 exttrig_id = gracedb_id 

202 superevent = result 

203 ext_event = alert_object 

204 elif 'S' in gracedb_id: 

205 superevent_id = gracedb_id 

206 exttrig_id = result['graceid'] 

207 superevent = alert_object 

208 ext_event = result 

209 

210 canvas = ( 

211 gracedb.add_event_to_superevent.si(superevent_id, exttrig_id) 

212 | 

213 calculate_coincidence_far.si(superevent, ext_event, tl, th) 

214 | 

215 group(gracedb.create_label.si('EM_COINC', superevent_id), 

216 gracedb.create_label.si('EM_COINC', exttrig_id), 

217 trigger_raven_alert.s(superevent, gracedb_id, 

218 ext_event, gw_group)) 

219 ) 

220 canvas.delay() 

221 

222 

223@app.task(shared=False) 

224def preferred_superevent(raven_search_results): 

225 """Chooses the superevent with the lowest far for an external 

226 event to be added to. This is to prevent errors from trying to 

227 add one external event to multiple superevents. 

228 

229 Parameters 

230 ---------- 

231 raven_search_results: list 

232 list of dictionaries of each related gracedb trigger 

233 

234 """ 

235 minfar, idx = min((result['far'], idx) for (idx, result) in 

236 enumerate(raven_search_results)) 

237 return [raven_search_results[idx]] 

238 

239 

240@app.task(shared=False) 

241def trigger_raven_alert(coinc_far_dict, superevent, gracedb_id, 

242 ext_event, gw_group): 

243 """Determine whether an event should be published as a preliminary alert. 

244 If yes, then triggers an alert by applying `RAVEN_ALERT` to the preferred 

245 event. 

246 

247 All of the following conditions must be true for a preliminary alert: 

248 

249 * The external event must be a threshold GRB or SNEWS event. 

250 * If triggered on a SNEW event, the GW false alarm rate must pass 

251 :obj:`~gwcelery.conf.snews_gw_far_threshold`. 

252 * The event's RAVEN coincidence false alarm rate, weighted by the 

253 group-specific trials factor as specified by the 

254 :obj:`~gwcelery.conf.preliminary_alert_trials_factor` configuration 

255 setting, is less than or equal to 

256 :obj:`~gwcelery.conf.preliminary_alert_far_threshold`. 

257 * If the external event is from Swift, both sky maps must be present. 

258 

259 Parameters 

260 ---------- 

261 coinc_far_dict : dict 

262 Dictionary containing coincidence false alarm rate results from 

263 RAVEN 

264 superevent : dict 

265 superevent dictionary 

266 gracedb_id: str 

267 ID of the trigger that launched RAVEN 

268 ext_event: dict 

269 external event dictionary 

270 gw_group: str 

271 Burst or CBC 

272 

273 """ 

274 preferred_gwevent_id = superevent['preferred_event'] 

275 superevent_id = superevent['superevent_id'] 

276 ext_id = ext_event['graceid'] 

277 gw_group = gw_group.lower() 

278 pipeline = ext_event['pipeline'] 

279 trials_factor = app.conf['preliminary_alert_trials_factor'][gw_group] 

280 missing_skymap = 'Swift' == pipeline 

281 messages = [] 

282 

283 # Since the significance of SNEWS triggers is so high, we will publish 

284 # any trigger coincident with a decently significant GW candidate 

285 if 'SNEWS' == pipeline: 

286 gw_far = superevent['far'] 

287 far_type = 'gw' 

288 far_threshold = app.conf['snews_gw_far_threshold'] 

289 pass_far_threshold = gw_far * trials_factor < far_threshold 

290 is_ext_subthreshold = False 

291 # Set coinc FAR to gw FAR only for the sake of a message below 

292 time_coinc_far = space_coinc_far = coinc_far = None 

293 coinc_far_f = gw_far 

294 

295 # The GBM team requested we not send automatic alerts from subthreshold 

296 # GRBs. This checks that at least one threshold GRB present as well as 

297 # the coinc far 

298 else: 

299 # check whether the GRB is threshold or sub-thresholds 

300 is_ext_subthreshold = 'SubGRB' == ext_event['search'] 

301 

302 # Use spatial FAR if available, otherwise use temporal 

303 time_coinc_far = coinc_far_dict['temporal_coinc_far'] 

304 space_coinc_far = coinc_far_dict['spatiotemporal_coinc_far'] 

305 if space_coinc_far is not None: 

306 coinc_far = space_coinc_far 

307 missing_skymap = False 

308 else: 

309 coinc_far = time_coinc_far 

310 

311 far_type = 'joint' 

312 far_threshold = app.conf['preliminary_alert_far_threshold'][gw_group] 

313 coinc_far_f = coinc_far * trials_factor * (trials_factor - 1.) 

314 pass_far_threshold = coinc_far_f <= far_threshold 

315 

316 no_previous_alert = {'RAVEN_ALERT'}.isdisjoint( 

317 gracedb.get_labels(superevent_id)) 

318 likely_real_ext_event = {'NOT_GRB'}.isdisjoint(ext_event['labels']) 

319 

320 # If publishable, trigger an alert by applying `RAVEN_ALERT` label to 

321 # preferred event 

322 if pass_far_threshold and not is_ext_subthreshold and \ 

323 likely_real_ext_event and not missing_skymap: 

324 messages.append('RAVEN: publishing criteria met for %s' % ( 

325 preferred_gwevent_id)) 

326 if no_previous_alert: 

327 gracedb.update_superevent(superevent_id, em_type=ext_id, 

328 time_coinc_far=time_coinc_far, 

329 space_coinc_far=space_coinc_far) 

330 messages.append('Triggering RAVEN alert for %s' % ( 

331 preferred_gwevent_id)) 

332 ( 

333 gracedb.create_label.si('RAVEN_ALERT', superevent_id) 

334 | 

335 gracedb.create_label.si('RAVEN_ALERT', ext_id) 

336 | 

337 gracedb.create_label.si('RAVEN_ALERT', preferred_gwevent_id) 

338 ).delay() 

339 if not pass_far_threshold: 

340 messages.append(('RAVEN: publishing criteria not met for %s,' 

341 ' %s FAR (w/ trials) too large (%s>%s)' % ( 

342 preferred_gwevent_id, far_type, 

343 coinc_far_f, far_threshold))) 

344 if is_ext_subthreshold: 

345 messages.append(('RAVEN: publishing criteria not met for %s,' 

346 ' %s is subthreshold' % (preferred_gwevent_id, 

347 ext_id))) 

348 if not likely_real_ext_event: 

349 messages.append(('RAVEN: %s is likely non-astrophysical.' 

350 % (ext_id))) 

351 if not no_previous_alert: 

352 messages.append(('RAVEN: Alert already triggered for %s' 

353 % (superevent_id))) 

354 if missing_skymap: 

355 messages.append('RAVEN: Will only publish Swift coincidence ' 

356 'event if spatial-temporal FAR is present. ' 

357 'Waiting for both sky maps to be available ' 

358 'first.') 

359 for message in messages: 

360 gracedb.upload(None, None, superevent_id, 

361 message, 

362 tags=['ext_coinc'])