#!/usr/bin/python
#
# Project Librarian: Brandon Piotrzkowski
# Staff Scientist
# UW-Milwaukee Department of Physics
# Center for Gravitation & Cosmology
# <brandon.piotrzkowski@ligo.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Script to perform offline RAVEN search, where the inputs can be any combination
of two online GraceDB instances or local files.
"""
__author__ = "Brandon Piotrzkowski <brandon.piotrzkowski@ligo.org>"
# Global imports
from ligo.raven.offline_search import offline_search
from ligo.raven.mock_gracedb import choose_gracedb
import argparse
from astropy.table import vstack
# Command line options.
parser = argparse.ArgumentParser(description='Perform query of GraceDB')
parser.add_argument("-i", "--inputs", nargs=2, metavar="input.csv https://gracedb.ligo.org/api/",
action="append", default=None,
help="Filepath of input file(s) or URL of GraceDB API(s) (any combination of the two)"),
parser.add_argument("-o", "--output_path", metavar="results", default="results",
help="Filepath of output directory"),
parser.add_argument("-s", "--ext_searches", metavar="GRB SubGRB SubGRBTargeted", nargs='+', default=None,
help="Run ID supported by GraceDB (e.g. O4a)"),
parser.add_argument("-R", "--runid", metavar="O4a", default=None,
help="Run ID supported by GraceDB (e.g. O4a)"),
parser.add_argument("-P", "--omit_plots", dest="omit_plots", action="store_true",
help="Choose to not generate plots of results."),
parser.add_argument("-G", "--load_gw_fars", dest="load_gw_fars", action="store_true",
help="Load and include GW FARs for comparison."),
args = parser.parse_args()
print(args)
# Check required options are there
if not args.inputs:
raise AssertionError('Inputs (GraceDB API URL(s) and/or or file path(s)) not given')
else:
input1, input2 = str(args.inputs[0][0]), str(args.inputs[0][1])
secs_per_year = 365. * 24. * 60. * 60.
runid = str(args.runid)
# Apply historical rates based on RunID
# Only Fermi+Swift during this time, in O3+ including INTEGRAL. However
# INTEGRAL is only ~5/year so including/discluding this doesn't matter much.
if runid in {'O2', 'O3', 'O4a', 'O4b'}:
grb_rate = 305 / secs_per_year
# Rate in O4c and going forward for now after including SVOM
else:
grb_rate = 325 / secs_per_year
# SubGRB rate adds an additional 65 candidates to whatever the GRB rate is
subgrb_rate = grb_rate + 65 / secs_per_year
# FIXME: Add new searches here once they are online. This will include CHIME
# FRBs, Einstein Probe Xrays, IceCube HENs, and potentially Rubin Optical
# vents
settings = \
[
{
'ext_search': 'GRB',
'group': 'CBC',
'pipeline': None,
'se_search': None,
'tl': -1,
'th': 5,
'ext_rate': grb_rate,
'gw_far_thresh': None,
'ext_far_thresh': None,
'joint_far_method': 'untargeted',
'trials_factor': (7 - 1),
'alert_far_thresh': 12 / secs_per_year
},
{
'ext_search': 'GRB',
'group': 'Burst',
'pipeline': None,
'se_search': 'BBH',
'tl': -1,
'th': 5,
'ext_rate': grb_rate,
'gw_far_thresh': None,
'ext_far_thresh': None,
'joint_far_method': 'untargeted',
'trials_factor': (7 - 1),
'alert_far_thresh': 12 / secs_per_year
},
{
'ext_search': 'GRB',
'group': 'Burst',
'pipeline': None,
'se_search': 'AllSky',
'tl': -60,
'th': 600,
'ext_rate': grb_rate,
'gw_far_thresh': None,
'ext_far_thresh': None,
'joint_far_method': 'untargeted',
'trials_factor': (3 - 1),
'alert_far_thresh': 1 / secs_per_year
},
{
'ext_search': 'SubGRB',
'group': 'CBC',
'pipeline': None,
'se_search': None,
'tl': -1,
'th': 11,
'ext_rate': subgrb_rate,
'gw_far_thresh': None,
'ext_far_thresh': None,
'joint_far_method': 'untargeted',
'trials_factor': (7 - 1),
'alert_far_thresh': 12 / secs_per_year
},
{
'ext_search': 'SubGRB',
'group': 'Burst',
'pipeline': None,
'se_search': 'BBH',
'tl': -1,
'th': 11,
'ext_rate': subgrb_rate,
'gw_far_thresh': None,
'ext_far_thresh': None,
'joint_far_method': 'untargeted',
'trials_factor': (7 - 1),
'alert_far_thresh': 12 / secs_per_year
},
{
'ext_search': 'SubGRBTargeted',
'group': 'CBC',
'pipeline': 'Fermi',
'se_search': 'AllSky',
'tl': -1,
'th': 11,
'ext_rate': None,
'gw_far_thresh': 2 / (1 * 86400) * 8,
'ext_far_thresh': 1e-4,
'joint_far_method': 'targeted',
'trials_factor': (7 - 1),
'alert_far_thresh': 12 / secs_per_year
},
{
'ext_search': 'SubGRBTargeted',
'group': 'Burst',
'pipeline': 'Fermi',
'se_search': 'BBH',
'tl': -1,
'th': 11,
'ext_rate': None,
'gw_far_thresh': 2 / (1 * 86400) * 8,
'ext_far_thresh': 1e-4,
'joint_far_method': 'targeted',
'trials_factor': (7 - 1),
'alert_far_thresh': 12 / secs_per_year
},
{
'ext_search': 'SubGRBTargeted',
'group': 'Burst',
'pipeline': 'Fermi',
'se_search': 'AllSky',
'tl': -1,
'th': 11,
'ext_rate': None,
'gw_far_thresh': 2 / (1 * 86400) * 8,
'ext_far_thresh': 1e-4,
'joint_far_method': 'targeted',
'trials_factor': (3 - 1),
'alert_far_thresh': 1 / secs_per_year
},
{
'ext_search': 'SubGRBTargeted',
'group': 'CBC',
'pipeline': 'Swift',
'se_search': 'AllSky',
'tl': -10,
'th': 20,
'ext_rate': None,
'gw_far_thresh': 2 / (1 * 86400) * 8,
'ext_far_thresh': 1e-3,
'joint_far_method': 'targeted',
'trials_factor': (7 - 1),
'alert_far_thresh': 12 / secs_per_year
},
{
'ext_search': 'SubGRBTargeted',
'group': 'Burst',
'pipeline': 'Swift',
'se_search': 'BBH',
'tl': -10,
'th': 20,
'ext_rate': None,
'gw_far_thresh': 2 / (1 * 86400) * 8,
'ext_far_thresh': 1e-3,
'joint_far_method': 'targeted',
'trials_factor': (7 - 1),
'alert_far_thresh': 12 / secs_per_year
},
{
'ext_search': 'SubGRBTargeted',
'group': 'Burst',
'pipeline': 'Swift',
'se_search': 'AllSky',
'tl': -10,
'th': 20,
'ext_rate': None,
'gw_far_thresh': 2 / (1 * 86400) * 8,
'ext_far_thresh': 1e-3,
'joint_far_method': 'targeted',
'trials_factor': (3 - 1),
'alert_far_thresh': 1 / secs_per_year
}
]
# Filter out searches not given
settings = [s for s in settings if s['ext_search'] in args.ext_searches]
print(f"Settings used: {settings}")
outputs = []
# Perform search
for s in settings:
path = args.output_path + f"/{runid}_{s['group']}_{s['ext_search']}"
if s.get('se_search'):
path += f"_{s['se_search']}"
if s.get('pipeline'):
path += f"_{s['pipeline']}"
output = offline_search(
input1, input2, runid=runid,
tl=s['tl'], th=s['th'],
ext_rate=s['ext_rate'],
gw_far_thresh=s['gw_far_thresh'],
ext_far_thresh=s['ext_far_thresh'],
alert_far_thresh=s['alert_far_thresh'],
trials_factor=s['trials_factor'],
output_path=path,
create_plots=not args.omit_plots,
load_gw_fars=args.load_gw_fars,
joint_far_method=s['joint_far_method'],
group=s['group'], pipeline=s['pipeline'],
ext_search=s['ext_search'], se_search=s['se_search'])
if output is not None:
outputs.append(output)
if outputs:
final_table = vstack(outputs)
final_table.write(args.output_path + "/total_results.csv", overwrite=True)