Source code for gwcelery.tasks.external_triggers

from lxml import etree
from urllib.parse import urlparse
from celery import group
from celery.utils.log import get_logger

from . import detchar
from . import gcn
from . import gracedb
from . import external_skymaps
from . import lvalert
from . import raven

log = get_logger(__name__)


REQUIRED_LABELS_BY_TASK = {
    'compare': {'SKYMAP_READY', 'EXT_SKYMAP_READY', 'EM_COINC'},
    'combine': {'SKYMAP_READY', 'EXT_SKYMAP_READY', 'RAVEN_ALERT'}
}
"""These labels should be present on an external event to consider it to
be ready for sky map comparison.
"""

FERMI_GRB_CLASS_VALUE = 4
"""This is the index that denote GRBs within Fermi's Flight Position
classification."""

FERMI_GRB_CLASS_THRESH = .5
"""This values denotes the threshold of the most likely Fermi source
classification, above which we will consider a Fermi Flight Position
notice."""


[docs]@gcn.handler(gcn.NoticeType.SNEWS, queue='exttrig', shared=False) def handle_snews_gcn(payload): """Handles the payload from SNEWS alerts. Prepares the alert to be sent to graceDB as 'E' events. """ root = etree.fromstring(payload) # Get TrigID and Test Event Boolean trig_id = root.find("./What/Param[@name='TrigID']").attrib['value'] group = 'Test' if root.attrib['role'] == 'test' else 'External' event_observatory = 'SNEWS' query = 'group: External pipeline: {} grbevent.trigger_id = "{}"'.format( event_observatory, trig_id) events = gracedb.get_events(query=query) if events: assert len(events) == 1, 'Found more than one matching GraceDB entry' event, = events graceid = event['graceid'] gracedb.replace_event(graceid, payload) return else: graceid = gracedb.create_event(filecontents=payload, search='Supernova', group=group, pipeline=event_observatory) event = gracedb.get_event(graceid) start, end = event['gpstime'], event['gpstime'] # Pre-start and post-end padding is applied by check_vectors detchar.check_vectors(event, event['graceid'], start, end)
[docs]@gcn.handler(gcn.NoticeType.FERMI_GBM_FLT_POS, gcn.NoticeType.FERMI_GBM_GND_POS, gcn.NoticeType.FERMI_GBM_FIN_POS, gcn.NoticeType.SWIFT_BAT_GRB_POS_ACK, gcn.NoticeType.FERMI_GBM_SUBTHRESH, gcn.NoticeType.INTEGRAL_WAKEUP, gcn.NoticeType.INTEGRAL_REFINED, gcn.NoticeType.INTEGRAL_OFFLINE, gcn.NoticeType.AGILE_MCAL_ALERT, queue='exttrig', shared=False) def handle_grb_gcn(payload): """Handles the payload from Fermi and Swift alerts. Prepares the alert to be sent to graceDB as 'E' events. """ root = etree.fromstring(payload) u = urlparse(root.attrib['ivorn']) stream_path = u.path # Get TrigID try: trig_id = root.find("./What/Param[@name='TrigID']").attrib['value'] except AttributeError: trig_id = root.find("./What/Param[@name='Trans_Num']").attrib['value'] group = 'Test' if root.attrib['role'] == 'test' else 'External' stream_obsv_dict = {'/SWIFT': 'Swift', '/Fermi': 'Fermi', '/INTEGRAL': 'INTEGRAL', '/AGILE': 'AGILE'} event_observatory = stream_obsv_dict[stream_path] reliability = root.find("./What/Param[@name='Reliability']") if reliability is not None and int(reliability.attrib['value']) <= 4: return # Check if Fermi trigger is likely noise by checking classification # Most_Likely_Index of 4 is an astrophysical GRB # If not at least 50% chance of GRB we will not consider it for RAVEN likely_source = root.find("./What/Param[@name='Most_Likely_Index']") likely_prob = root.find("./What/Param[@name='Most_Likely_Prob']") if likely_source is not None and \ (likely_source.attrib['value'] != FERMI_GRB_CLASS_VALUE or likely_prob.attrib['value'] < FERMI_GRB_CLASS_THRESH): labels = ['NOT_GRB'] else: labels = None # Check if Swift has lost lock. If so then veto lost_lock = \ root.find("./What/Group[@name='Solution_Status']" + "/Param[@name='StarTrack_Lost_Lock']") if lost_lock is not None and lost_lock.attrib['value'] == 'true': labels = ['NOT_GRB'] ivorn = root.attrib['ivorn'] if 'subthresh' in ivorn.lower(): search = 'SubGRB' else: search = 'GRB' query = 'group: External pipeline: {} grbevent.trigger_id = "{}"'.format( event_observatory, trig_id) events = gracedb.get_events(query=query) if events: assert len(events) == 1, 'Found more than one matching GraceDB entry' event, = events graceid = event['graceid'] gracedb.replace_event(graceid, payload) if labels: gracedb.create_label(labels[0], graceid) else: gracedb.remove_label('NOT_GRB', graceid) event = gracedb.get_event(graceid) else: graceid = gracedb.create_event(filecontents=payload, search=search, group=group, pipeline=event_observatory, labels=labels) event = gracedb.get_event(graceid) start = event['gpstime'] integration_time = event['extra_attributes']['GRB']['trigger_duration'] # if None, pick a wide window to check data if integration_time is None: integration_time = 4. end = start + integration_time detchar.check_vectors(event, event['graceid'], start, end) if search == 'GRB': notice_type = \ int(root.find("./What/Param[@name='Packet_Type']").attrib['value']) notice_date = root.find("./Who/Date").text external_skymaps.create_upload_external_skymap( event, notice_type, notice_date) if event['pipeline'] == 'Fermi': if event['search'] == 'SubGRB': skymap_link = \ root.find("./What/Param[@name='HealPix_URL']").attrib['value'] else: skymap_link = None external_skymaps.get_upload_external_skymap.s(graceid, event['search'], skymap_link).delay()
[docs]@lvalert.handler('superevent', 'mdc_superevent', 'external_fermi', 'external_swift', 'external_integral', 'external_agile', shared=False) def handle_grb_lvalert(alert): """Parse an LVAlert message related to superevents/GRB external triggers and dispatch it to other tasks. Notes ----- This LVAlert message handler is triggered by creating a new superevent or GRB external trigger event, or applying the ``EM_COINC`` label to any superevent: * Any new event triggers a coincidence search with :meth:`gwcelery.tasks.raven.coincidence_search`. * When both a GW and GRB sky map are available during a coincidence, indicated by the labels ``SKYMAP_READY`` and ``EXT_SKYMAP_READY`` respectfully, this trigger the spacetime coinc FAR to be calculated. If an alert is triggered with these same conditions, indicated by the ``RAVEN_ALERT`` label, a combined GW-GRB sky map is created using :meth:`gwcelery.tasks.external_skymaps.create_combined_skymap`. """ # Determine GraceDB ID graceid = alert['uid'] if alert['alert_type'] == 'new': if alert['object'].get('group') == 'External': # Create and upload Swift sky map for the joint targeted # sub-threshold search as agreed on in the MOU if alert['object']['search'] == 'SubGRBTargeted' and \ alert['object']['pipeline'] == 'Swift': external_skymaps.create_upload_external_skymap( alert['object'], None, alert['object']['created']) # launch standard Burst-GRB search raven.coincidence_search(graceid, alert['object'], group='Burst') if alert['object']['search'] in ['SubGRB', 'SubGRBTargeted']: # if sub-threshold GRB, launch search with that pipeline raven.coincidence_search( graceid, alert['object'], group='CBC', searches=['SubGRB', 'SubGRBTargeted'], pipelines=[alert['object']['pipeline']]) else: # if threshold GRB, launch standard CBC-GRB search raven.coincidence_search(graceid, alert['object'], group='CBC', searches=['GRB']) elif 'S' in graceid: # launch standard GRB search based on group preferred_event_id = alert['object']['preferred_event'] gw_group = gracedb.get_group(preferred_event_id) raven.coincidence_search(graceid, alert['object'], group=gw_group, searches=['GRB']) if gw_group == 'CBC': # launch subthreshold searches if CBC # for Fermi and Swift separately to use different time windows for pipeline in ['Fermi', 'Swift']: raven.coincidence_search( graceid, alert['object'], group='CBC', searches=['SubGRB', 'SubGRBTargeted'], pipelines=[pipeline]) elif alert['alert_type'] == 'label_added' and \ alert['object'].get('group') == 'External': if _skymaps_are_ready(alert['object'], alert['data']['name'], 'compare'): # if both sky maps present and a coincidence, compare sky maps se_id, ext_ids = _get_superevent_ext_ids(graceid, alert['object'], 'compare') superevent = gracedb.get_superevent(se_id) preferred_event_id = superevent['preferred_event'] gw_group = gracedb.get_group(preferred_event_id) tl, th = raven._time_window(graceid, gw_group, [alert['object']['pipeline']], [alert['object']['search']]) raven.raven_pipeline([alert['object']], se_id, superevent, tl, th, gw_group) if _skymaps_are_ready(alert['object'], alert['data']['name'], 'combine'): # if both sky maps present and a raven alert, create combined # skymap se_id, ext_id = _get_superevent_ext_ids(graceid, alert['object'], 'combine') external_skymaps.create_combined_skymap(se_id, ext_id) elif 'EM_COINC' in alert['object']['labels']: # if not complete, check if GW sky map; apply label to external # event if GW sky map se_labels = gracedb.get_labels(alert['object']['superevent']) if 'SKYMAP_READY' in se_labels: gracedb.create_label.si('SKYMAP_READY', graceid).delay() elif alert['alert_type'] == 'label_added' and 'S' in graceid and \ 'SKYMAP_READY' in alert['object']['labels']: # if sky map in superevent, apply label to all external events # at the time group( gracedb.create_label.si('SKYMAP_READY', ext_id) for ext_id in alert['object']['em_events'] ).delay()
[docs]@lvalert.handler('superevent', 'mdc_superevent', 'external_snews', shared=False) def handle_snews_lvalert(alert): """Parse an LVAlert message related to superevents/SN external triggers and dispatch it to other tasks. Notes ----- This LVAlert message handler is triggered by creating a new superevent or SN external trigger event, or applying the ``EM_COINC`` label to any superevent: * Any new event triggers a coincidence search with :meth:`gwcelery.tasks.raven.coincidence_search`. """ # Determine GraceDB ID graceid = alert['uid'] if alert['object'].get('group', '') == 'Test': pass elif alert['alert_type'] == 'new' and \ alert['object'].get('group') == 'External': raven.coincidence_search(graceid, alert['object'], group='Burst', pipelines=['SNEWS']) elif 'S' in graceid: preferred_event_id = gracedb.get_superevent(graceid)['preferred_event'] group = gracedb.get_event(preferred_event_id)['group'] if alert['alert_type'] == 'new' and group == 'Burst': raven.coincidence_search(graceid, alert['object'], group=group, pipelines=['SNEWS'])
def _skymaps_are_ready(event, label, task): label_set = set(event['labels']) required_labels = REQUIRED_LABELS_BY_TASK[task] return required_labels.issubset(label_set) and label in required_labels def _get_superevent_ext_ids(graceid, event, task): if task == 'combine': if 'S' in graceid: se_id = event['superevent_id'] ext_id = event['em_type'] else: se_id = event['superevent'] ext_id = event['graceid'] elif task == 'compare': if 'S' in graceid: se_id = event['superevent_id'] ext_id = event['em_events'] else: se_id = event['superevent'] ext_id = [event['graceid']] return se_id, ext_id