From 5e90d979b7ddb8dfded562c850b284c2ca3ea9eb Mon Sep 17 00:00:00 2001 From: Brandon Piotrzkowski <brandon.piotrzkowski@ligo.org> Date: Mon, 9 May 2022 11:43:57 -0400 Subject: [PATCH] Listen to GCN Fermi GBM_ALERT channel for earlier warning; fixes #396 --- CHANGES.rst | 2 + gwcelery/tasks/external_triggers.py | 62 +++++++--- gwcelery/tests/data/fermi_initial_grb_gcn.xml | 108 ++++++++++++++++++ .../tests/test_tasks_external_triggers.py | 71 ++++++++++++ 4 files changed, 227 insertions(+), 16 deletions(-) create mode 100644 gwcelery/tests/data/fermi_initial_grb_gcn.xml diff --git a/CHANGES.rst b/CHANGES.rst index 22bc89bb7..3b74319f0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -24,6 +24,8 @@ Changelog - Move functions in handle_grb_gcn to asynchronous group to prevent detchar errors from interupting sky map generation. - Prevent sub-threhsold GRBs from overwriting high-threshold GRBs. +- Listen to initial GBM alerts for earlier warning. Prevent these events + from triggering alerts unless later updated. 1.0.1 (2022-05-09) ------------------ diff --git a/gwcelery/tasks/external_triggers.py b/gwcelery/tasks/external_triggers.py index 5edb17a6e..3969be31a 100644 --- a/gwcelery/tasks/external_triggers.py +++ b/gwcelery/tasks/external_triggers.py @@ -69,7 +69,8 @@ def handle_snews_gcn(payload): detchar.check_vectors(event, event['graceid'], start, end) -@gcn.handler(gcn.NoticeType.FERMI_GBM_FLT_POS, +@gcn.handler(gcn.NoticeType.FERMI_GBM_ALERT, + 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, @@ -87,6 +88,13 @@ def handle_grb_gcn(payload): Filters out candidates likely to be noise. Creates external events from the notice if new notice, otherwise updates existing event. Then creates and/or grabs external sky map to be uploaded to the external event. + + More info for these notices can be found at: + Fermi-GBM: https://gcn.gsfc.nasa.gov/fermi_grbs.html + Fermi-GBM sub: https://gcn.gsfc.nasa.gov/fermi_gbm_subthresh_archive.html + Swift: https://gcn.gsfc.nasa.gov/swift.html + INTEGRAL: https://gcn.gsfc.nasa.gov/integral.html + AGILE-MCAL: https://gcn.gsfc.nasa.gov/agile_mcal.html """ root = etree.fromstring(payload) u = urlparse(root.attrib['ivorn']) @@ -99,6 +107,9 @@ def handle_grb_gcn(payload): trig_id = root.find("./What/Param[@name='Trans_Num']").attrib['value'] ext_group = 'Test' if root.attrib['role'] == 'test' else 'External' + notice_type = \ + int(root.find("./What/Param[@name='Packet_Type']").attrib['value']) + stream_obsv_dict = {'/SWIFT': 'Swift', '/Fermi': 'Fermi', '/INTEGRAL': 'INTEGRAL', @@ -114,19 +125,27 @@ def handle_grb_gcn(payload): # 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 \ + not_likely_grb = 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 + or likely_prob.attrib['value'] < FERMI_GRB_CLASS_THRESH) + + # Check if initial Fermi alert. These are generally unreliable and should + # never trigger a RAVEN alert, but will give us earlier warning of a + # possible coincidence. Later notices could change this. + initial_gbm_alert = notice_type == gcn.NoticeType.FERMI_GBM_ALERT # 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': + swift_veto = lost_lock is not None and lost_lock.attrib['value'] == 'true' + + # Only send alerts if likely a GRB, is not a low-confidence early Fermi + # alert, and if not a Swift veto + if not_likely_grb or initial_gbm_alert or swift_veto: labels = ['NOT_GRB'] + else: + labels = None ivorn = root.attrib['ivorn'] if 'subthresh' in ivorn.lower(): @@ -165,8 +184,6 @@ def handle_grb_gcn(payload): group_canvas += _launch_external_detchar.s(), if search in {'GRB', 'MDC'}: - notice_type = \ - int(root.find("./What/Param[@name='Packet_Type']").attrib['value']) notice_date = root.find("./Who/Date").text group_canvas += external_skymaps.create_upload_external_skymap.s( notice_type, notice_date), @@ -279,23 +296,23 @@ def handle_grb_igwn_alert(alert): 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) + superevent_id, ext_ids = _get_superevent_ext_ids( + graceid, alert['object'], 'compare') + superevent = gracedb.get_superevent(superevent_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, + raven.raven_pipeline([alert['object']], superevent_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) + superevent_id, ext_id = _get_superevent_ext_ids( + graceid, alert['object'], 'combine') + external_skymaps.create_combined_skymap(superevent_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 @@ -310,6 +327,19 @@ def handle_grb_igwn_alert(alert): gracedb.create_label.si('SKYMAP_READY', ext_id) for ext_id in alert['object']['em_events'] ).delay() + elif alert['alert_type'] == 'label_removed' and \ + alert['object'].get('group') == 'External': + if alert['data']['name'] == 'NOT_GRB': + # if NOT_GRB is removed, re-check publishing conditions + superevent_id = alert['object']['superevent'] + superevent = gracedb.get_superevent(superevent_id) + gw_group = superevent['preferred_event_data']['group'] + coinc_far_dict = { + 'temporal_coinc_far': superevent['time_coinc_far'], + 'spatiotemporal_coinc_far': superevent['space_coinc_far'] + } + raven.trigger_raven_alert(coinc_far_dict, superevent, graceid, + alert['object'], gw_group) @igwn_alert.handler('superevent', diff --git a/gwcelery/tests/data/fermi_initial_grb_gcn.xml b/gwcelery/tests/data/fermi_initial_grb_gcn.xml new file mode 100644 index 000000000..b5b8a197e --- /dev/null +++ b/gwcelery/tests/data/fermi_initial_grb_gcn.xml @@ -0,0 +1,108 @@ +<?xml version = '1.0' encoding = 'UTF-8'?> +<voe:VOEvent + ivorn="ivo://nasa.gsfc.gcn/Fermi#GBM_ALERT2018-05-24T09:58:26.31_548848711_0-566" + role="observation" version="2.0" + xmlns:voe="http://www.ivoa.net/xml/VOEvent/v2.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.ivoa.net/xml/VOEvent/v2.0 http://www.ivoa.net/xml/VOEvent/VOEvent-v2.0.xsd" > + <Who> + <AuthorIVORN>ivo://nasa.gsfc.tan/gcn</AuthorIVORN> + <Author> + <shortName>Fermi (via VO-GCN)</shortName> + <contactName>Julie McEnery</contactName> + <contactPhone>+1-301-286-1632</contactPhone> + <contactEmail>Julie.E.McEnery@nasa.gov</contactEmail> + </Author> + <Date>2018-05-24T18:35:45</Date> + <Description>This VOEvent message was created with GCN VOE version: 1.25 07feb18</Description> + </Who> + <What> + <Param name="Packet_Type" value="110" /> + <Param name="Pkt_Ser_Num" value="15" /> + <Param name="TrigID" value="548841234" ucd="meta.id" /> + <Param name="Sequence_Num" value="0" ucd="meta.id.part" /> + <Param name="Burst_TJD" value="18262" unit="days" ucd="time" /> + <Param name="Burst_SOD" value="35906.31" unit="sec" ucd="time" /> + <Param name="Burst_Inten" value="0" unit="cts" ucd="phot.count" /> + <Param name="Data_Integ" value="0.000" unit="sec" ucd="time.interval" /> + <Param name="Burst_Signif" value="0.00" unit="sigma" ucd="stat.snr" /> + <Param name="Phi" value="262.01" unit="deg" ucd="pos.az.azi" /> + <Param name="Theta" value="64.10" unit="deg" ucd="pos.az.zd" /> + <Param name="Algorithm" value="415" unit="dn" /> + <Param name="Lo_Energy" value="50000" unit="keV" /> + <Param name="Hi_Energy" value="300000" unit="keV" /> + <Param name="Trigger_ID" value="0x0" /> + <Param name="Misc_flags" value="0x40000001" /> + <Group name="Trigger_ID" > + <Param name="Def_NOT_a_GRB" value="false" /> + <Param name="Target_in_Blk_Catalog" value="false" /> + <Param name="Human_generated" value="false" /> + <Param name="Robo_generated" value="true" /> + <Param name="Spatial_Prox_Match" value="false" /> + <Param name="Temporal_Prox_Match" value="false" /> + <Param name="Test_Submission" value="false" /> + </Group> + <Group name="Misc_Flags" > + <Param name="Values_Out_of_Range" value="false" /> + <Param name="Flt_Generated" value="false" /> + <Param name="Gnd_Generated" value="true" /> + <Param name="CRC_Error" value="false" /> + </Group> + <Param name="LightCurve_URL" value="http://heasarc.gsfc.nasa.gov/FTP/fermi/data/gbm/triggers/2018/bn180524416/quicklook/glg_lc_medres34_bn180524416.gif" ucd="meta.ref.url" /> + <Param name="LocationMap_URL" value="http://heasarc.gsfc.nasa.gov/FTP/fermi/data/gbm/triggers/2018/bn180524416/quicklook/glg_locplot_all_bn180524416.png" ucd="meta.ref.url" /> + <Param name="Coords_Type" value="1" unit="dn" /> + <Param name="Coords_String" value="source_object" /> + <Group name="Obs_Support_Info" > + <Description>The Sun and Moon values are valid at the time the VOEvent XML message was created.</Description> + <Param name="Sun_RA" value="61.53" unit="deg" ucd="pos.eq.ra" /> + <Param name="Sun_Dec" value="20.86" unit="deg" ucd="pos.eq.dec" /> + <Param name="Sun_Distance" value="94.75" unit="deg" ucd="pos.angDistance" /> + <Param name="Sun_Hr_Angle" value="-5.25" unit="hr" /> + <Param name="Moon_RA" value="187.65" unit="deg" ucd="pos.eq.ra" /> + <Param name="Moon_Dec" value="1.42" unit="deg" ucd="pos.eq.dec" /> + <Param name="MOON_Distance" value="59.39" unit="deg" ucd="pos.angDistance" /> + <Param name="Moon_Illum" value="77.31" unit="%" ucd="arith.ratio" /> + <Param name="Galactic_Long" value="264.32" unit="deg" ucd="pos.galactic.lon" /> + <Param name="Galactic_Lat" value="7.50" unit="deg" ucd="pos.galactic.lat" /> + <Param name="Ecliptic_Long" value="160.83" unit="deg" ucd="pos.ecliptic.lon" /> + <Param name="Ecliptic_Lat" value="-50.93" unit="deg" ucd="pos.ecliptic.lat" /> + </Group> + <Description>The Fermi-GBM location of a transient.</Description> + </What> + <WhereWhen> + <ObsDataLocation> + <ObservatoryLocation id="GEOLUN" /> + <ObservationLocation> + <AstroCoordSystem id="UTC-FK5-GEO" /> + <AstroCoords coord_system_id="UTC-FK5-GEO"> + <Time unit="s"> + <TimeInstant> + <ISOTime>2018-05-24T09:58:26.31</ISOTime> + </TimeInstant> + </Time> + <Position2D unit="deg"> + <Name1>RA</Name1> + <Name2>Dec</Name2> + <Value2> + <C1>0.0</C1> + <C2>0.0</C2> + </Value2> + <Error2Radius>0.000</Error2Radius> + </Position2D> + </AstroCoords> + </ObservationLocation> + </ObsDataLocation> + <Description>The RA,Dec coordinates are of the type: source_object.</Description> + </WhereWhen> + <How> + <Description>Fermi Satellite, GBM Instrument</Description> + <Reference uri="http://gcn.gsfc.nasa.gov/fermi.html" type="url" /> + </How> + <Why importance="0.95"> + <Inference probability="1.0"> + <Concept>process.variation.burst;em.gamma</Concept> + </Inference> + </Why> + <Description> + </Description> +</voe:VOEvent> diff --git a/gwcelery/tests/test_tasks_external_triggers.py b/gwcelery/tests/test_tasks_external_triggers.py index 09be53574..2dd456827 100644 --- a/gwcelery/tests/test_tasks_external_triggers.py +++ b/gwcelery/tests/test_tasks_external_triggers.py @@ -155,6 +155,38 @@ def test_handle_noise_fermi_event(mock_check_vectors, mock_get_upload_external_skymap.assert_called_once() +@patch('gwcelery.tasks.external_skymaps.create_external_skymap') +@patch('gwcelery.tasks.external_skymaps.get_upload_external_skymap.run') +@patch('gwcelery.tasks.gracedb.get_events', return_value=[]) +@patch('gwcelery.tasks.gracedb.create_event.run', return_value={ + 'graceid': 'E1', 'gpstime': 1, 'instruments': '', 'pipeline': 'Fermi', + 'search': 'GRB', + 'extra_attributes': {'GRB': {'trigger_duration': 1, 'trigger_id': 123, + 'ra': 0., 'dec': 0., 'error_radius': 0.}}, + 'links': {'self': 'https://gracedb.ligo.org/events/E356793/'}}) +@patch('gwcelery.tasks.detchar.check_vectors.run') +def test_handle_initial_fermi_event(mock_check_vectors, + mock_create_event, + mock_get_events, + mock_get_upload_external_skymap, + mock_create_external_skymap): + text = read_binary(data, 'fermi_initial_grb_gcn.xml') + external_triggers.handle_grb_gcn(payload=text) + mock_get_events.assert_called_once_with(query=( + 'group: External pipeline: ' + 'Fermi grbevent.trigger_id ' + '= "548841234"')) + # Note that this is the exact ID in the .xml file + mock_create_event.assert_called_once_with(filecontents=text, + search='GRB', + pipeline='Fermi', + group='External', + labels=['NOT_GRB']) + mock_check_vectors.assert_called_once() + mock_get_upload_external_skymap.assert_called_once() + mock_create_external_skymap.assert_not_called() + + @pytest.mark.parametrize('filename', ['fermi_grb_gcn.xml', 'fermi_noise_gcn.xml', @@ -261,6 +293,45 @@ def test_handle_skymap_comparison(mock_get_event, mock_get_superevent, -5, 1, 'CBC') +@patch('gwcelery.tasks.raven.trigger_raven_alert') +@patch('gwcelery.tasks.gracedb.get_superevent', + return_value={'superevent_id': 'S1234', + 'preferred_event': 'G1234', + 'preferred_event_data': { + 'group': 'CBC'}, + 'time_coinc_far': 1e-9, + 'space_coinc_far': 1e-10}) +def test_handle_label_removed(mock_get_superevent, + mock_trigger_raven_alert): + alert = {"uid": "E1212", + "alert_type": "label_removed", + "data": {"name": "NOT_GRB"}, + "object": { + "graceid": "E1212", + "group": "External", + "labels": ["EM_COINC", "EXT_SKYMAP_READY", "SKYMAP_READY"], + "superevent": "S1234", + "pipeline": "Fermi", + "search": "GRB" + } + } + superevent = {'superevent_id': 'S1234', + 'preferred_event': 'G1234', + 'preferred_event_data': { + 'group': 'CBC'}, + 'time_coinc_far': 1e-9, + 'space_coinc_far': 1e-10} + coinc_far_dict = { + 'temporal_coinc_far': 1e-9, + 'spatiotemporal_coinc_far': 1e-10 + } + external_triggers.handle_grb_igwn_alert(alert) + mock_trigger_raven_alert.assert_called_once_with( + coinc_far_dict, superevent, alert['uid'], + alert['object'], 'CBC' + ) + + @patch('gwcelery.tasks.external_skymaps.create_combined_skymap') def test_handle_skymap_combine(mock_create_combined_skymap): alert = {"uid": "E1212", -- GitLab