Searching for coincidences#

The RAVEN framework is a data-analysis pipeline used to identify GW events that coincide with external observatories. RAVEN ingests candidate gravitational-wave triggers, extracts physical parameters (time, sky location, significance), and cross-references them with event notices from partners in the electromagnetic domain. By comparing the timing and localization of these external alerts against gravitational-wave candidates, RAVEN assigns coincidence scores, flags statistically significant matches, and delivers rapid follow-up summaries. This enables coordinated multi-messenger observations and accelerates the discovery of events like binary mergers or supernovae.

Searching a GW event on GraceDB#

from ligo.raven import gracedb_events, search
from gracedb_sdk import Client
gracedb = Client('https://gracedb.ligo.org/api/')
from astropy.time import Time
utc_time = '2025-10-17 12:41:04'
gps_from_utc = Time(utc_time, format='iso', scale='utc').gps
gpstime = gps_from_utc
tl, th = -1e4, 1e4
group = 'CBC'     # 'CBC', 'Burst', or 'Test'
pipelines = []    # 'Fermi', 'Swift', 'INTEGRAL', 'AGILE', 'SNEWS', or 'IceCube'
ext_searches = []     # 'GRB', 'SubGRB', 'SubGRBTargeted', 'HEN', or 'MDC'
se_searches = []  # 'AllSky', 'BBH', 'IMBH', or 'MDC'
results = search.query('Superevent', gpstime, tl, th, gracedb=gracedb,
                       group=group, pipelines=pipelines,
                       ext_searches=ext_searches, se_searches=se_searches)
for n in results:
    print(n['superevent_id'], n['far'])

The search on External events can be done only using LVK credentials

gpstime = gps_from_utc
tl, th = -1e5, 1e5
group = 'CBC'     # 'CBC', 'Burst', or 'Test'
pipelines = ['SVOM']    # 'Fermi', 'Swift', 'SVOM', 'SNEWS', or 'IceCube'
ext_searches = ['GRB']     # 'GRB', 'SubGRB', 'SubGRBTargeted', 'HEN', or 'MDC'
se_searches = []  # 'AllSky', 'BBH', 'IMBH', or 'MDC'
results = search.query('External', gpstime, tl, th, gracedb=gracedb,
                       pipelines=pipelines,
                       ext_searches=ext_searches, se_searches=se_searches)
for n in results:
    print(n['graceid'], n['far'])

Joint FAR calculation#

We test here the calculation of joint FAR according to which search we are considering

Temporal FAR#

Imagine to have an EXT event and you only know its trigger time and FAR

se_far = 1e-6 # Hz
# this corresponds to
print("1 event every", format(1 / se_far / 3600 / 24, ".2f"), "days")
# indeed T = 1 / se_far = 1 / (1e-6) seconds = 1e6 seconds ~ 11.57 days
tl, th = -1, 10

Note

The default temporal windows used by RAVEN are these:

Event Type

Time window (s)

Notice Type Considered (see full list)

CBC

Burst

GRB (Fermi, Swift, INTEGRAL, AGILE)

[-1,5]

[-60,600]

FERMI_GBM_ALERT
FERMI_GBM_FIN_POS
FERMI_GBM_FLT_POS
FERMI_GBM_GND_POS
SWIFT_BAT_GRB_ALERT
SWIFT_BAT_GRB_LC
INTEGRAL_WAKEUP
INTEGRAL_REFINED
INTEGRAL_OFFLINE
AGILE_MCAL_ALERT

SubGRB (Fermi)

[-1,11]

[-1,11]

FERMI_GBM_SUBTHRESH

SubGRBTargeted (Fermi)

[-1,11]

[-1,11]

via Kafka alert

SubGRBTargeted (Swift)

[-10,20]

[-10,20]

via Kafka alert

Low-energy Neutrinos (SNEWS)

[-10,10]

[-10,10]

SNEWS

https://emfollow.docs.ligo.org/userguide/analysis/searches.html#coincident-with-external-trigger-search

The generic command to compute joint FAR is

far_joint = search.coinc_far(se_far, tl, th,
                joint_far_method = 'untargeted', # 'targeted' or 'untargeted'
                ext_rate = 1 / 24 / 3600, # once per day, it is used only if joint_far_method is 'untargeted'
                far_ext = 1e-5, # it is used only if joint_far_method is 'untargeted'
                far_gw_thresh = 1e-4, far_ext_thresh = 1e-4, # by default far_gw_thresh is 2/day and far_ext_thresh depends on the ext instrument, only used if joint_far_method is 'targeted'
                ext_search = None, # "GRB", "SubGRB", "SubGRBTargeted", "MDC", or "HEN" --> if you don't specify joint_far_method, you must specify ext_search
                # {'GRB', 'SubGRB', 'HEN'} --> uses untargeted
                # {'SubGRBTargeted'} --> uses targeted
                ext_pipeline=None # --> if you don't specify far_ext_thresh, you must specify ext_pipeline. Now it supports 'Fermi', 'Swift'
                )

Note

The following rates are used in the untargeted search:

the combined rate of independent GRB discovery by Swift, Fermi, and SVOM (avoiding double counts)

  • Fermi: 236/yr

  • Swift: 65/yr

  • SVOM ECLAIRs: ~25/yr

grb_gcn_rate = 325. / (365. * 24. * 60. * 60.)

Rate of subthreshold GRBs (rate of above threshold plus rate of subthreshold)

subgrb_gcn_rate = 65. / (365. * 24. * 60. * 60.)

Combined rate of all GOLD IceCube notice from Table 1 of https://arxiv.org/pdf/2304.01174

hen_gcn_rate = 13.91 / (365. * 24. * 60. * 60.)

Joint FAR from targeted search (confident EXT detection & any GW candidate)#

far_joint = search.coinc_far(se_far, tl, th,
                joint_far_method = 'targeted', # 'targeted' or 'untargeted'
                far_ext = 1e-5, # it is used only if joint_far_method is 'untargeted'
                far_gw_thresh = 1e-4, far_ext_thresh = 1e-4, # by default far_gw_thresh is 2/day and far_ext_thresh depends on the ext instrument, only used if joint_far_method is 'targeted'
                ext_search = 'SubGRBTargeted', # "GRB", "SubGRB", "SubGRBTargeted", "MDC", or "HEN" --> if you don't specify joint_far_method, you must specify ext_search
                # {'GRB', 'SubGRB', 'HEN'} --> uses untargeted
                # {'SubGRBTargeted'} --> uses targeted
                ext_pipeline=None # --> if you don't specify far_ext_thresh, you must specify ext_pipeline
                )

Joint FAR from targeted search, but we are in the special case of a confident GW detection & any EXT candidate#

gw_rate = 1 / 3 / 24 / 3600  # one GW event every 3 days
# But if there is strong evidence from GW data that it is either a BNS or NSBH merger,
# the rate can should be much lower, since we detect just a handful of these SO FAR
ext_far = 1e-5 # Hz
far_joint = search.coinc_far(ext_far, tl, th,
                joint_far_method = 'untargeted', # 'targeted' or 'untargeted'
                ext_rate = gw_rate, # once per day, it is used only if joint_far_method is 'untargeted'
                )
(th - tl) * 1 / 24 / 3600 * se_far
print(f"This coincidence happens every {format(1 / far_joint['temporal_coinc_far'] / 365 /3600 / 24, '.2f')} yrs")

Joint FAR from untargeted search, but we are in the special case of both GW and EXT are higly significant#

grb_rate = 325. / (365. * 24. * 60. * 60.)  # Hz
gw_rate = 1 / 3 / 24 / 3600  # one GW event every 3 days
far_joint =  gw_rate * grb_rate * (th - tl)
print(f"This coincidence happens every {format(1 / far_joint / 365 /3600 / 24, '.2f')} yrs")

Note

Remember that after computing joint FAR, RAVEN applies a trials factor \(N(N+1)\), where \(N\) is the number of independent searches. Currently \(N = 6\) for CBC and \(N = 2\) for Burst. Since search pipelines are not really independent, this trials factor is a conservative choice.

Once the trials factor is applied, RAVEN sends a public alert if FAR < 1 / month for CBC and FAR < 1 / yr for Burst.

How to give empirically “weight” to each \(\mathcal{H}^{XY}\) hypothesis#

To decide which FAR one should consider for a given couple of GW + EXT event, a possible proposal is to give a relative weight to each hypothesis, according to the properties of the events.

\[\begin{split} p_{astro}^{GRB} \sim \begin{cases} 1 & \text{if } \text{search = GRB} \\ 0 & \text{if } \text{search = SubGRBTargeted} \end{cases} \end{split}\]

Note

This is a rude approximation because we know that we could detect high significant GRB with Swift-BAT targeted search, for instance if we were slewing or the GRB is outside the FOV and not visible to other telescopes

The weights could look like:

\[ \mathcal{H}^{SS} \sim p_{astro}^{GW} \times p_{astro}^{EXT} \]
\[ \mathcal{H}^{SN} \sim p_{astro}^{GW} \times (1-p_{astro}^{EXT}) \]
\[ \mathcal{H}^{NS} \sim (1-p_{astro}^{GW}) \times p_{astro}^{EXT} \]
\[ \mathcal{H}^{NN} \sim (1-p_{astro}^{GW}) \times (1-p_{astro}^{EXT}) \]

Warning

Remember that FAR cannot be summed to have a final joint FAR. But you can still decide which \(\mathcal{H}^{XY}\) is more appropriate according to observational evidences and decide accordingly how to compute your FAR