Coverage for gwcelery/tasks/raven.py: 99%

194 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-25 18:01 +0000

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 

5 

6from .. import app 

7from . import external_skymaps, gracedb 

8from .core import identity 

9 

10log = get_task_logger(__name__) 

11 

12 

13@app.task(shared=False) 

14def calculate_coincidence_far(superevent, exttrig, tl, th, 

15 use_superevent_skymap=None): 

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

17 by calling ligo.raven.search.calc_signif_gracedb, using sky map info if 

18 available. 

19 

20 Parameters 

21 ---------- 

22 superevent : dict 

23 Superevent dictionary 

24 exttrig : dict 

25 External event dictionary 

26 tl : int 

27 Lower bound of search window in seconds 

28 th : int 

29 Upper bound of search window in seconds 

30 use_superevent_skymap : bool 

31 If True/False, use/don't use skymap info from superevent. 

32 Else if None, check SKYMAP_READY label in external event. 

33 

34 Returns 

35 ------- 

36 joint_far : dict 

37 Dictionary containing joint false alarm rate, including sky map info 

38 if available 

39 

40 """ 

41 superevent_id = superevent['superevent_id'] 

42 exttrig_id = exttrig['graceid'] 

43 far_grb = exttrig['far'] 

44 

45 # Don't compute coinc FAR for SNEWS coincidences 

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

47 return {} 

48 

49 # Define max far thresholds for targeted subthreshold search 

50 if exttrig['search'] == 'SubGRBTargeted': 

51 far_thresholds = app.conf['raven_targeted_far_thresholds'] 

52 far_gw_thresh = far_thresholds['GW'][exttrig['pipeline']] 

53 far_grb_thresh = far_thresholds['GRB'][exttrig['pipeline']] 

54 else: 

55 far_gw_thresh = None 

56 far_grb_thresh = None 

57 

58 # Get rate for expected number of astrophysical external triggers if needed 

59 if exttrig['search'] in {'GRB', 'SubGRB', 'MDC'}: 

60 ext_rate = app.conf['raven_ext_rates'][exttrig['search']] 

61 else: 

62 ext_rate = None 

63 

64 if ({'EXT_SKYMAP_READY', 'SKYMAP_READY'}.issubset(exttrig['labels']) or 

65 {'EXT_SKYMAP_READY', 'EM_READY'}.issubset(exttrig['labels'])): 

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

67 use_preferred_event_skymap = ( 

68 not use_superevent_skymap 

69 if use_superevent_skymap is not None else 

70 'SKYMAP_READY' not in exttrig['labels']) 

71 se_skymap = external_skymaps.get_skymap_filename( 

72 (superevent['preferred_event'] if use_preferred_event_skymap 

73 else superevent_id), is_gw=True) 

74 ext_skymap = external_skymaps.get_skymap_filename( 

75 exttrig_id, is_gw=False) 

76 ext_moc = '.multiorder.fits' in ext_skymap 

77 

78 return ligo.raven.search.calc_signif_gracedb( 

79 superevent_id, exttrig_id, tl, th, 

80 se_dict=superevent, ext_dict=exttrig, 

81 grb_search=exttrig['search'], 

82 se_fitsfile=se_skymap, ext_fitsfile=ext_skymap, 

83 se_moc=True, ext_moc=ext_moc, 

84 incl_sky=True, gracedb=gracedb.client, 

85 em_rate=ext_rate, 

86 far_grb=far_grb, 

87 far_gw_thresh=far_gw_thresh, 

88 far_grb_thresh=far_grb_thresh, 

89 use_preferred_event_skymap=use_preferred_event_skymap) 

90 else: 

91 return ligo.raven.search.calc_signif_gracedb( 

92 superevent_id, exttrig_id, tl, th, 

93 se_dict=superevent, ext_dict=exttrig, 

94 grb_search=exttrig['search'], 

95 incl_sky=False, gracedb=gracedb.client, 

96 em_rate=ext_rate, 

97 far_grb=far_grb, 

98 far_gw_thresh=far_gw_thresh, 

99 far_grb_thresh=far_grb_thresh) 

100 

101 

102@app.task(shared=False) 

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

104 searches=[], se_searches=[]): 

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

106 use. If events found, launches RAVEN pipeline. 

107 

108 Parameters 

109 ---------- 

110 gracedb_id : str 

111 GraceDB ID of the trigger that launched RAVEN 

112 alert_object : dict 

113 Alert dictionary 

114 group : str 

115 Burst or CBC 

116 pipelines : list 

117 List of external trigger pipeline names 

118 searches : list 

119 List of external trigger searches 

120 se_searches : list 

121 List of superevent searches 

122 

123 """ 

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

125 

126 ( 

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

128 searches, se_searches) 

129 | 

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

131 ).delay() 

132 

133 

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

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

136 

137 Parameters 

138 ---------- 

139 gracedb_id : str 

140 GraceDB ID of the trigger that launched RAVEN 

141 group : str 

142 Burst or CBC 

143 pipelines : list 

144 List of external trigger pipeline names 

145 searches : list 

146 List of external trigger searches 

147 se_searches : list 

148 List of superevent searches 

149 

150 Returns 

151 ------- 

152 tl, th : tuple 

153 Tuple of lower bound and upper bound of search window 

154 

155 """ 

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

157 tl_subfermi, th_subfermi = \ 

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

159 tl_subswift, th_subswift = \ 

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

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

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

163 

164 if 'SNEWS' in pipelines: 

165 tl, th = tl_snews, th_snews 

166 # Use Targeted search window if CBC or Burst 

167 elif not {'SubGRB', 'SubGRBTargeted'}.isdisjoint(searches): 

168 if 'Fermi' in pipelines: 

169 tl, th = tl_subfermi, th_subfermi 

170 elif 'Swift' in pipelines: 

171 tl, th = tl_subswift, th_subswift 

172 else: 

173 raise ValueError('Specify Fermi or Swift as pipeline when ' + 

174 'launching subthreshold search') 

175 elif group == 'CBC': 

176 tl, th = tl_cbc, th_cbc 

177 elif group == 'Burst': 

178 tl, th = tl_burst, th_burst 

179 else: 

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

181 gracedb_id)) 

182 if 'S' in gracedb_id: 

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

184 tl, th = -th, -tl 

185 

186 return tl, th 

187 

188 

189@app.task(shared=False) 

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

191 pipelines=[], searches=[], se_searches=[]): 

192 """Perform ligo-raven search to look for coincidences. This function 

193 does a query of GraceDB and uploads a log message of the result(s). 

194 

195 Parameters 

196 ---------- 

197 gracedb_id : str 

198 GraceDB ID of the trigger that launched RAVEN 

199 alert_object : dict 

200 Alert dictionary 

201 tl : int 

202 Lower bound of search window in seconds 

203 th : int 

204 Upper bound of search window in seconds 

205 group : str 

206 Burst or CBC 

207 pipelines : list 

208 List of external trigger pipelines for performing coincidence search 

209 against 

210 searches : list 

211 List of external trigger searches 

212 se_searches : list 

213 List of superevent searches 

214 

215 Returns 

216 ------- 

217 results : list 

218 List with the dictionaries of related GraceDB events 

219 

220 """ 

221 return ligo.raven.search.search(gracedb_id, tl, th, 

222 event_dict=alert_object, 

223 gracedb=gracedb.client, 

224 group=group, pipelines=pipelines, 

225 searches=searches, 

226 se_searches=se_searches) 

227 

228 

229@app.task(shared=False) 

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

231 gw_group, use_superevent_skymap=None): 

232 """Executes the full RAVEN pipeline, including adding 

233 the external trigger to the superevent, calculating the 

234 coincidence false alarm rate, applying 'EM_COINC' to the 

235 appropriate events, and checking whether the candidate(s) pass all 

236 publishing conditions. 

237 

238 Parameters 

239 ---------- 

240 raven_search_results : list 

241 List of dictionaries of each related gracedb trigger 

242 gracedb_id : str 

243 GraceDB ID of the trigger that launched RAVEN 

244 alert_object : dict 

245 Alert dictionary, either a superevent or an external event 

246 tl : int 

247 Lower bound of search window in seconds 

248 th : int 

249 Upper bound of search window in seconds 

250 gw_group : str 

251 Burst or CBC 

252 use_superevent_skymap : bool 

253 If True/False, use/don't use skymap info from superevent. 

254 Else if None, checks SKYMAP_READY label in external event. 

255 

256 """ 

257 if not raven_search_results: 

258 return 

259 if 'S' not in gracedb_id: 

260 raven_search_results = preferred_superevent(raven_search_results) 

261 for result in raven_search_results: 

262 if 'S' in gracedb_id: 

263 superevent_id = gracedb_id 

264 exttrig_id = result['graceid'] 

265 superevent = alert_object 

266 ext_event = result 

267 else: 

268 superevent_id = result['superevent_id'] 

269 exttrig_id = gracedb_id 

270 superevent = result 

271 ext_event = alert_object 

272 # Don't continue if it is a different superevent than previous one. 

273 if ext_event['superevent'] is not None \ 

274 and ext_event['superevent'] != superevent['superevent_id']: 

275 return 

276 

277 canvas = ( 

278 gracedb.add_event_to_superevent.si(superevent_id, exttrig_id) 

279 | 

280 calculate_coincidence_far.si( 

281 superevent, ext_event, tl, th, 

282 use_superevent_skymap=use_superevent_skymap 

283 ) 

284 | 

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

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

287 trigger_raven_alert.s(superevent, gracedb_id, 

288 ext_event, gw_group)) 

289 ) 

290 canvas.delay() 

291 

292 

293@app.task(shared=False) 

294def preferred_superevent(raven_search_results): 

295 """Chooses the superevent with the lowest FAR for an external 

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

297 add one external event to multiple superevents. 

298 

299 Parameters 

300 ---------- 

301 raven_search_results : list 

302 List of dictionaries of each related gracedb trigger 

303 

304 Returns 

305 ------- 

306 superevent : list 

307 List containing single chosen superevent 

308 

309 """ 

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

311 enumerate(raven_search_results)) 

312 return [raven_search_results[idx]] 

313 

314 

315@app.task(shared=False) 

316def update_coinc_far(coinc_far_dict, superevent, ext_event): 

317 """Update joint info in superevent based on the current preferred 

318 coincidence. In order of priority, the determing conditions are the 

319 following: 

320 

321 * A SNEWS coincidence is preferred over GRB. 

322 * Likely astrophysical external candidates are preferred over likely 

323 non-astrophysical candidates. 

324 * Candidates that pass publishing thresholds are preferred over those 

325 that do not. 

326 * A spacetime joint FAR over a time-only joint FAR. 

327 * Lower joint FARs are preferred over higher joint FARs. 

328 

329 Parameters 

330 ---------- 

331 coinc_far_dict : dict 

332 Dictionary containing coincidence false alarm rate results from 

333 RAVEN 

334 superevent : dict 

335 Superevent dictionary 

336 ext_event: dict 

337 External event dictionary 

338 

339 Returns 

340 ------- 

341 joint_far : dict 

342 Dictionary containing joint false alarm rate passed to the function 

343 as an initial argument 

344 

345 """ 

346 # Get graceids 

347 superevent_id = superevent['superevent_id'] 

348 ext_id = ext_event['graceid'] 

349 

350 # Get the latest info to prevent race condition 

351 superevent_latest = gracedb.get_superevent(superevent_id) 

352 

353 # Joint FAR isn't computed for SNEWS coincidence 

354 # Choose SNEWS coincidence over any other type of coincidence 

355 if ext_event['pipeline'] == 'SNEWS': 

356 gracedb.update_superevent(superevent_id, em_type=ext_id, 

357 time_coinc_far=None, 

358 space_coinc_far=None) 

359 return coinc_far_dict 

360 

361 # Load needed variables 

362 infty = float('inf') 

363 new_time_far = coinc_far_dict['temporal_coinc_far'] 

364 new_space_far = coinc_far_dict['spatiotemporal_coinc_far'] 

365 # Map None to infinity to make logic easier 

366 new_space_far_f = new_space_far if new_space_far else infty 

367 old_time_far = superevent_latest['time_coinc_far'] 

368 old_time_far_f = old_time_far if old_time_far else infty 

369 old_space_far = superevent_latest['space_coinc_far'] 

370 old_space_far_f = old_space_far if old_space_far else infty 

371 is_far_improved = (new_space_far_f < old_space_far_f or 

372 (new_time_far < old_time_far_f and 

373 old_space_far_f == infty)) 

374 

375 if superevent_latest['em_type']: 

376 # If previous preferred external event, load to compare 

377 emtype_event = gracedb.get_event(superevent_latest['em_type']) 

378 # Don't overwrite SNEWS with GRB event 

379 snews_to_grb = \ 

380 (emtype_event['pipeline'] == 'SNEWS' and 

381 ext_event['pipeline'] != 'SNEWS') 

382 # Determine which events are likely real or not 

383 is_old_grb_real, is_new_grb_real = \ 

384 ('NOT_GRB' not in emtype_event['labels'], 

385 'NOT_GRB' not in ext_event['labels']) 

386 is_old_raven_alert, is_new_raven_alert = \ 

387 ('RAVEN_ALERT' in emtype_event['labels'], 

388 'RAVEN_ALERT' in ext_event['labels']) 

389 # Use new event only if it is better old event information 

390 is_event_improved = ((is_new_grb_real and not is_old_grb_real) or 

391 (is_new_raven_alert and not is_old_raven_alert)) 

392 # if both real or both not, use FAR to differentiate 

393 if is_old_grb_real == is_new_grb_real \ 

394 and is_old_raven_alert == is_new_raven_alert: 

395 is_event_improved = is_far_improved 

396 else: 

397 snews_to_grb = False 

398 is_event_improved = is_far_improved 

399 

400 if is_event_improved and not snews_to_grb: 

401 gracedb.update_superevent(superevent_id, em_type=ext_id, 

402 time_coinc_far=new_time_far, 

403 space_coinc_far=new_space_far) 

404 return coinc_far_dict 

405 

406 

407@app.task(shared=False) 

408def trigger_raven_alert(coinc_far_dict, superevent, gracedb_id, 

409 ext_event, gw_group): 

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

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

412 event. 

413 

414 All of the following conditions must be true to either trigger an alert or 

415 include coincidence info into the next alert include: 

416 

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

418 * If triggered on a SNEWS event, the GW false alarm rate must pass 

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

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

421 group-specific trials factor as specified by the 

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

423 setting, is less than or equal to 

424 :obj:`~gwcelery.conf.preliminary_alert_far_threshold`. This FAR also 

425 must not be negative. 

426 * If the coincidence involves a GRB, then both sky maps must be present. 

427 

428 Parameters 

429 ---------- 

430 coinc_far_dict : dict 

431 Dictionary containing coincidence false alarm rate results from 

432 RAVEN 

433 superevent : dict 

434 Superevent dictionary 

435 gracedb_id : str 

436 GraceDB ID of the trigger that launched RAVEN 

437 ext_event : dict 

438 External event dictionary 

439 gw_group : str 

440 Burst or CBC 

441 

442 """ 

443 preferred_gwevent_id = superevent['preferred_event'] 

444 superevent_id = superevent['superevent_id'] 

445 ext_id = ext_event['graceid'] 

446 # Specify group is not given, currently missing for subthreshold searches 

447 gw_group = gw_group or superevent['preferred_event_data']['group'] 

448 gw_group = gw_group.lower() 

449 gw_search = superevent['preferred_event_data']['search'].lower() 

450 pipeline = ext_event['pipeline'] 

451 if gw_search in app.conf['significant_alert_trials_factor'][gw_group]: 

452 trials_factor = \ 

453 app.conf['significant_alert_trials_factor'][gw_group][gw_search] 

454 else: 

455 trials_factor = 1 

456 missing_skymap = True 

457 comments = [] 

458 messages = [] 

459 

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

461 # any trigger coincident with a decently significant GW candidate 

462 if 'SNEWS' == pipeline: 

463 gw_far = superevent['far'] 

464 far_type = 'gw' 

465 far_threshold = app.conf['snews_gw_far_threshold'] 

466 pass_far_threshold = gw_far * trials_factor < far_threshold 

467 is_far_negative = gw_far < 0 

468 is_ext_subthreshold = False 

469 missing_skymap = False 

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

471 time_coinc_far = space_coinc_far = coinc_far = None 

472 coinc_far_f = gw_far 

473 

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

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

476 # the coinc far 

477 else: 

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

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

480 

481 # Use spatial FAR if available, otherwise use temporal 

482 time_coinc_far = coinc_far_dict['temporal_coinc_far'] 

483 space_coinc_far = coinc_far_dict['spatiotemporal_coinc_far'] 

484 if space_coinc_far is not None: 

485 coinc_far = space_coinc_far 

486 missing_skymap = False 

487 else: 

488 coinc_far = time_coinc_far 

489 

490 far_type = 'joint' 

491 if gw_search in app.conf['significant_alert_far_threshold'][gw_group]: 

492 far_threshold = ( 

493 app.conf['significant_alert_far_threshold'][gw_group] 

494 [gw_search] 

495 ) 

496 else: 

497 # Fallback in case an event is uploaded to an unlisted search 

498 far_threshold = -1 * float('inf') 

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

500 pass_far_threshold = coinc_far_f <= far_threshold 

501 is_far_negative = coinc_far_f < 0 

502 

503 # Get most recent labels to prevent race conditions 

504 ext_labels = gracedb.get_labels(ext_id) 

505 no_previous_alert = {'RAVEN_ALERT'}.isdisjoint(ext_labels) 

506 likely_real_ext_event = {'NOT_GRB'}.isdisjoint(ext_labels) 

507 is_test_event = (superevent['preferred_event_data']['group'] == 'Test' or 

508 ext_event['group'] == 'Test') 

509 

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

511 # preferred event 

512 if pass_far_threshold and not is_ext_subthreshold and \ 

513 likely_real_ext_event and not missing_skymap and \ 

514 not is_test_event and no_previous_alert and \ 

515 not is_far_negative: 

516 comments.append(('RAVEN: publishing criteria met for {0}-{1}. ' 

517 'Triggering RAVEN alert'.format( 

518 preferred_gwevent_id, ext_id))) 

519 # Add label to local dictionary and to event on GraceDB server 

520 # NOTE: We may prefer to apply the superevent label first and the grab 

521 # labels to refresh in the future 

522 superevent['labels'] += 'RAVEN_ALERT' 

523 # Add RAVEN_ALERT to preferred event last to avoid race conditions 

524 # where superevent is expected to have it once alert is issued 

525 alert_canvas = ( 

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

527 | 

528 gracedb.create_label.si('HIGH_PROFILE', superevent_id) 

529 | 

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

531 | 

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

533 ) 

534 else: 

535 alert_canvas = identity.si() 

536 if not pass_far_threshold: 

537 comments.append(('RAVEN: publishing criteria not met for {0}-{1},' 

538 ' {2} FAR (w/ trials) too large ' 

539 '({3:.4g} > {4:.4g})'.format( 

540 preferred_gwevent_id, ext_id, far_type, 

541 coinc_far_f, far_threshold))) 

542 if is_ext_subthreshold: 

543 comments.append(('RAVEN: publishing criteria not met for {0}-{1},' 

544 ' {1} is subthreshold'.format(preferred_gwevent_id, 

545 ext_id))) 

546 if not likely_real_ext_event: 

547 ext_far = ext_event['far'] 

548 grb_far_threshold = \ 

549 app.conf['raven_targeted_far_thresholds']['GRB'][pipeline] 

550 extra_sentence = '' 

551 if ext_far is not None and grb_far_threshold < ext_far: 

552 extra_sentence = (' This due to the GRB FAR being too high ' 

553 '({0:.4g} > {1:.4g})'.format( 

554 ext_far, grb_far_threshold)) 

555 comments.append(('RAVEN: publishing criteria not met for {0}-{1},' 

556 ' {1} is likely non-astrophysical.{2}'.format( 

557 preferred_gwevent_id, ext_id, extra_sentence))) 

558 if is_test_event: 

559 comments.append('RAVEN: {0}-{1} is non-astrophysical, ' 

560 'at least one event is a Test event'.format( 

561 preferred_gwevent_id, ext_id)) 

562 if missing_skymap: 

563 comments.append('RAVEN: Will only publish GRB coincidence ' 

564 'if spatial-temporal FAR is present. ' 

565 'Waiting for both sky maps to be available ' 

566 'first.') 

567 if is_far_negative: 

568 comments.append(('RAVEN: publishing criteria not met for {0}-{1},' 

569 ' {2} FAR is negative ({3:.4g})'.format( 

570 preferred_gwevent_id, ext_id, far_type, 

571 coinc_far_f))) 

572 for comment in comments: 

573 messages.append(gracedb.upload.si(None, None, superevent_id, comment, 

574 tags=['ext_coinc'])) 

575 

576 # Update coincidence FAR with latest info, including the application of 

577 # RAVEN_ALERT, then issue alert 

578 ( 

579 update_coinc_far.si(coinc_far_dict, superevent, ext_event) 

580 | 

581 group( 

582 alert_canvas, 

583 external_skymaps.plot_overlap_integral.s(superevent, ext_event), 

584 *messages 

585 ) 

586 ).delay() 

587 

588 

589@app.task(shared=False) 

590def sog_paper_pipeline(ext_event, superevent): 

591 """Determine whether an a speed of gravity measurment manuscript should be 

592 created for a given coincidence. This is denoted by applying the 

593 ``SOG_READY`` label to a superevent. 

594 

595 All of the following conditions must be true for a SoG paper: 

596 

597 * The coincidence is significant and FARs more significant than in 

598 :obj:`~sog_paper_far_threshold`. 

599 * The external event is a high-significance GRB and from an MOU partner. 

600 * The GW event is a CBC candidate. 

601 

602 Parameters 

603 ---------- 

604 superevent : dict 

605 Superevent dictionary 

606 ext_event : dict 

607 External event dictionary 

608 

609 """ 

610 gw_far = superevent['far'] 

611 coinc_far = superevent['space_coinc_far'] 

612 gw_far_threshold = app.conf['sog_paper_far_threshold']['gw'] 

613 joint_far_threshold = app.conf['sog_paper_far_threshold']['joint'] 

614 

615 # Check publishing conditions 

616 pass_gw_far_threshold = gw_far <= gw_far_threshold 

617 pass_joint_far_threshold = coinc_far <= joint_far_threshold 

618 is_grb = ext_event['search'] in ['GRB', 'MDC'] 

619 is_mou_partner = ext_event['pipeline'] in ['Fermi', 'Swift'] 

620 is_cbc = superevent['preferred_event_data']['group'] == 'CBC' 

621 

622 if is_grb and is_cbc and is_mou_partner and \ 

623 pass_gw_far_threshold and pass_joint_far_threshold: 

624 # Trigger SOG_READY label alert to alert SOG analysts 

625 gracedb.create_label.si('SOG_READY', 

626 superevent['superevent_id']).delay()