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 lxml import etree 

2from urllib.parse import urlparse 

3from celery import group 

4from celery.utils.log import get_logger 

5 

6from . import detchar 

7from . import gcn 

8from . import gracedb 

9from . import external_skymaps 

10from . import lvalert 

11from . import raven 

12 

13log = get_logger(__name__) 

14 

15 

16REQUIRED_LABELS_BY_TASK = { 

17 'compare': {'SKYMAP_READY', 'EXT_SKYMAP_READY', 'EM_COINC'}, 

18 'combine': {'SKYMAP_READY', 'EXT_SKYMAP_READY', 'RAVEN_ALERT'} 

19} 

20"""These labels should be present on an external event to consider it to 

21be ready for sky map comparison. 

22""" 

23 

24FERMI_GRB_CLASS_VALUE = 4 

25"""This is the index that denote GRBs within Fermi's Flight Position 

26classification.""" 

27 

28FERMI_GRB_CLASS_THRESH = .5 

29"""This values denotes the threshold of the most likely Fermi source 

30classification, above which we will consider a Fermi Flight Position 

31notice.""" 

32 

33 

34@gcn.handler(gcn.NoticeType.SNEWS, 

35 queue='exttrig', 

36 shared=False) 

37def handle_snews_gcn(payload): 

38 """Handles the payload from SNEWS alerts. 

39 

40 Prepares the alert to be sent to graceDB as 'E' events. 

41 """ 

42 root = etree.fromstring(payload) 

43 

44 # Get TrigID and Test Event Boolean 

45 trig_id = root.find("./What/Param[@name='TrigID']").attrib['value'] 

46 group = 'Test' if root.attrib['role'] == 'test' else 'External' 

47 

48 event_observatory = 'SNEWS' 

49 query = 'group: External pipeline: {} grbevent.trigger_id = "{}"'.format( 

50 event_observatory, trig_id) 

51 events = gracedb.get_events(query=query) 

52 

53 if events: 

54 assert len(events) == 1, 'Found more than one matching GraceDB entry' 

55 event, = events 

56 graceid = event['graceid'] 

57 gracedb.replace_event(graceid, payload) 

58 return 

59 

60 else: 

61 graceid = gracedb.create_event(filecontents=payload, 

62 search='Supernova', 

63 group=group, 

64 pipeline=event_observatory) 

65 event = gracedb.get_event(graceid) 

66 start, end = event['gpstime'], event['gpstime'] 

67 # Pre-start and post-end padding is applied by check_vectors 

68 detchar.check_vectors(event, event['graceid'], start, end) 

69 

70 

71@gcn.handler(gcn.NoticeType.FERMI_GBM_FLT_POS, 

72 gcn.NoticeType.FERMI_GBM_GND_POS, 

73 gcn.NoticeType.FERMI_GBM_FIN_POS, 

74 gcn.NoticeType.SWIFT_BAT_GRB_POS_ACK, 

75 gcn.NoticeType.FERMI_GBM_SUBTHRESH, 

76 gcn.NoticeType.INTEGRAL_WAKEUP, 

77 gcn.NoticeType.INTEGRAL_REFINED, 

78 gcn.NoticeType.INTEGRAL_OFFLINE, 

79 gcn.NoticeType.AGILE_MCAL_ALERT, 

80 queue='exttrig', 

81 shared=False) 

82def handle_grb_gcn(payload): 

83 """Handles the payload from Fermi, Swift, INTEGRAL, and AGILE MCAL 

84 GCN notices. 

85 

86 Filters out candidates likely to be noise. Creates external events 

87 from the notice if new notice, otherwise updates existing event. Then 

88 creates and/or grabs external sky map to be uploaded to the external event. 

89 """ 

90 root = etree.fromstring(payload) 

91 u = urlparse(root.attrib['ivorn']) 

92 stream_path = u.path 

93 

94 # Get TrigID 

95 try: 

96 trig_id = root.find("./What/Param[@name='TrigID']").attrib['value'] 

97 except AttributeError: 

98 trig_id = root.find("./What/Param[@name='Trans_Num']").attrib['value'] 

99 group = 'Test' if root.attrib['role'] == 'test' else 'External' 

100 

101 stream_obsv_dict = {'/SWIFT': 'Swift', 

102 '/Fermi': 'Fermi', 

103 '/INTEGRAL': 'INTEGRAL', 

104 '/AGILE': 'AGILE'} 

105 event_observatory = stream_obsv_dict[stream_path] 

106 

107 reliability = root.find("./What/Param[@name='Reliability']") 

108 if reliability is not None and int(reliability.attrib['value']) <= 4: 

109 return 

110 

111 # Check if Fermi trigger is likely noise by checking classification 

112 # Most_Likely_Index of 4 is an astrophysical GRB 

113 # If not at least 50% chance of GRB we will not consider it for RAVEN 

114 likely_source = root.find("./What/Param[@name='Most_Likely_Index']") 

115 likely_prob = root.find("./What/Param[@name='Most_Likely_Prob']") 

116 if likely_source is not None and \ 

117 (likely_source.attrib['value'] != FERMI_GRB_CLASS_VALUE 

118 or likely_prob.attrib['value'] < FERMI_GRB_CLASS_THRESH): 

119 labels = ['NOT_GRB'] 

120 else: 

121 labels = None 

122 

123 # Check if Swift has lost lock. If so then veto 

124 lost_lock = \ 

125 root.find("./What/Group[@name='Solution_Status']" + 

126 "/Param[@name='StarTrack_Lost_Lock']") 

127 if lost_lock is not None and lost_lock.attrib['value'] == 'true': 

128 labels = ['NOT_GRB'] 

129 

130 ivorn = root.attrib['ivorn'] 

131 if 'subthresh' in ivorn.lower(): 

132 search = 'SubGRB' 

133 else: 

134 search = 'GRB' 

135 

136 query = 'group: External pipeline: {} grbevent.trigger_id = "{}"'.format( 

137 event_observatory, trig_id) 

138 events = gracedb.get_events(query=query) 

139 

140 if events: 

141 assert len(events) == 1, 'Found more than one matching GraceDB entry' 

142 event, = events 

143 graceid = event['graceid'] 

144 gracedb.replace_event(graceid, payload) 

145 if labels: 

146 gracedb.create_label(labels[0], graceid) 

147 else: 

148 gracedb.remove_label('NOT_GRB', graceid) 

149 event = gracedb.get_event(graceid) 

150 

151 else: 

152 graceid = gracedb.create_event(filecontents=payload, 

153 search=search, 

154 group=group, 

155 pipeline=event_observatory, 

156 labels=labels) 

157 event = gracedb.get_event(graceid) 

158 start = event['gpstime'] 

159 integration_time = event['extra_attributes']['GRB']['trigger_duration'] 

160 # if None, pick a wide window to check data 

161 if integration_time is None: 

162 integration_time = 4. 

163 end = start + integration_time 

164 detchar.check_vectors(event, event['graceid'], start, end) 

165 

166 if search == 'GRB': 

167 notice_type = \ 

168 int(root.find("./What/Param[@name='Packet_Type']").attrib['value']) 

169 notice_date = root.find("./Who/Date").text 

170 external_skymaps.create_upload_external_skymap( 

171 event, notice_type, notice_date) 

172 if event['pipeline'] == 'Fermi': 

173 if event['search'] == 'SubGRB': 

174 skymap_link = \ 

175 root.find("./What/Param[@name='HealPix_URL']").attrib['value'] 

176 else: 

177 skymap_link = None 

178 external_skymaps.get_upload_external_skymap.s(graceid, 

179 event['search'], 

180 skymap_link).delay() 

181 

182 

183@lvalert.handler('superevent', 

184 'mdc_superevent', 

185 'external_fermi', 

186 'external_swift', 

187 'external_integral', 

188 'external_agile', 

189 shared=False) 

190def handle_grb_lvalert(alert): 

191 """Parse an LVAlert message related to superevents/GRB external triggers 

192 and dispatch it to other tasks. 

193 

194 Notes 

195 ----- 

196 This LVAlert message handler is triggered by creating a new superevent or 

197 GRB external trigger event, or a label associated with completeness of sky 

198 maps: 

199 

200 * Any new event triggers a coincidence search with 

201 :meth:`gwcelery.tasks.raven.coincidence_search`. 

202 * When both a GW and GRB sky map are available during a coincidence, 

203 indicated by the labels ``SKYMAP_READY`` and ``EXT_SKYMAP_READY`` 

204 respectfully, this trigger the spacetime coinc FAR to be calculated. If 

205 an alert is triggered with these same conditions, indicated by the 

206 ``RAVEN_ALERT`` label, a combined GW-GRB sky map is created using 

207 :meth:`gwcelery.tasks.external_skymaps.create_combined_skymap`. 

208 

209 """ 

210 # Determine GraceDB ID 

211 graceid = alert['uid'] 

212 

213 # launch searches 

214 if alert['alert_type'] == 'new': 

215 if alert['object'].get('group') == 'External': 

216 # Create and upload Swift sky map for the joint targeted 

217 # sub-threshold search as agreed on in the MOU 

218 if alert['object']['search'] == 'SubGRBTargeted' and \ 

219 alert['object']['pipeline'] == 'Swift': 

220 external_skymaps.create_upload_external_skymap( 

221 alert['object'], None, alert['object']['created']) 

222 

223 # launch standard Burst-GRB search 

224 raven.coincidence_search(graceid, alert['object'], group='Burst') 

225 

226 if alert['object']['search'] in ['SubGRB', 'SubGRBTargeted']: 

227 # if sub-threshold GRB, launch search with that pipeline 

228 raven.coincidence_search( 

229 graceid, alert['object'], group='CBC', 

230 searches=['SubGRB', 'SubGRBTargeted'], 

231 pipelines=[alert['object']['pipeline']]) 

232 else: 

233 # if threshold GRB, launch standard CBC-GRB search 

234 raven.coincidence_search(graceid, alert['object'], 

235 group='CBC', searches=['GRB']) 

236 elif 'S' in graceid: 

237 # launch standard GRB search based on group 

238 preferred_event_id = alert['object']['preferred_event'] 

239 gw_group = gracedb.get_group(preferred_event_id) 

240 raven.coincidence_search(graceid, alert['object'], 

241 group=gw_group, searches=['GRB']) 

242 if gw_group == 'CBC': 

243 # launch subthreshold searches if CBC 

244 # for Fermi and Swift separately to use different time windows 

245 for pipeline in ['Fermi', 'Swift']: 

246 raven.coincidence_search( 

247 graceid, alert['object'], group='CBC', 

248 searches=['SubGRB', 'SubGRBTargeted'], 

249 pipelines=[pipeline]) 

250 

251 # rerun raven pipeline or created combined sky map when sky maps are 

252 # available 

253 elif alert['alert_type'] == 'label_added' and \ 

254 alert['object'].get('group') == 'External': 

255 if _skymaps_are_ready(alert['object'], alert['data']['name'], 

256 'compare'): 

257 # if both sky maps present and a coincidence, compare sky maps 

258 se_id, ext_ids = _get_superevent_ext_ids(graceid, alert['object'], 

259 'compare') 

260 superevent = gracedb.get_superevent(se_id) 

261 preferred_event_id = superevent['preferred_event'] 

262 gw_group = gracedb.get_group(preferred_event_id) 

263 tl, th = raven._time_window(graceid, gw_group, 

264 [alert['object']['pipeline']], 

265 [alert['object']['search']]) 

266 raven.raven_pipeline([alert['object']], se_id, superevent, 

267 tl, th, gw_group) 

268 if _skymaps_are_ready(alert['object'], alert['data']['name'], 

269 'combine'): 

270 # if both sky maps present and a raven alert, create combined 

271 # skymap 

272 se_id, ext_id = _get_superevent_ext_ids(graceid, alert['object'], 

273 'combine') 

274 external_skymaps.create_combined_skymap(se_id, ext_id) 

275 elif 'EM_COINC' in alert['object']['labels']: 

276 # if not complete, check if GW sky map; apply label to external 

277 # event if GW sky map 

278 se_labels = gracedb.get_labels(alert['object']['superevent']) 

279 if 'SKYMAP_READY' in se_labels: 

280 gracedb.create_label.si('SKYMAP_READY', graceid).delay() 

281 elif alert['alert_type'] == 'label_added' and 'S' in graceid and \ 

282 'SKYMAP_READY' in alert['object']['labels']: 

283 # if sky map in superevent, apply label to all external events 

284 # at the time 

285 group( 

286 gracedb.create_label.si('SKYMAP_READY', ext_id) 

287 for ext_id in alert['object']['em_events'] 

288 ).delay() 

289 

290 

291@lvalert.handler('superevent', 

292 'mdc_superevent', 

293 'external_snews', 

294 shared=False) 

295def handle_snews_lvalert(alert): 

296 """Parse an LVAlert message related to superevents/SN external triggers and 

297 dispatch it to other tasks. 

298 

299 Notes 

300 ----- 

301 This LVAlert message handler is triggered by creating a new superevent or 

302 SN external trigger event: 

303 

304 * Any new event triggers a coincidence search with 

305 :meth:`gwcelery.tasks.raven.coincidence_search`. 

306 

307 """ 

308 # Determine GraceDB ID 

309 graceid = alert['uid'] 

310 

311 if alert['object'].get('group', '') == 'Test': 

312 pass 

313 elif alert['alert_type'] == 'new' and \ 

314 alert['object'].get('group') == 'External': 

315 raven.coincidence_search(graceid, alert['object'], 

316 group='Burst', pipelines=['SNEWS']) 

317 elif 'S' in graceid: 

318 preferred_event_id = gracedb.get_superevent(graceid)['preferred_event'] 

319 group = gracedb.get_event(preferred_event_id)['group'] 

320 if alert['alert_type'] == 'new' and group == 'Burst': 

321 raven.coincidence_search(graceid, alert['object'], 

322 group=group, pipelines=['SNEWS']) 

323 

324 

325def _skymaps_are_ready(event, label, task): 

326 label_set = set(event['labels']) 

327 required_labels = REQUIRED_LABELS_BY_TASK[task] 

328 return required_labels.issubset(label_set) and label in required_labels 

329 

330 

331def _get_superevent_ext_ids(graceid, event, task): 

332 if task == 'combine': 

333 if 'S' in graceid: 

334 se_id = event['superevent_id'] 

335 ext_id = event['em_type'] 

336 else: 

337 se_id = event['superevent'] 

338 ext_id = event['graceid'] 

339 elif task == 'compare': 

340 if 'S' in graceid: 

341 se_id = event['superevent_id'] 

342 ext_id = event['em_events'] 

343 else: 

344 se_id = event['superevent'] 

345 ext_id = [event['graceid']] 

346 return se_id, ext_id