gstlal-inspiral  0.4.2
 All Classes Namespaces Files Functions Variables Pages
gstlal_inspiral_lvalert_psd_plotter
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2013 Kipp Cannon
4 #
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 # Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 
19 ## @file gstlal_inspiral_lvalert_psd_plotter
20 # A program to listen to lvalerts, download the psd from gstlal gracedb events, plot it, and upload the results
21 #
22 # ### Command line interface
23 #
24 # + `--no-upload`: Write plots to disk.
25 # + `--skip-404`: Skip events that give 404 (file not found) errors (default is to abort).
26 # + `--verbose`: Be verbose.
27 #
28 # =============================================================================
29 #
30 # Preamble
31 #
32 # =============================================================================
33 #
34 
35 
36 import httplib
37 import logging
38 import math
39 import numpy
40 from optparse import OptionParser
41 import os.path
42 import StringIO
43 import sys
44 import time
45 import urlparse
46 
47 
48 from glue.ligolw import ligolw
49 from glue.ligolw import array as ligolw_array
50 from glue.ligolw import param as ligolw_param
51 from glue.ligolw import lsctables
52 from glue.ligolw import utils as ligolw_utils
53 from gstlal.reference_psd import horizon_distance
54 from gstlal import plotpsd
55 try:
56  from ligo.gracedb import cli as gracedb
57 except ImportError:
58  from ligo import gracedb
59 from ligo.lvalert.utils import get_LVAdata_from_stdin
60 from pylal import series as lal_series
61 
62 
63 class LIGOLWContentHandler(ligolw.LIGOLWContentHandler):
64  pass
65 ligolw_array.use_in(LIGOLWContentHandler)
66 ligolw_param.use_in(LIGOLWContentHandler)
67 lsctables.use_in(LIGOLWContentHandler)
68 
69 
70 #
71 # =============================================================================
72 #
73 # Library
74 #
75 # =============================================================================
76 #
77 
78 
79 def get_filename(gracedb_client, graceid, filename, retries = 3, retry_delay = 10.0, ignore_404 = False):
80  for i in range(retries):
81  logging.info("retrieving \"%s\" for %s" % (filename, graceid))
82  response = gracedb_client.files(graceid, filename)
83  if response.status == httplib.OK:
84  return response
85  if response.status == httplib.NOT_FOUND and ignore_404:
86  logging.warning("retrieving \"%s\" for %s: (%d) %s. skipping ..." % (filename, graceid, response.status, response.reason))
87  return None
88  logging.warning("retrieving \"%s\" for %s: (%d) %s. pausing ..." % (filename, graceid, response.status, response.reason))
89  time.sleep(retry_delay)
90  raise Exception("retrieving \"%s\" for %s: (%d) %s" % (filename, graceid, response.status, response.reason))
91 
92 
93 def get_psds(gracedb_client, graceid, filename = "psd.xml.gz", ignore_404 = False):
94  response = get_filename(gracedb_client, graceid, filename = filename, ignore_404 = ignore_404)
95  if response is None:
96  return None
97  return lal_series.read_psd_xmldoc(ligolw_utils.load_fileobj(response, contenthandler = LIGOLWContentHandler)[0])
98 
99 
100 def get_coinc_xmldoc(gracedb_client, graceid, filename = "coinc.xml"):
101  return ligolw_utils.load_fileobj(get_filename(gracedb_client, graceid, filename = filename), contenthandler = LIGOLWContentHandler)[0]
102 
103 
104 def upload_fig(fig, gracedb_client, graceid, filename = "psd.png"):
105  plotfile = StringIO.StringIO()
106  fig.savefig(plotfile, format = os.path.splitext(filename)[-1][1:])
107  logging.info("uploading \"%s\" for %s" % (filename, graceid))
108  response = gracedb_client.writeLog(graceid, "strain spectral densities", filename = filename, filecontents = plotfile.getvalue(), tagname = "psd")
109  if response.status != httplib.CREATED:
110  raise Exception("upload of \"%s\" for %s failed: %s" % (filename, graceid, response["error"]))
111 
112 
113 #
114 # =============================================================================
115 #
116 # Command Line
117 #
118 # =============================================================================
119 #
120 
121 
122 def parse_command_line():
123  parser = OptionParser()
124  parser.add_option("--no-upload", action = "store_true", help = "Write plots to disk.")
125  parser.add_option("--skip-404", action = "store_true", help = "Skip events that give 404 (file not found) errors (default is to abort).")
126  parser.add_option("-v", "--verbose", action = "store_true", help = "Be verbose.")
127  options, graceids = parser.parse_args()
128 
129  if not graceids:
130  # FIXME: lvalert_listen doesn't allow command-line options
131  options.verbose = True
132 
133  # can only call basicConfig once (otherwise need to switch to more
134  # complex logging configuration)
135  if options.verbose:
136  logging.basicConfig(format = "%(asctime)s:%(message)s", level = logging.INFO)
137  else:
138  logging.basicConfig(format = "%(asctime)s:%(message)s")
139 
140  return options, graceids
141 
142 
143 #
144 # =============================================================================
145 #
146 # Main
147 #
148 # =============================================================================
149 #
150 
151 
152 options, graceids = parse_command_line()
153 
154 
155 if not graceids:
156  lvalert_data = get_LVAdata_from_stdin(sys.stdin, as_dict = True)
157  logging.info("%(alert_type)s-type alert for event %(uid)s" % lvalert_data)
158  logging.info("lvalert data: %s" % repr(lvalert_data))
159  filename = os.path.split(urlparse.urlparse(lvalert_data["file"]).path)[-1]
160  if filename not in (u"coinc.xml",) and "_CBC_" not in filename:
161  logging.info("filename is not 'coinc.xml'. skipping")
162  sys.exit()
163  graceids = [str(lvalert_data["uid"])]
164  # pause to give the psd file a chance to get uploaded.
165  time.sleep(8)
166 
167 
168 gracedb_client = gracedb.Client()
169 
170 
171 for graceid in graceids:
172  psds = get_psds(gracedb_client, graceid, ignore_404 = options.skip_404)
173  if psds is None:
174  continue
175  fig = plotpsd.plot_psds(psds, get_coinc_xmldoc(gracedb_client, graceid))
176  if options.no_upload:
177  filename = "psd_%s.png" % graceid
178  logging.info("writing %s ..." % filename)
179  fig.savefig(filename)
180  else:
181  upload_fig(fig, gracedb_client, graceid)
182  logging.info("finished processing %s" % graceid)