Commit 173bba40 authored by Alexander Urban's avatar Alexander Urban
Browse files

Final cleanups on all files; bumping version number to 1.0 (includes time...

Final cleanups on all files; bumping version number to 1.0 (includes time coincidence only and runs in production)
parent 96f0f230
Welcome to RAVEN, the Rapid, on-source VOEvent Coincidence Monitor.
More information can be found at the website:
<link to be provided>
More information can be found at the review documentation website:
https://www.lsc-group.phys.uwm.edu/ligovirgo/cbcnote/GRBreview/RAVEN
A version of this documentation is included as a PDF file in the docs/ subdirectory.
To install RAVEN locally:
python setup.py install --prefix=/path/to/install/directory
......
#!/usr/bin/env python
#!/usr/bin/python
#
# Project Librarian: Alex Urban
# Graduate Student
# UW-Milwaukee Department of Physics
# Center for Gravitation & Cosmology
# <alexander.urban@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 that queries GraceDB for external triggers in a given period,
then saves them in a CSV table.
"""
__author__ = "Mark Poe <mark.poe@ligo.org>"
__author__ = "Alex Urban <alexander.urban@ligo.org>"
__author__ = "Mark Poe <mark.poe@ligo.org>, Alex Urban <alexander.urban@ligo.org>"
# Imports.
from lal.gpstime import tconvert
......
#!/usr/bin/env python
#
# Project Librarian: Alex Urban
# Graduate Student
# UW-Milwaukee Department of Physics
# Center for Gravitation & Cosmology
# <alexander.urban@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 that scrapes all GRB candidates in a given period from a NASA-
maintained webpage, then saves them in a CSV table.
"""
__author__ = "Mark Poe <mark.poe@ligo.org>"
__author__ = "Alex Urban <alexander.urban@ligo.org>"
__author__ = "Mark Poe <mark.poe@ligo.org>, Alex Urban <alexander.urban@ligo.org>"
# Imports.
import lxml.html
......
#!/usr/bin/env python
#!/usr/bin/python
#
# Copyright (C) 2012 Chris Pankow
# 2012-2013 Drew Keppel
......@@ -17,6 +18,7 @@
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
__doc__ = """
Build a set of GRB VOEvents from a LIGOLW SimInspiral Table according to user-specified constraints.
"""
......
#!/usr/bin/env python
"""
Module to define functions and attributes corresponding
to some external (non-gravitational) trigger
"""
__author__ = "Alex Urban <alexander.urban@ligo.org>"
# imports
import numpy as np
import healpy as hp
import dateutil.parser as dip
import os
import VOEventLib.VOEvent
import VOEventLib.Vutil
from ligo.gracedb.rest import GraceDb
from lalinference.bayestar import fits
from lalinference.fits import iso8601_to_gps
from math import floor
from grace import GW
# initiate instance of GraceDB server as a global variable
gracedb = GraceDb()
# define functions
def kappa(err):
""" Approximant to the von Mises-Fisher concentration parameter """
R = np.exp( -err**2 / 2 )
return R * (3 - R**2) / (1 - R**2)
def pdf(n, nside, theta0, phi0, err):
""" Posterior probability density function for the sky location of the
external trigger, fit to a von Mises-Fisher distribution on the unit
sphere. """
# If you're already in the most probable pixel, return unity.
if n == hp.ang2pix(nside, theta0, phi0): return 1.
# Otherwise, calculate and return the unnormalized probability in this pixel.
else:
k = kappa(err)
th, ph = hp.pix2ang(nside, n)
xi = k * ( ( np.sin(theta0) * np.sin(th) * np.cos(ph - phi0) ) + ( np.cos(theta0) * np.cos(th) ) - 1 )
return np.exp( xi )
def Cacc(rho_sky):
""" Estimator for the cumulative fraction of accidental associations with
sky coincidence better than psky. """
if rho_sky < 1e-50: return 1.
else:
x = np.log10(rho_sky)
p = [6.43375601e+00, -3.83233594e+04, 1.35768892e+01]
return p[0] * x**3 / (p[1] + p[2]*x**3)
def stream(voevent):
""" Returns name of event stream that detected the external trigger,
given the trigger's parsed VOEvent file """
role = voevent.get_role() # observation, test, or utility
ivorn = voevent.get_ivorn() # get the identifier of the event
# get the part of the ivorn that is the stream identifier
streamIvorn = ivorn.split('#')[0]
# get element with the relevant tag name
ins_tag = streamIvorn + ' ' + role
return ins_tag
# define the external trigger object class
class GRB(object):
""" Instance of an external trigger event (i.e. gamma-ray burst) """
def __init__(self, graceid, xml):
self.graceid = graceid
self.xml = xml # standard GRB designation
self.name = os.path.splitext(xml)[0] # name of VOEvent .xml file accompanying the GRB
self.voevent = VOEventLib.Vutil.parse(xml) # parsed VOEvent informations
wwd = VOEventLib.Vutil.getWhereWhen(self.voevent)
self.RA, self.dec = wwd['longitude'], wwd['latitude'] # right ascention, declination
self.err_rad = wwd['positionalError'] # error radius
self.inst = stream(self.voevent) # instrument that detected the event
self.fits = self.name+'.fits.gz' # name of .fits file for this event
self.gpstime = int(floor(iso8601_to_gps(wwd['time'])))
def submit_gracedb_log(self, message, filename=None, tagname=None):
""" Wrapper for gracedb.writeLog() for this event """
if filename is not None: gracedb.writeLog(self.graceid, message, filename, tagname=tagname)
else: gracedb.writeLog(self.graceid, message, tagname=tagname)
def sky_map(self, nside):
""" Returns a numpy array equivalent to the one that would get written
to a FITS file for this event """
# convert RA, dec and error radius to standard spherical coordinates
theta, phi, err = np.deg2rad( (90. - self.dec, self.RA, self.err_rad) )
# calculate the probability distribution and store it in a skymap
npix = hp.nside2npix(nside)
trig_map = np.array( [pdf(i, nside, theta, phi, err) for i in xrange(npix)] )
trig_map /= np.sum(trig_map) # normalize
return trig_map
def write_fits(self, sky_map, publish=False):
""" Given sky location and error radius of an external trigger, fit
it to a von Mises-Fisher distribution and write to a HEALPix .fits
file whose name is supplied """
# write to a .fits file
fits.write_sky_map(self.fits, trig_map, objid=self.graceid, gps_time=self.gpstime)
# publish to GraceDB if desired
if publish: self.submit_gracedb_log(self.graceid, "Uploaded sky map",
filename=self.fits, tagname="sky_loc")
def search(self, tl, th):
""" Search for coincident GW events happening within a window
of [-tl, +th] seconds in gpstime """
start, end = self.gpstime + tl, self.gpstime + th
arg = '%s..%s' % (start, end)
# return list of graceids of coincident events
try:
return list(gracedb.events(arg))
except:
import sys
print "Problem accessing GraCEDb while calling gracedb_events.exttrig.GRB.search()"
sys.exit(1)
# define special attributes for short- and long-duration GRB coincidence searches
def short_search(self):
""" Speecialized short-duration coincidence search; also annotates
relevant events with brief overview of results """
results = [event for event in self.search(-5, 1) if event['graceid'][0] == 'G' or event['graceid'][0] == 'T']
if results == []:
message = 'No GW candidates in window [-5,+1] seconds'
self.submit_gracedb_log(message, tagname="ext_coinc") # annotate GRB with news of lack of news
else:
for result in results:
gid = result['graceid']
far = np.float64( result['far'] )
message1 = "Raven: GW candidate found: <a href='http://gracedb.ligo.org/events/"
message1 += gid + "'>" + gid + "</a> with untriggered FAR = %s Hz within [-5,+1] seconds" % far
self.submit_gracedb_log(message1, tagname="ext_coinc") # annotate GRB with news of discovery
message2 = "Raven: External trigger <a href='http://gracedb.ligo.org/events/"
message2 += self.graceid + "'>" + self.name + "</a> within window [-5,+1] seconds"
GW(gid).submit_gracedb_log(message2, tagname="ext_coinc") # annotate GW with news of discovery
return results
def long_search(self):
""" Speecialized long-duration coincidence search; also annotates
relevant events with brief overview of results """
result1 = [event for event in self.search(-120, -5) if event['graceid'][0] == 'G' or event['graceid'][0] == 'T']
result2 = [event for event in self.search(1, 60) if event['graceid'][0] == 'G' or event['graceid'][0] == 'T']
results = result1 + result2 # must ensure the two searches do not overlap
if results == []:
message = 'No GW candidates in window [-120,+60] seconds'
self.submit_gracedb_log(message, tagname="ext_coinc") # annotate GRB with news of lack of news
else:
for result in results:
gid = result['graceid']
far = result['far']
message1 = "Raven: GW candidate found; <a href='http://gracedb.ligo.org/events/"
message1 += gid + "'>" + gid + "</a> with untriggered FAR = %s Hz within [-120,+60] seconds" % far
self.submit_gracedb_log(message1, tagname="ext_coinc") # annotate GRB with news of discovery
message2 = "Raven: External trigger <a href='http://gracedb.ligo.org/events/"
message2 += self.graceid + "'>" + self.name + "</a> within window [-120,+60] seconds"
GW(gid).submit_gracedb_log(message2, tagname="ext_coinc") # annotate GW with news of discovery
return results
def calc_signif_gracedb(self, coinc, gw_sky_map=None, incl_sky=False, short=True):
""" Calculate the improvement in significance that is got out of the second tier
of this hierarchical GRB-triggered search. """
if gw_sky_map is not None:
nside = hp.npix2nside( len(gw_sky_map) )
grb_sky_map = self.sky_map(nside)
psky = (4 * np.pi)**2 * np.sum( [x * y for x, y in zip(gw_sky_map, grb_sky_map)] ) / len(gw_sky_map)
if short: far = 6 * 1.268e-6 * Cacc( psky ) * coinc['far']
else : far = 180 * 1e-5 * Cacc( psky ) * coinc['far']
message = "Raven: Spatiotemporal coincidence with external trigger <a href='http://gracedb.ligo.org/events/"
message += self.graceid + "'>" + self.name + "</a> gives a coincident FAR = %s Hz" % far
GW(coinc['graceid']).submit_gracedb_log(message, tagname="ext_coinc")
else:
if short: far = 6 * 1.268e-6 * coinc['far']
else: far = 180 * 1e-5 * coinc['far']
message = "Raven: Temporal coincidence with external trigger <a href='http://gracedb.ligo.org/events/"
message += self.graceid + "'>" + self.name + "</a> gives a coincident FAR = %s Hz" % far
GW(coinc['graceid']).submit_gracedb_log(message, tagname="ext_coinc")
#!/usr/bin/env python
"""
Module to define functions and attributes corresponding
to some gravitational-wave candidate event
"""
__author__ = "Alex Urban <alexander.urban@ligo.org>"
import os
import tempfile
import numpy as np
import healpy as hp
from exttrig import GRB
from ligo.gracedb.rest import GraceDb
# initiate instance of GraceDB server as a global variable
gracedb = GraceDb()
# define function for use later
def get_fits(gw_event):
""" Downloads and unzips .fits file from gracedb into the
current working directory """
os.system('gracedb download ' + gw_event.graceid + ' skymap.fits.gz')
# define the gravitational-wave candidate event object class
class GW:
""" Instance of a gravitational-wave candidate event """
def __init__(self, graceid):
self.graceid = graceid # graceid of GW candidate
self.fits = 'skymap.fits.gz' # default name of fits file
try:
get_fits(self) # download .fits file from gracedb
except:
print 'ERROR: Could not find file skymap.fits.gz for event ' + self.graceid
pass
self.__result__ = gracedb.events(query=self.graceid)
for event in self.__result__:
self.far = event['far']
self.gpstime = event['gpstime']
def submit_gracedb_log(self, message, tagname=None):
""" wrapper for gracedb.writeLog() for this event """
gracedb.writeLog(self.graceid,message,tagname=tagname)
def search(self, tl, th):
""" Search for coincident GW events happening within a window
of [-tl, +th] seconds in gpstime """
start, end = self.gpstime + tl, self.gpstime + th
arg = '%s..%s' % (start, end)
# return list of graceids of coincident events
try:
return list(gracedb.events(arg))
except HTTPError:
import sys
print "Problem accessing GraCEDb while calling gracedb_events.grace.GW.search()"
raise HTTPError
sys.exit(1)
# define special attributes for short- and long-duration GRB coincidence searches
def short_search(self):
""" Speecialized short-duration coincidence search; also annotates
relevant events with brief overview of results """
result = [event for event in self.search(-1, 5) if event['graceid'][0] == 'E']
if result == []:
message = 'No external triggers in window [-1,+5] seconds'
self.submit_gracedb_log(message, tagname="ext_coinc") # annotate GRB with news of lack of news
else:
from exttrig import GRB
for i in xrange(len(result)):
gid = result[i]['graceid']
filedict = gracedb.files(gid).json()
for key in filedict: # search for this trigger's VOEvent file
if key.endswith('.xml'):
voevent = key
result[i]['file'] = voevent
break
os.system('/usr/bin/gracedb download %s %s' % (gid, voevent))
grb = GRB(gid,voevent)
message1 = "GW candidate found: <a href='http://gracedb.ligo.org/events/"
message1 += self.graceid + "'>" + self.graceid + "</a> with FAR = %s within [-5,+1] seconds" % self.far
grb.submit_gracedb_log(message1, tagname="ext_coinc") # annotate GRB with news of discovery
message2 = "External trigger <a href='http://gracedb.ligo.org/events/"
message2 += gid + "'>" + grb.name + "</a> within window [-5,+1] seconds"
self.submit_gracedb_log(message2, tagname="ext_coinc") # annotate GW with news of discovery
return result
def long_search(self):
""" Speecialized long-duration coincidence search; also annotates
relevant events with brief overview of results """
result1 = [event for event in self.search(-60, -1) if event['graceid'][0] == 'E']
result2 = [event for event in self.search(5, 120) if event['graceid'][0] == 'E']
result = result1 + result2 # must ensure the two searches do not overlap
if result == []:
message = 'No external triggers in window [-60,+120] seconds'
self.submit_gracedb_log(message, tagname="ext_coinc") # annotate GRB with news of lack of news
else:
from exttrig import GRB
for i in xrange(len(result)):
gid = result[i]['graceid']
filedict = gracedb.files(gid).json()
for key in filedict: # search for this trigger's VOEvent file
if key.endswith('.xml'):
voevent = key
result[i]['file'] = voevent
break
os.system('/usr/bin/gracedb download %s %s' % (gid, voevent))
grb = GRB(gid,voevent)
message1 = "GW candidate found; <a href='http://gracedb.ligo.org/events/"
message1 += self.graceid + "'>" + self.graceid + "</a> with FAR = %s within [-120,+60] seconds" % self.far
grb.submit_gracedb_log(message1, tagname="ext_coinc") # annotate GRB with news of discovery
message2 = "External trigger <a href='http://gracedb.ligo.org/events/"
message2 += gid + "'>" + grb.name + "</a> within window [-120,+60] seconds"
self.submit_gracedb_log(message2, tagname="ext_coinc") # annotate GW with news of discovery
return result
def hardware_search(self):
result = [event for event in self.search(-5, 5) if event['graceid'][0] == 'H']
if result == []:
message = 'No unblind injections in window [-5,+5] seconds'
self.submit_gracedb_log(message, tagname="analyst_comments") # annotate GW with news of lack of news
else:
for i in xrange(len(result)):
gid = result[i]['graceid']
filedict = gracedb.files(gid).json()
for key in filedict: # search for this injection's sim_inspiral table
if key.endswith('.xml.gz'):
sim_inspiral_table = key
result[i]['file'] = sim_inspiral_table
break
message = "Unblind injection <a href='http://gracedb.ligo.org/events/"
message += "%s'>%s</a> within window [-5,+5] seconds" % (gid, gid)
self.submit_gracedb_log(message, tagname="analyst_comments") # annotate GW with news of discovery
return result
#!/usr/bin/env python
#!/usr/bin/python
#
# Project Librarian: Alex Urban
# Graduate Student
# UW-Milwaukee Department of Physics
# Center for Gravitation & Cosmology
# <alexander.urban@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/>.
#
"""
Module to define functions and attributes corresponding
......
#!/usr/bin/env python
#!/usr/bin/python
#
# Project Librarian: Alex Urban
# Graduate Student
# UW-Milwaukee Department of Physics
# Center for Gravitation & Cosmology
# <alexander.urban@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/>.
#
"""
Module containing time- and sky- coincidence search functions.
......
#!/usr/bin/env python
"""
Helper module to handle workflow directory structure
"""
__author__ = "Alex Urban <alexander.urban@ligo.org>"
import os
home = os.getenv("HOME")
class directory(object):
""" Instance of a working directory integrated into the workflow """
def __init__(self, graceid):
self.graceid = graceid # unique ID of event in GraCEDb
self.event_type = graceid[:1] # first character of graceid encodes event type
# organize events so that no more than 1000 live in one directory
self.millenium = self.graceid[1:-3] + '000'
# name the working directory for a given event
self.name = home + '/working/' + self.event_type + '/' + self.millenium + '/' + self.graceid
def build_and_move(self):
""" Method that builds, and then moves to, the working directory """
# try to build directory ${HOME}/working
try:
os.mkdir(home+'/working')
except OSError:
pass
# try to build directory ${HOME}/working/${event_type}
try:
os.mkdir(home+'/working/'+self.event_type)
except OSError:
pass
# try to build directory ${HOME}/working/${event_type}/${millenium}
try:
os.mkdir(home+'/working/'+self.event_type+'/'+self.millenium)
except OSError:
pass
# try to build directory ${HOME}/working/${event_type}/${millenium}/${graceid}
try:
os.mkdir(self.name)
except OSError:
print 'Working directory %s already exists' % self.name
pass
os.chdir(self.name) # move to the working directory
#!/usr/bin/env python
#!/usr/bin/python
#
# Project Librarian: Alex Urban
# Graduate Student
# UW-Milwaukee Department of Physics
# Center for Gravitation & Cosmology
# <alexander.urban@ligo.org>
# Graduate Student
# UW-Milwaukee Department of Physics
# Center for Gravitation & Cosmology
# <alexander.urban@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
......@@ -26,18 +26,18 @@ from distutils.core import setup
setup(
name='raven',
version='0.0',
url='http://gracedb.ligo.org',
author='Alex Urban',
author_email='alexander.urban@ligo.org',
description='Low-latency coincidence search between external triggers and GW candidates',
license='GNU General Public License Version 3',
packages=['raven'],
scripts=[
'bin/raven_coinc_search_gracedb',
name='raven',
version='1.0',
url='http://gracedb.ligo.org',
author='Alex Urban',
author_email='alexander.urban@ligo.org',
description='Low-latency coincidence search between external triggers and GW candidates',
license='GNU General Public License Version 3',
packages=['raven'],
scripts=[
'bin/raven_coinc_search_gracedb',
'bin/raven_fetch_external_triggers',
'bin/raven_gcn_web_scraper',
'bin/raven_grb_pop_from_gwinj'
]
'bin/raven_grb_pop_from_gwinj'
]
)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment