gstlal-inspiral  0.4.2
 All Classes Namespaces Files Functions Variables Pages
gstlal_inspiral_plotsummary
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2009-2013 Kipp Cannon, Chad Hanna, Drew Keppel
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
20 # A program to produce a variety of plots from a gstlal inspiral analysis, e.g. IFAR plots, missed found, etc.
21 
22 #
23 # =============================================================================
24 #
25 # Preamble
26 #
27 # =============================================================================
28 #
29 
30 
31 import math
32 import matplotlib
33 matplotlib.rcParams.update({
34  "font.size": 16.0,
35  "axes.titlesize": 14.0,
36  "axes.labelsize": 14.0,
37  "xtick.labelsize": 13.0,
38  "ytick.labelsize": 13.0,
39  "legend.fontsize": 10.0,
40  "figure.dpi": 300,
41  "savefig.dpi": 300,
42  "text.usetex": True,
43  "path.simplify": True
44 })
45 from matplotlib import figure
46 from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
47 import scipy
48 import numpy
49 from optparse import OptionParser
50 import sqlite3
51 import sys
52 
53 import os
54 
55 from glue import segments
56 from glue.ligolw import dbtables
57 from glue.ligolw import lsctables
58 from glue.ligolw.utils import segments as ligolw_segments
59 from glue import lal
60 from pylal import db_thinca_rings
61 from pylal import git_version
62 from pylal import SimBurstUtils
63 from pylal.xlal.datatypes.ligotimegps import LIGOTimeGPS
64 from gstlal import far
65 from gstlal import inspiral_pipe
66 
67 
68 class SimInspiral(lsctables.SimInspiral):
69  @property
70  def mtotal(self):
71  return self.mass1 + self.mass2
72 
73  @property
74  def chi(self):
75  return (self.mass1 * self.spin1z + self.mass2 * self.spin2z) / self.mtotal
76 
77 
78 class SnglInspiral(lsctables.SnglInspiral):
79  @property
80  def mtotal(self):
81  return self.mass1 + self.mass2
82 
83  @property
84  def eta(self):
85  return self.mass1 * self.mass2 / self.mtotal**2.
86 
87  @property
88  def mchirp(self):
89  return self.mtotal * self.eta**0.6
90 
91  @property
92  def chi(self):
93  return (self.mass1 * self.spin1z + self.mass2 * self.spin2z) / self.mtotal
94 
95  def get_effective_snr(self, fac):
96  return self.snr / (self.chisq / self.chisq_dof)**.5
97 
98 lsctables.LIGOTimeGPS = LIGOTimeGPS
99 lsctables.SimInspiralTable.RowType = SimInspiral
100 lsctables.SnglInspiralTable.RowType = SnglInspiral
101 
102 
103 __author__ = "Kipp Cannon <kipp.cannon@ligo.org>, Chad Hanna <channa@ligo.caltech.edu>"
104 __version__ = "git id %s" % git_version.id
105 __date__ = git_version.date
106 
107 
108 #
109 # =============================================================================
110 #
111 # Command Line
112 #
113 # =============================================================================
114 #
115 
116 
117 def parse_command_line():
118  parser = OptionParser(
119  version = "Name: %%prog\n%s" % git_version.verbose_msg
120  )
121  parser.add_option("","--input-cache", help = "Also get the list of databases to process from this LAL cache.")
122  parser.add_option("--user-tag", metavar = "user-tag", default = "ALL", help = "Set the prefix for output filenames (default = \"ALL\").")
123  parser.add_option("--output-dir", metavar = "output-dir", default = ".", help = "Provide an output directory")
124  parser.add_option("-f", "--format", metavar = "{\"png\",\"pdf\",\"svg\",\"eps\",...}", action = "append", default = [], help = "Set the output image format. Can be given multiple times (default = \"png\").")
125  parser.add_option("--segments-name", metavar = "name", default = "statevectorsegments", help = "Set the name of the segments that were analyzed (default = \"statevectorsegments\").")
126  parser.add_option("--vetoes-name", metavar = "name", default = "vetoes", help = "Set the name of the veto segments (default = \"vetoes\").")
127  parser.add_option("--plot-group", metavar = "number", action = "append", default = None, help = """Generate the given plot group. Can be given multiple times (default = make all plot groups)
128  0. Summary Table (top 10 loudest events globally across all zero lag triggers read in)
129  1. Missed Found (Scatter plots of missed and found injections on several axes)
130  2. Injection Parameter Accuracy Plots
131  3. Background Vs Injection Plots (sngl detector triggers from coincs of snr, chisq, bank chisq,...)
132  4. Background Vs Injection Plots pairwise (effective snr DET1 Vs. DET2...),
133  5. Rate Vs Threshold (SNR histograms, IFAR histograms, ...)
134  6. Injection Parameter Distribution Plots (The input parameters that went into inspinj, like mass1 vs mass2...)
135 """)
136  parser.add_option("--far-threshold", metavar = "Hz", default = 1. / (30 * 86400), type = "float", help = "Set the FAR threshold for found injections (default = 1 / 30 days).")
137  parser.add_option("-t", "--tmp-space", metavar = "path", help = "Path to a directory suitable for use as a work area while manipulating the database file. The database file will be worked on in this directory, and then moved to the final location when complete. This option is intended to improve performance when running in a networked environment, where there might be a local disk with higher bandwidth than is available to the filesystem on which the final output will reside.")
138  parser.add_option("-v", "--verbose", action = "store_true", help = "Be verbose.")
139  options, filenames = parser.parse_args()
140 
141  if options.plot_group is not None:
142  options.plot_group = sorted(map(int, options.plot_group))
143  if not options.format:
144  options.format = ["png"]
145 
146  if not filenames:
147  filenames = []
148  if options.input_cache:
149  filenames.extend(c.path for c in map(lal.CacheEntry, open(options.input_cache)))
150 
151  return options, filenames
152 
153 
154 #
155 # =============================================================================
156 #
157 # Database
158 #
159 # =============================================================================
160 #
161 
162 
163 class CoincDatabase(object):
164  def __init__(self, connection, data_segments_name, veto_segments_name = None, verbose = False, wiki = None, base = None, program_name = "gstlal_inspiral"):
165  """
166  Compute and record some summary information about the
167  database.
168  """
169 
170  self.base = base
171  self.connection = connection
172  xmldoc = dbtables.get_xml(connection)
173 
174  cursor = connection.cursor()
175 
176  # find the tables
177  try:
178  self.sngl_inspiral_table = lsctables.SnglInspiralTable.get_table(xmldoc)
179  except ValueError:
180  self.sngl_inspiral_table = None
181  try:
182  self.sim_inspiral_table = lsctables.SimInspiralTable.get_table(xmldoc)
183  except ValueError:
184  self.sim_inspiral_table = None
185  try:
186  self.coinc_def_table = lsctables.CoincDefTable.get_table(xmldoc)
187  self.coinc_table = lsctables.CoincTable.get_table(xmldoc)
188  self.time_slide_table = lsctables.TimeSlideTable.get_table(xmldoc)
189  except ValueError:
190  self.coinc_def_table = None
191  self.coinc_table = None
192  self.time_slide_table = None
193  try:
194  self.coinc_inspiral_table = lsctables.CoincInspiralTable.get_table(xmldoc)
195  except ValueError:
196  self.coinc_inspiral_table = None
197 
198  # determine a few coinc_definer IDs
199  # FIXME: don't hard-code the numbers
200  if self.coinc_def_table is not None:
201  try:
202  self.ii_definer_id = self.coinc_def_table.get_coinc_def_id("inspiral", 0, create_new = False)
203  except KeyError:
204  self.ii_definer_id = None
205  try:
206  self.si_definer_id = self.coinc_def_table.get_coinc_def_id("inspiral", 1, create_new = False)
207  except KeyError:
208  self.si_definer_id = None
209  try:
210  self.sc_definer_id = self.coinc_def_table.get_coinc_def_id("inspiral", 2, create_new = False)
211  except KeyError:
212  self.sc_definer_id = None
213  else:
214  self.ii_definer_id = None
215  self.si_definer_id = None
216  self.sc_definer_id = None
217 
218  # retrieve the distinct on and participating instruments
219  self.on_instruments_combos = [frozenset(lsctables.instrument_set_from_ifos(x)) for x, in cursor.execute("SELECT DISTINCT(instruments) FROM coinc_event WHERE coinc_def_id == ?", (self.ii_definer_id,))]
220  self.participating_instruments_combos = [frozenset(lsctables.instrument_set_from_ifos(x)) for x, in cursor.execute("SELECT DISTINCT(ifos) FROM coinc_inspiral")]
221 
222  # get the segment lists
223  self.seglists = ligolw_segments.segmenttable_get_by_name(xmldoc, data_segments_name).coalesce()
224  self.instruments = set(self.seglists)
225  if veto_segments_name is not None:
226  self.veto_segments = ligolw_segments.segmenttable_get_by_name(xmldoc, veto_segments_name).coalesce()
227  else:
228  self.veto_segments = segments.segmentlistdict()
229  self.seglists -= self.veto_segments
230 
231  # Get the live time used for the far calculation. By convention this is simply the entire interval of the analysis with no regard for segments
232  self.farsegs = far.get_live_time_segs_from_search_summary_table(connection)
233 
234  # get the live time
235  if verbose:
236  print >>sys.stderr, "calculating background livetimes: ",
237  self.offset_vectors = db_thinca_rings.get_background_offset_vectors(connection)
238 
239  if verbose:
240  print >>sys.stderr
241  self.zerolag_livetime = {}
242  self.background_livetime = {}
243  for on_instruments in self.on_instruments_combos:
244  self.zerolag_livetime[on_instruments] = float(abs(self.seglists.intersection(on_instruments) - self.seglists.union(self.instruments - on_instruments)))
245  # FIXME: background livetime hard-coded to be same
246  # as zero-lag livetime. figure out what to do
247  self.background_livetime.update(self.zerolag_livetime)
248 
249  # verbosity
250  if verbose:
251  print >>sys.stderr, "database overview:"
252  for on_instruments in self.on_instruments_combos:
253  print >>sys.stderr, "\tzero-lag livetime for %s: %f s" % ("+".join(sorted(on_instruments)), self.zerolag_livetime[on_instruments])
254  print >>sys.stderr, "\tbackground livetime for %s: %f s" % ("+".join(sorted(on_instruments)), self.background_livetime[on_instruments])
255  if self.sngl_inspiral_table is not None:
256  print >>sys.stderr, "\tinspiral events: %d" % len(self.sngl_inspiral_table)
257  if self.sim_inspiral_table is not None:
258  print >>sys.stderr, "\tinjections: %d" % len(self.sim_inspiral_table)
259  if self.time_slide_table is not None:
260  print >>sys.stderr, "\ttime slides: %d" % cursor.execute("SELECT COUNT(DISTINCT(time_slide_id)) FROM time_slide").fetchone()[0]
261  if self.coinc_def_table is not None:
262  for description, n in cursor.execute("SELECT description, COUNT(*) FROM coinc_definer NATURAL JOIN coinc_event GROUP BY coinc_def_id"):
263  print >>sys.stderr, "\t%s: %d" % (description, n)
264 
265  if wiki:
266  wiki.write("database overview:\n\n")
267  for on_instruments in self.on_instruments_combos:
268  wiki.write("||zero-lag livetime for %s||%f s||\n" % ("+".join(sorted(on_instruments)), self.zerolag_livetime[on_instruments]))
269  wiki.write("||background livetime for %s ||%f s||\n" % ("+".join(sorted(on_instruments)), self.background_livetime[on_instruments]))
270  if self.sngl_inspiral_table is not None:
271  wiki.write("||inspiral events|| %d||\n" % len(self.sngl_inspiral_table))
272  if self.sim_inspiral_table is not None:
273  wiki.write("||injections|| %d||\n" % len(self.sim_inspiral_table))
274  if self.time_slide_table is not None:
275  wiki.write("||time slides|| %d||\n" % cursor.execute("SELECT COUNT(DISTINCT(time_slide_id)) FROM time_slide").fetchone()[0])
276  if self.coinc_def_table is not None:
277  for description, n in cursor.execute("SELECT description, COUNT(*) FROM coinc_definer NATURAL JOIN coinc_event GROUP BY coinc_def_id"):
278  wiki.write("||%s||%d||\n" % (description, n) )
279 
280 
281 #
282 # =============================================================================
283 #
284 # Utilities
285 #
286 # =============================================================================
287 #
288 
289 
290 def sim_end_time(sim, instrument):
291  # this function requires .get_time_geocent() and .get_ra_dec()
292  # methods, and so can be used for both burst and inspiral
293  # injections. FIXME: update function call when inspiral
294  # injections carry offset vector information
295  return SimBurstUtils.time_at_instrument(sim, instrument, {instrument: 0.0})
296 
297 
298 def roman(i, arabics = (1000,900,500,400,100,90,50,40,10,9,5,4,1), romans = ("m","cm","d","cd","c","xc","l","xl","x","ix","v","iv","i")):
299  if not arabics:
300  return ""
301  if i < arabics[0]:
302  return roman(i, arabics[1:], romans[1:])
303  return romans[0] + roman(i - arabics[0], arabics, romans)
304 
305 
306 #
307 # width is in mm, default aspect ratio is the golden ratio
308 #
309 
310 
311 def create_plot(x_label = None, y_label = None, width = 165.0, aspect = None):
312  if aspect is None:
313  aspect = (1 + math.sqrt(5)) / 2
314  fig = figure.Figure()
315  FigureCanvas(fig)
316  fig.set_size_inches(width / 25.4, width / 25.4 / aspect)
317  axes = fig.gca()
318  axes.grid(True)
319  if x_label is not None:
320  axes.set_xlabel(x_label)
321  if y_label is not None:
322  axes.set_ylabel(y_label)
323  return fig, axes
324 
325 
326 def create_sim_coinc_view(connection):
327  """
328  Construct a sim_inspiral --> best matching coinc_event mapping.
329  Only injections that match at least one coinc get an entry in this
330  table.
331  """
332  #
333  # the log likelihood ratio stored in the likelihood column of the
334  # coinc_event table is the ranking statistic. the "best match" is
335  # the coinc with the highest value in this column. although it has
336  # not been true in the past, there is now a one-to-one relationship
337  # between the value of this ranking statistic and false-alarm rate,
338  # therefore it is OK to order by log likelihood ratio and then,
339  # later, impose a "detection" threshold based on false-alarm rate.
340  #
341 
342  connection.cursor().execute("""
343 CREATE TEMPORARY TABLE
344  sim_coinc_map
345 AS
346  SELECT
347  sim_inspiral.simulation_id AS simulation_id,
348  (
349  SELECT
350  coinc_event.coinc_event_id
351  FROM
352  coinc_event_map AS a
353  JOIN coinc_event_map AS b ON (
354  b.coinc_event_id == a.coinc_event_id
355  )
356  JOIN coinc_event ON (
357  b.table_name == 'coinc_event'
358  AND b.event_id == coinc_event.coinc_event_id
359  )
360  WHERE
361  a.table_name == 'sim_inspiral'
362  AND a.event_id == sim_inspiral.simulation_id
363  AND NOT EXISTS (SELECT * FROM time_slide WHERE time_slide.time_slide_id == coinc_event.time_slide_id AND time_slide.offset != 0)
364  ORDER BY
365  coinc_event.likelihood DESC
366  LIMIT 1
367  ) AS coinc_event_id
368  FROM
369  sim_inspiral
370  WHERE
371  coinc_event_id IS NOT NULL
372  """)
373 
374 
375 #
376 # =============================================================================
377 #
378 # Summary Table
379 #
380 # =============================================================================
381 #
382 
383 
384 class SummaryTable(object):
385  def __init__(self):
386  self.candidates = []
387  self.bgcandidates = []
388  self.livetime = {}
389  self.num_trigs = {}
390 
391  def add_contents(self, contents):
392  self.base = contents.base
393  if contents.sim_inspiral_table:
394  #For now we only return summary information on non injections
395  return
396  self.candidates += contents.connection.cursor().execute("""
397 SELECT
398  coinc_inspiral.combined_far,
399  coinc_inspiral.false_alarm_rate,
400  coinc_event.likelihood,
401  coinc_inspiral.snr,
402  coinc_inspiral.end_time + coinc_inspiral.end_time_ns * 1e-9,
403  coinc_inspiral.mass,
404  coinc_inspiral.mchirp,
405  coinc_inspiral.ifos,
406  coinc_event.instruments,
407  (SELECT
408  group_concat(sngl_inspiral.ifo || ":" || sngl_inspiral.snr || ":" || sngl_inspiral.chisq || ":" || sngl_inspiral.mass1 || ":" || sngl_inspiral.mass2, " ")
409  FROM
410  sngl_inspiral
411  JOIN coinc_event_map ON (
412  sngl_inspiral.event_id == coinc_event_map.event_id AND coinc_event_map.table_name == "sngl_inspiral"
413  )
414  WHERE
415  coinc_event_map.coinc_event_id == coinc_inspiral.coinc_event_id
416  )
417 FROM
418  coinc_inspiral
419  JOIN coinc_event ON (
420  coinc_event.coinc_event_id == coinc_inspiral.coinc_event_id
421  )
422 WHERE
423  NOT EXISTS(
424  SELECT
425  *
426  FROM
427  time_slide
428  WHERE
429  time_slide.time_slide_id == coinc_event.time_slide_id AND time_slide.offset != 0
430  )
431 ORDER BY
432  combined_far
433 LIMIT 10
434  """).fetchall()
435 
436  self.bgcandidates += contents.connection.cursor().execute("""
437 SELECT
438  coinc_inspiral.combined_far,
439  coinc_inspiral.false_alarm_rate,
440  coinc_event.likelihood,
441  coinc_inspiral.snr,
442  coinc_inspiral.end_time + coinc_inspiral.end_time_ns * 1e-9,
443  coinc_inspiral.mass,
444  coinc_inspiral.mchirp,
445  coinc_inspiral.ifos,
446  coinc_event.instruments,
447  (SELECT
448  group_concat(sngl_inspiral.ifo || ":" || sngl_inspiral.snr || ":" || sngl_inspiral.chisq || ":" || sngl_inspiral.mass1 || ":" || sngl_inspiral.mass2, " ")
449  FROM
450  sngl_inspiral
451  JOIN coinc_event_map ON (
452  sngl_inspiral.event_id == coinc_event_map.event_id AND coinc_event_map.table_name == "sngl_inspiral"
453  )
454  WHERE
455  coinc_event_map.coinc_event_id == coinc_inspiral.coinc_event_id
456  )
457 FROM
458  coinc_inspiral
459  JOIN coinc_event ON (
460  coinc_event.coinc_event_id == coinc_inspiral.coinc_event_id
461  )
462 WHERE
463  EXISTS(
464  SELECT
465  *
466  FROM
467  time_slide
468  WHERE
469  time_slide.time_slide_id == coinc_event.time_slide_id AND time_slide.offset != 0
470  )
471 ORDER BY
472  combined_far
473 LIMIT 10
474  """).fetchall()
475 
476 
477  contents.connection.cursor().execute("CREATE TEMPORARY TABLE distinct_ifos AS SELECT DISTINCT(ifos) AS ifos FROM coinc_inspiral")
478  for instruments, num in contents.connection.cursor().execute("""
479 SELECT distinct_ifos.ifos, count(*) FROM coinc_inspiral JOIN distinct_ifos ON (distinct_ifos.ifos==coinc_inspiral.ifos) JOIN coinc_event ON (coinc_event.coinc_event_id == coinc_inspiral.coinc_event_id) WHERE coinc_inspiral.ifos==distinct_ifos.ifos AND NOT EXISTS(SELECT * FROM time_slide WHERE time_slide.time_slide_id == coinc_event.time_slide_id AND time_slide.offset != 0) GROUP BY distinct_ifos.ifos;
480 """):
481  key = frozenset(lsctables.instrument_set_from_ifos(instruments))
482  self.num_trigs.setdefault(key,0)
483  self.num_trigs[key] += num
484 
485  contents.connection.cursor().execute("DROP TABLE distinct_ifos")
486 
487  for on_instruments in set(contents.background_livetime) | set(contents.zerolag_livetime):
488  self.livetime.setdefault(on_instruments, 0.0)
489 
490  for on_instruments, livetime in contents.zerolag_livetime.items():
491  self.livetime[on_instruments] += livetime
492 
493  def write_wiki_string(self, l, f, lt):
494  f.write("|| Rank || FAR (Hz) || FAP || ln &Lambda; || Combined SNR || GPS End Time || <i>M</i><sub>total</sub> / M<sub>&#x2299;</sub> || <i>M</i><sub>chirp</sub> / M<sub>&#x2299;</sub> || Participating Instruments || On Instruments ||\n")
495  f.write("|| || || || || || Instrument || SNR || &chi;<sup>2</sup>/DOF || <i>m</i><sub>1</sub> / M<sub>&#x2299;</sub> || <i>m</i><sub>2</sub> / M<sub>&#x2299;</sub> ||\n")
496  for rank, values in enumerate(l, 1):
497  values = tuple(values)
498  f.write('|| %d || %.2e || %.3g || %.3g || %.2f || %.4f || %.2f || %.2f || %s || %s ||\n' % ((rank,) + values[:9]))
499  for ifo_row in values[9].split():
500  ifo_row = ifo_row.split(":")
501  ifo_row[1:] = map(float, ifo_row[1:])
502  f.write('|| || || || || || %s || %.2f || %.2f || %.2f || %.2f ||\n' % tuple(ifo_row) )
503 
504  def finish(self):
505  self.candidates.sort()
506  f = open(self.base+'summary_table.txt','w')
507  f.write("=== Open box loudest 10 summary table ===\n")
508  self.write_wiki_string(self.candidates[:11], f, self.livetime)
509  f.close()
510 
511  f = open(self.base+'num_trigs_table.txt','w')
512  f.write("||<b>DETECTORS</b>||<b># COINC EVENTS</b>||\n")
513  for inst in self.livetime.keys():
514  f.write("||%s||" % ("".join(sorted(inst)),))
515  try:
516  num = self.num_trigs[inst]
517  except:
518  num = 0
519  f.write("%d||\n" % (num,))
520  f.close()
521 
522  f = open(self.base+'live_time_table.txt','w')
523  f.write("||<b>DETECTORS ON</b>||<b>LIVETIME (s) (d) (yr)</b>||\n")
524  for inst in self.livetime.keys():
525  f.write("||%s||%.2f %.2f %.2f||\n" % ("".join(sorted(inst)), self.livetime[inst],self.livetime[inst]/86400.0,self.livetime[inst]/31556926.0))
526  f.close()
527 
528  self.bgcandidates.sort()
529  f = open(self.base+'bgsummary_table.txt','w')
530  f.write("=== Closed box loudest 10 summary table ===\n")
531  self.write_wiki_string(self.bgcandidates[:11], f, self.livetime)
532  f.close()
533  yield None, None, None
534 
535 #
536 # =============================================================================
537 #
538 # Injection Parameter Distributions
539 #
540 # =============================================================================
541 #
542 
543 
544 class InjectionParameterDistributionPlots(object):
545  def __init__(self):
546  self.injections = {}
547 
548  def add_contents(self, contents):
549  if contents.sim_inspiral_table is None:
550  # no injections
551  return
552  for values in contents.connection.cursor().execute("""
553 SELECT
554  *
555 FROM
556  sim_inspiral
557  """):
558  sim = contents.sim_inspiral_table.row_from_cols(values)
559  del sim.process_id, sim.source, sim.simulation_id
560  instruments = frozenset(instrument for instrument, segments in contents.seglists.items() if sim.get_time_geocent() in segments)
561  self.injections.setdefault(sim.waveform, []).append(sim)
562 
563  def finish(self):
564  for waveform, sims in self.injections.items():
565  for col1,col2,ax1,ax2,name,aspect in [
566  ([sim.mass1 for sim in sims], [sim.mass2 for sim in sims], r"$M_{1}$ ($\mathrm{M}_{\odot}$)", r"$M_{2}$ ($\mathrm{M}_{\odot}$)", "sim_dist_m1_m2_%s", 1),
567  ([sim.geocent_end_time for sim in sims], [math.log10(sim.distance) for sim in sims], r"Time (s)", r"$\log_{10} (\mathrm{distance} / 1\,\mathrm{Mpc})$", "sim_dist_time_distance_%s",None),
568  ([sim.longitude * 12 / math.pi for sim in sims], [math.sin(sim.latitude) for sim in sims], r"RA (h)", r"$\sin \mathrm{dec}$", "sim_dist_ra_dec_%s",None),
569  ([math.cos(sim.inclination) for sim in sims], [sim.polarization for sim in sims], r"$\cos $Inclination (rad)", r"Polarization (rad)", "sim_dist_inc_pol_%s",None),
570  ([sim.spin1z for sim in sims], [sim.spin2z for sim in sims], r"Spin 1 z", r"Spin 2 z", "sim_dist_spin1z_spin2z_%s",None)]:
571  fig, axes = create_plot(ax1,ax2, aspect = aspect)
572  axes.set_title(r"Injection Parameter Distribution (%s Injections)" % waveform)
573  if len(col1) > 16383:
574  axes.plot(col1,col2, "k,")
575  else:
576  axes.plot(col1,col2, "k.")
577  minx, maxx = axes.get_xlim()
578  miny, maxy = axes.get_ylim()
579  if aspect == 1:
580  axes.set_xlim((min(minx, miny), max(maxx, maxy)))
581  axes.set_ylim((min(minx, miny), max(maxx, maxy)))
582  yield fig, name % (waveform), False
583 
584 
585 #
586 # =============================================================================
587 #
588 # Missed/Found Plot
589 #
590 # =============================================================================
591 #
592 
593 
594 class MissedFoundPlots(object):
595  class MissedFound(object):
596  def __init__(self, on_instruments, far_thresh):
597  self.on_instruments = on_instruments
598  self.far_thresh = far_thresh
599  self.found_in = {}
600 
601  def add_contents(self, contents):
602  self.base = contents.base
603  zerolag_segments = contents.seglists.intersection(self.on_instruments) - contents.seglists.union(contents.instruments - self.on_instruments)
604  for values in contents.connection.cursor().execute("""
605 SELECT
606  sim_inspiral.*,
607  (
608  SELECT
609  coinc_inspiral.ifos
610  FROM
611  sim_coinc_map
612  JOIN coinc_inspiral ON (
613  coinc_inspiral.coinc_event_id == sim_coinc_map.coinc_event_id
614  )
615  WHERE
616  sim_coinc_map.simulation_id == sim_inspiral.simulation_id
617  AND coinc_inspiral.combined_far < ?
618  )
619 FROM
620  sim_inspiral
621  """, (self.far_thresh if self.far_thresh is not None else float("+inf"),)):
622  sim = contents.sim_inspiral_table.row_from_cols(values)
623  del sim.process_id, sim.source, sim.simulation_id
624  if sim.get_time_geocent() in zerolag_segments:
625  participating_instruments = lsctables.instrument_set_from_ifos(values[-1])
626  if participating_instruments is not None:
627  participating_instruments = frozenset(participating_instruments)
628  try:
629  self.found_in[participating_instruments].append(sim)
630  except KeyError:
631  self.found_in[participating_instruments] = [sim]
632 
633  def finish(self):
634  f = open(self.base + "injection_summary.txt", "a")
635  missed = self.found_in.pop(None, [])
636  for cnt, (title, x_label, x_func, y_label, y_func, filename_fragment) in enumerate((
637  (r"Distance vs.\ Chirp Mass (With %s Operating)" % ", ".join(sorted(self.on_instruments)), r"$M_{\mathrm{chirp}}$ ($\mathrm{M}_{\odot}$)", lambda sim: sim.mchirp, r"$D$ ($\mathrm{Mpc}$)", lambda sim, instruments: sim.distance, "d_vs_mchirp"),
638  (r"Decisive Distance vs.\ Chirp Mass (With %s Operating)" % ", ".join(sorted(self.on_instruments)), r"$M_{\mathrm{chirp}}$ ($\mathrm{M}_{\odot}$)", lambda sim: sim.mchirp, r"$\mathrm{Decisive} D_{\mathrm{eff}}$ ($\mathrm{Mpc}$)", lambda sim, instruments: sorted(sim.get_eff_dist(instrument) for instrument in instruments)[1], "deff_vs_mchirp"),
639  (r"Chirp Decisive Distance vs.\ Chirp Mass (With %s Operating)" % ", ".join(sorted(self.on_instruments)), r"$M_{\mathrm{chirp}}$ ($\mathrm{M}_{\odot}$)", lambda sim: sim.mchirp, r"$\mathrm{Decisive} D_{\mathrm{chirp}}$ ($\mathrm{Mpc}$)", lambda sim, instruments: sorted(sim.get_chirp_eff_dist(instrument) for instrument in instruments)[1], "chirpdist_vs_mchirp"),
640  (r"Decisive Distance vs.\ Total Mass (With %s Operating)" % ", ".join(sorted(self.on_instruments)), r"$M_{\mathrm{total}}$ ($\mathrm{M}_{\odot}$)", lambda sim: sim.mass1 + sim.mass2, r"$\mathrm{Decisive} D_{\mathrm{eff}}$ ($\mathrm{Mpc}$)", lambda sim, instruments: sorted(sim.get_eff_dist(instrument) for instrument in instruments)[1], "deff_vs_mtotal"),
641  (r"Decisive Distance vs.\ Effective Spin (With %s Operating)" % ", ".join(sorted(self.on_instruments)), r"$\chi$", lambda sim: (sim.spin1z*sim.mass1 + sim.spin2z*sim.mass2)/(sim.mass1 + sim.mass2), r"$\mathrm{Decisive} D_{\mathrm{eff}}$ ($\mathrm{Mpc}$)", lambda sim, instruments: sorted(sim.get_eff_dist(instrument) for instrument in instruments)[1], "deff_vs_chi"),
642  (r"Decisive Distance vs.\ Time (With %s Operating)" % ", ".join(sorted(self.on_instruments)), r"GPS Time (s)", lambda sim: sim.get_time_geocent(), r"$\mathrm{Decisive} D_{\mathrm{eff}}$ ($\mathrm{Mpc}$)", lambda sim, instruments: sorted(sim.get_eff_dist(instrument) for instrument in instruments)[1], "deff_vs_t")
643  )):
644  fig, axes = create_plot(x_label, y_label)
645  legend = []
646  for participating_instruments, sims in sorted(self.found_in.items(), key = (lambda x: lsctables.ifos_from_instrument_set(x[0]))):
647  if not cnt: f.write("||%s||%s||FOUND: %d||\n" % ("".join(sorted(self.on_instruments)), "".join(sorted(participating_instruments)), len(sims)))
648  legend.append("Found in %s" % ", ".join(sorted(participating_instruments)))
649  axes.semilogy([x_func(sim) for sim in sims], [y_func(sim, participating_instruments) for sim in sims], ".")
650  if missed:
651  if not cnt: f.write("||%s||%s||MISSED: %d||\n" % ("".join(sorted(self.on_instruments)), "---", len(missed)))
652  legend.append("Missed")
653  axes.semilogy([x_func(sim) for sim in missed], [y_func(sim, self.on_instruments) for sim in missed], "k.")
654  f.close()
655  if legend:
656  axes.legend(legend)
657  axes.set_title(title)
658  yield fig, filename_fragment, False
659 
660  def __init__(self, far_thresh):
661  self.far_thresh = far_thresh
662  self.plots = {}
663 
664  def add_contents(self, contents):
665  self.base = contents.base
666  if contents.sim_inspiral_table is None:
667  # no injections
668  return
669  for on_instruments in contents.on_instruments_combos:
670  if on_instruments not in self.plots:
671  self.plots[on_instruments] = MissedFoundPlots.MissedFound(on_instruments, self.far_thresh)
672  self.plots[on_instruments].add_contents(contents)
673 
674  def finish(self):
675  f = open(self.base + "injection_summary.txt", "w")
676  f.write("||<b>ON INSTRUMENTS</b>||<b> PARTICIPATING INSTRUMENTS</b>||<b>MISSED/FOUND</b||\n")
677  f.close()
678  for on_instruments, plot in self.plots.items():
679  for fig, filename_fragment, is_open_box in plot.finish():
680  yield fig, "%s_%s" % (filename_fragment, "".join(sorted(on_instruments))), is_open_box
681 
682 
683 #
684 # =============================================================================
685 #
686 # Parameter Accuracy
687 #
688 # =============================================================================
689 #
690 
691 
692 class ParameterAccuracyPlots(object):
693  def __init__(self):
694  self.sim_sngl_pairs = {}
695 
696  def add_contents(self, contents):
697  if contents.sim_inspiral_table is None:
698  # not an injections file
699  return
700  n_simcolumns = len(contents.sim_inspiral_table.columnnames)
701  for values in contents.connection.cursor().execute("""
702 SELECT
703  sim_inspiral.*,
704  sngl_inspiral.*
705 FROM
706  sim_inspiral
707  JOIN sim_coinc_map ON (
708  sim_coinc_map.simulation_id == sim_inspiral.simulation_id
709  )
710  JOIN coinc_event_map ON (
711  coinc_event_map.coinc_event_id == sim_coinc_map.coinc_event_id
712  )
713  JOIN sngl_inspiral ON (
714  coinc_event_map.table_name == 'sngl_inspiral'
715  AND coinc_event_map.event_id == sngl_inspiral.event_id
716  )
717  WHERE sngl_inspiral.snr > 8.0
718  """):
719  sim = contents.sim_inspiral_table.row_from_cols(values)
720  sngl = contents.sngl_inspiral_table.row_from_cols(values[n_simcolumns:])
721  del sim.process_id, sim.source, sim.simulation_id
722  del sngl.process_id, sngl.event_id
723  self.sim_sngl_pairs.setdefault((sim.waveform, sngl.ifo), []).append((sim, sngl))
724 
725  def finish(self):
726 
727  def hist(arr, axes):
728  start = scipy.stats.mstats.mquantiles(arr, 0.01)
729  end = scipy.stats.mstats.mquantiles(arr, 0.99)
730 
731  axes.hist(arr, numpy.linspace(start, end, 100))
732 
733  for (waveform, instrument), pairs in self.sim_sngl_pairs.items():
734  fig, axes = create_plot(r"Injected $M_{\mathrm{chirp}}$ ($\mathrm{M}_{\odot}$)", r"Recovered $M_{\mathrm{chirp}}$ - Injected $M_{\mathrm{chirp}}$ ($\mathrm{M}_{\odot}$)")
735  axes.set_title(r"Absolute $M_{\mathrm{chirp}}$ Accuracy in %s (%s Injections)" % (instrument, waveform))
736  axes.plot([sim.mchirp for sim, sngl in pairs], [sngl.mchirp - sim.mchirp for sim, sngl in pairs], "kx")
737  yield fig, "mchirp_acc_abs_%s_%s" % (waveform, instrument), False
738 
739  fig, axes = create_plot(r"Recovered $M_{\mathrm{chirp}}$ - Injected $M_{\mathrm{chirp}}$ ($\mathrm{M}_{\odot}$)", "Number")
740  axes.set_title(r"Absolute $M_{\mathrm{chirp}}$ Accuracy in %s (%s Injections)" % (instrument, waveform))
741  hist(numpy.array([sngl.mchirp - sim.mchirp for sim, sngl in pairs]), axes)
742  yield fig, "mchirp_acc_abs_hist_%s_%s" % (waveform, instrument), False
743 
744  fig, axes = create_plot(r"Injected $M_{\mathrm{chirp}}$ ($\mathrm{M}_{\odot}$)", r"(Recovered $M_{\mathrm{chirp}}$ - Injected $M_{\mathrm{chirp}}$) / Injected $M_{\mathrm{chirp}}$")
745  axes.set_title(r"Fractional $M_{\mathrm{chirp}}$ Accuracy in %s (%s Injections)" % (instrument, waveform))
746  axes.plot([sim.mchirp for sim, sngl in pairs], [(sngl.mchirp - sim.mchirp) / sim.mchirp for sim, sngl in pairs], "kx")
747  yield fig, "mchirp_acc_frac_%s_%s" % (waveform, instrument), False
748 
749  fig, axes = create_plot(r"(Recovered $M_{\mathrm{chirp}}$ - Injected $M_{\mathrm{chirp}}$ ($\mathrm{M}_{\odot}$)) / Injected $M_{\mathrm{chirp}}$ ($\mathrm{M}_{\odot}$)", "Number")
750  axes.set_title(r"Fractional $M_{\mathrm{chirp}}$ Accuracy in %s (%s Injections)" % (instrument, waveform))
751  hist(numpy.array([(sngl.mchirp - sim.mchirp) / sim.mchirp for sim, sngl in pairs]), axes)
752  yield fig, "mchirp_acc_frac_hist_%s_%s" % (waveform, instrument), False
753 
754  fig, axes = create_plot(r"Injected $\eta$", r"Recovered $\eta$ - Injected $\eta$")
755  axes.set_title(r"Absolute $\eta$ Accuracy in %s (%s Injections)" % (instrument, waveform))
756  axes.plot([sim.eta for sim, sngl in pairs], [sngl.eta - sim.eta for sim, sngl in pairs], "kx")
757  yield fig, "eta_acc_abs_%s_%s" % (waveform, instrument), False
758 
759  fig, axes = create_plot(r"Recovered $\eta$ - Injected $\eta$", "Number")
760  axes.set_title(r"Absolute $\eta$ Accuracy in %s (%s Injections)" % (instrument, waveform))
761  hist(numpy.array([sngl.eta - sim.eta for sim, sngl in pairs]), axes)
762  yield fig, "eta_acc_abs_hist_%s_%s" % (waveform, instrument), False
763 
764  fig, axes = create_plot(r"Injected $\eta$", r"(Recovered $\eta$ - Injected $\eta$) / Injected $\eta$")
765  axes.set_title(r"Fractional $\eta$ Accuracy in %s (%s Injections)" % (instrument, waveform))
766  axes.plot([sim.eta for sim, sngl in pairs], [(sngl.eta - sim.eta) / sim.eta for sim, sngl in pairs], "kx")
767  yield fig, "eta_acc_frac_%s_%s" % (waveform, instrument), False
768 
769  fig, axes = create_plot(r"(Recovered $\eta$ - Injected $\eta$) / Injected $\eta$", "Number")
770  axes.set_title(r"Fractional $\eta$ Accuracy in %s (%s Injections)" % (instrument, waveform))
771  hist(numpy.array([(sngl.eta - sim.eta) / sim.eta for sim, sngl in pairs]), axes)
772  yield fig, "eta_acc_frac_hist_%s_%s" % (waveform, instrument), False
773 
774  fig, axes = create_plot(r"Injection End Time (GPS s)", r"Recovered End Time - Injection End Time (s)")
775  axes.set_title(r"End Time Accuracy in %s (%s Injections)" % (instrument, waveform))
776  axes.plot([sim_end_time(sim, instrument) for sim, sngl in pairs], [sngl.get_end() - sim_end_time(sim, instrument) for sim, sngl in pairs], "kx")
777  yield fig, "t_acc_%s_%s" % (waveform, instrument), False
778 
779  fig, axes = create_plot(r"Recovered End Time - Injection End Time (s)", "Number")
780  axes.set_title(r"End Time Accuracy in %s (%s Injections)" % (instrument, waveform))
781  hist(numpy.array([float(sngl.get_end()) - float(sim_end_time(sim, instrument)) for sim, sngl in pairs]), axes)
782  yield fig, "t_acc_hist_%s_%s" % (waveform, instrument), False
783 
784  fig, axes = create_plot(r"Injection $D_{\mathrm{eff}}$ ($\mathrm{Mpc}$)", r"(Recovered $D_{\mathrm{eff}}$ - Injection $D_{\mathrm{eff}}$) / Injection $D_{\mathrm{eff}}$")
785  axes.set_title(r"Fractional Effective Distance Accuracy in %s (%s Injections)" % (instrument, waveform))
786  axes.semilogx([sim.get_eff_dist(instrument) for sim, sngl in pairs], [(sngl.eff_distance - sim.get_eff_dist(instrument)) / sim.get_eff_dist(instrument) for sim, sngl in pairs], "kx")
787  yield fig, "deff_acc_frac_%s_%s" % (waveform, instrument), False
788 
789  fig, axes = create_plot(r"(Recovered $D_{\mathrm{eff}}$ - Injection $D_{\mathrm{eff}}$) / Injection $D_{\mathrm{eff}}$", "Number")
790  axes.set_title(r"Fractional Effective Distance Accuracy in %s (%s Injections)" % (instrument, waveform))
791  hist(numpy.array([(sngl.eff_distance - sim.get_eff_dist(instrument)) / sim.get_eff_dist(instrument) for sim, sngl in pairs]), axes)
792  yield fig, "deff_acc_frac_hist_%s_%s" % (waveform, instrument), False
793 
794  fig, axes = create_plot(r"(Recovered $1/D_{\mathrm{eff}}$ - Injection $1/D_{\mathrm{eff}}$) / Injection $1/D_{\mathrm{eff}}$", "Number")
795  axes.set_title(r"Fractional Effective Amplitude Accuracy in %s (%s Injections)" % (instrument, waveform))
796  hist(numpy.array([(1. / sngl.eff_distance - 1. / sim.get_eff_dist(instrument)) / (1. / sim.get_eff_dist(instrument)) for sim, sngl in pairs]), axes)
797  yield fig, "deff_acc_frac_inv_hist_%s_%s" % (waveform, instrument), False
798 
799  fig, axes = create_plot(r"Injected $\chi$", r"Recovered $\chi$")
800  axes.set_title(r"Effective Spin Accuracy in %s (%s Injections)" % (instrument, waveform))
801  axes.plot([sim.chi for sim, sngl in pairs], [sngl.chi for sim, sngl in pairs], "kx")
802  yield fig, "chi_acc_%s_%s" % (waveform, instrument), False
803 
804 
805 #
806 # =============================================================================
807 #
808 # Background vs. Injections --- Single Instrument
809 #
810 # =============================================================================
811 #
812 
813 
814 class BackgroundVsInjectionPlots(object):
815  class Points(object):
816  def __init__(self):
817  self.snr = []
818  self.chi2 = []
819  self.bankveto = []
820  self.spin = []
821 
822  def __init__(self):
823  self.injections = {}
824  self.background = {}
825  self.zerolag = {}
826 
827  def add_contents(self, contents):
828  if contents.sim_inspiral_table is None:
829  # non-injections file
830  for instrument, snr, chi2, bankveto, is_background in contents.connection.cursor().execute("""
831 SELECT
832  sngl_inspiral.ifo,
833  sngl_inspiral.snr,
834  sngl_inspiral.chisq,
835  sngl_inspiral.bank_chisq / bank_chisq_dof,
836  EXISTS (
837  SELECT
838  *
839  FROM
840  time_slide
841  WHERE
842  time_slide.time_slide_id == coinc_event.time_slide_id
843  AND time_slide.offset != 0
844  )
845 FROM
846  coinc_event
847  JOIN coinc_event_map ON (
848  coinc_event_map.coinc_event_id == coinc_event.coinc_event_id
849  )
850  JOIN sngl_inspiral ON (
851  coinc_event_map.table_name == 'sngl_inspiral'
852  AND coinc_event_map.event_id == sngl_inspiral.event_id
853  )
854 WHERE
855  coinc_event.coinc_def_id == ?
856  """, (contents.ii_definer_id,)):
857  if is_background:
858  if instrument not in self.background:
859  self.background[instrument] = BackgroundVsInjectionPlots.Points()
860  self.background[instrument].snr.append(snr)
861  self.background[instrument].chi2.append(chi2)
862  self.background[instrument].bankveto.append(bankveto)
863  else:
864  if instrument not in self.zerolag:
865  self.zerolag[instrument] = BackgroundVsInjectionPlots.Points()
866  self.zerolag[instrument].snr.append(snr)
867  self.zerolag[instrument].chi2.append(chi2)
868  self.zerolag[instrument].bankveto.append(bankveto)
869  else:
870  # injections file
871  for instrument, snr, chi2, bankveto, end_time, spin in contents.connection.cursor().execute("""
872 SELECT
873  sngl_inspiral.ifo,
874  sngl_inspiral.snr,
875  sngl_inspiral.chisq,
876  sngl_inspiral.bank_chisq / bank_chisq_dof,
877  sngl_inspiral.end_time + sngl_inspiral.end_time_ns * 1e-9,
878  sim.spin1x * sim.spin1x + sim.spin1y * sim.spin1y + sim.spin1z * sim.spin1z + sim.spin2x * sim.spin2x + sim.spin2y * sim.spin2y + sim.spin2z * sim.spin2z
879 FROM
880  sim_coinc_map
881  JOIN coinc_event_map ON (
882  coinc_event_map.coinc_event_id == sim_coinc_map.coinc_event_id
883  )
884  JOIN sngl_inspiral ON (
885  coinc_event_map.table_name == 'sngl_inspiral'
886  AND coinc_event_map.event_id == sngl_inspiral.event_id
887  )
888  JOIN sim_inspiral AS sim ON sim.simulation_id == sim_coinc_map.simulation_id
889  """):
890  if end_time in contents.seglists[instrument]:
891  if instrument not in self.injections:
892  self.injections[instrument] = BackgroundVsInjectionPlots.Points()
893  self.injections[instrument].snr.append(snr)
894  self.injections[instrument].chi2.append(chi2)
895  self.injections[instrument].bankveto.append(bankveto)
896  self.injections[instrument].spin.append((spin / 2.)**.5)
897 
898  def finish(self):
899  for instrument in set(self.injections) | set(self.background) | set(self.zerolag):
900  self.injections.setdefault(instrument, BackgroundVsInjectionPlots.Points())
901  self.background.setdefault(instrument, BackgroundVsInjectionPlots.Points())
902  self.zerolag.setdefault(instrument, BackgroundVsInjectionPlots.Points())
903  for instrument in self.background:
904  fig, axes = create_plot(r"$\rho$", r"$\chi^{2}$")
905  axes.set_title(r"$\chi^{2}$ vs.\ $\rho$ in %s (Closed Box)" % instrument)
906 
907  for (spinstart, spinstop) in [(0,0.1), (0.1,0.2), (0.2,0.3), (0.3,0.4), (0.4,0.5), (0.5, 0.6), (0.6, 1.0)][::-1]:
908  injsnr = numpy.array([self.injections[instrument].snr[n] for n in range(len(self.injections[instrument].snr)) if self.injections[instrument].spin[n] >= spinstart and self.injections[instrument].spin[n] < spinstop])
909  injchi2 = numpy.array([self.injections[instrument].chi2[n] for n in range(len(self.injections[instrument].snr)) if self.injections[instrument].spin[n] >= spinstart and self.injections[instrument].spin[n] < spinstop])
910  axes.loglog(injsnr, injchi2, '.', label = "Inj $|s|$=%.1f" % spinstart)
911 
912  axes.loglog(self.background[instrument].snr, self.background[instrument].chi2, "kx", label = "Background")
913  axes.legend(loc = "upper left")
914  yield fig, "chi2_vs_rho_%s" % instrument, False
915 
916  fig, axes = create_plot(r"$\rho$", r"$\chi^{2}$")
917  axes.set_title(r"$\chi^{2}$ vs.\ $\rho$ in %s" % instrument)
918  for (spinstart, spinstop) in [(0,0.1), (0.1,0.2), (0.2,0.3), (0.3,0.4), (0.4,0.5), (0.5, 0.6), (0.6, 1.0)][::-1]:
919  injsnr = numpy.array([self.injections[instrument].snr[n] for n in range(len(self.injections[instrument].snr)) if self.injections[instrument].spin[n] >= spinstart and self.injections[instrument].spin[n] < spinstop])
920  injchi2 = numpy.array([self.injections[instrument].chi2[n] for n in range(len(self.injections[instrument].snr)) if self.injections[instrument].spin[n] >= spinstart and self.injections[instrument].spin[n] < spinstop])
921  axes.loglog(injsnr, injchi2, '.', label = "Inj $|s|$=%.1f" % spinstart)
922  axes.loglog(self.background[instrument].snr, self.background[instrument].chi2, "kx", label = "Background")
923  axes.loglog(self.zerolag[instrument].snr, self.zerolag[instrument].chi2, "bx", label = "Zero-lag")
924  axes.legend(loc = "upper left")
925  yield fig, "chi2_vs_rho_%s" % instrument, True
926 
927 
928 #
929 # =============================================================================
930 #
931 # Background vs. Injections --- Multi Instrument
932 #
933 # =============================================================================
934 #
935 
936 
937 class BackgroundVsInjectionPlotsMulti(object):
938  class Points(object):
939  def __init__(self):
940  self.background_snreff = []
941  self.injections_snreff = []
942  self.zerolag_snreff = []
943  self.background_deff = []
944  self.injections_deff = []
945  self.zerolag_deff = []
946 
947  def __init__(self, snrfactor):
948  self.snrfactor = snrfactor
949  self.points = {}
950 
951  def add_contents(self, contents):
952  if contents.sim_inspiral_table is None:
953  # non-injections file
954  for values in contents.connection.cursor().execute("""
955 SELECT
956  sngl_inspiral_x.*,
957  sngl_inspiral_y.*,
958  EXISTS (
959  SELECT
960  *
961  FROM
962  time_slide
963  WHERE
964  time_slide.time_slide_id == coinc_event.time_slide_id
965  AND time_slide.offset != 0
966  )
967 FROM
968  coinc_event
969  JOIN coinc_event_map AS coinc_event_map_x ON (
970  coinc_event_map_x.coinc_event_id == coinc_event.coinc_event_id
971  )
972  JOIN sngl_inspiral AS sngl_inspiral_x ON (
973  coinc_event_map_x.table_name == 'sngl_inspiral'
974  AND coinc_event_map_x.event_id == sngl_inspiral_x.event_id
975  )
976  JOIN coinc_event_map AS coinc_event_map_y ON (
977  coinc_event_map_y.coinc_event_id == coinc_event.coinc_event_id
978  )
979  JOIN sngl_inspiral AS sngl_inspiral_y ON (
980  coinc_event_map_y.table_name == 'sngl_inspiral'
981  AND coinc_event_map_y.event_id == sngl_inspiral_y.event_id
982  )
983  JOIN coinc_inspiral ON (
984  coinc_inspiral.coinc_event_id == coinc_event.coinc_event_id
985  )
986 WHERE
987  coinc_event.coinc_def_id == ?
988  AND sngl_inspiral_x.ifo > sngl_inspiral_y.ifo
989  """, (contents.ii_definer_id,)):
990  x = contents.sngl_inspiral_table.row_from_cols(values)
991  y = contents.sngl_inspiral_table.row_from_cols(values[len(contents.sngl_inspiral_table.columnnames):])
992  is_background, = values[-1:]
993  instrument_pair = (x.ifo, y.ifo)
994  if instrument_pair not in self.points:
995  self.points[instrument_pair] = BackgroundVsInjectionPlotsMulti.Points()
996  if is_background:
997  self.points[instrument_pair].background_snreff.append((x.get_effective_snr(fac = self.snrfactor), y.get_effective_snr(fac = self.snrfactor)))
998  self.points[instrument_pair].background_deff.append((x.eff_distance, y.eff_distance))
999  else:
1000  self.points[instrument_pair].zerolag_snreff.append((x.get_effective_snr(fac = self.snrfactor), y.get_effective_snr(fac = self.snrfactor)))
1001  self.points[instrument_pair].zerolag_deff.append((x.eff_distance, y.eff_distance))
1002  else:
1003  # injections file
1004  for values in contents.connection.cursor().execute("""
1005 SELECT
1006  sngl_inspiral_x.*,
1007  sngl_inspiral_y.*
1008 FROM
1009  sim_coinc_map
1010  JOIN coinc_event_map AS coinc_event_map_x ON (
1011  coinc_event_map_x.coinc_event_id == sim_coinc_map.coinc_event_id
1012  )
1013  JOIN sngl_inspiral AS sngl_inspiral_x ON (
1014  coinc_event_map_x.table_name == 'sngl_inspiral'
1015  AND coinc_event_map_x.event_id == sngl_inspiral_x.event_id
1016  )
1017  JOIN coinc_event_map AS coinc_event_map_y ON (
1018  coinc_event_map_y.coinc_event_id == sim_coinc_map.coinc_event_id
1019  )
1020  JOIN sngl_inspiral AS sngl_inspiral_y ON (
1021  coinc_event_map_y.table_name == 'sngl_inspiral'
1022  AND coinc_event_map_y.event_id == sngl_inspiral_y.event_id
1023  )
1024 WHERE
1025  sngl_inspiral_x.ifo > sngl_inspiral_y.ifo
1026  """):
1027  x = contents.sngl_inspiral_table.row_from_cols(values)
1028  y = contents.sngl_inspiral_table.row_from_cols(values[len(contents.sngl_inspiral_table.columnnames):])
1029  instrument_pair = (x.ifo, y.ifo)
1030  if instrument_pair not in self.points:
1031  self.points[instrument_pair] = BackgroundVsInjectionPlotsMulti.Points()
1032  self.points[instrument_pair].injections_snreff.append((x.get_effective_snr(fac = self.snrfactor), y.get_effective_snr(fac = self.snrfactor)))
1033  self.points[instrument_pair].injections_deff.append((x.eff_distance, y.eff_distance))
1034 
1035  def finish(self):
1036  for (x_instrument, y_instrument), points in self.points.items():
1037  fig, axes = create_plot(r"$\rho_{\mathrm{eff}}$ in %s" % x_instrument, r"$\rho_{\mathrm{eff}}$ in %s" % y_instrument, aspect = 1.0)
1038  axes.set_title(r"Effective SNR in %s vs.\ %s (SNR Factor = %g) (Closed Box)" % (y_instrument, x_instrument, self.snrfactor))
1039  axes.loglog([x for x, y in points.injections_snreff], [y for x, y in points.injections_snreff], "rx")
1040  axes.loglog([x for x, y in points.background_snreff], [y for x, y in points.background_snreff], "kx")
1041  axes.legend(("Injections", "Background"), loc = "lower right")
1042  yield fig, "rho_%s_vs_%s" % (y_instrument, x_instrument), False
1043 
1044  fig, axes = create_plot(r"$\rho_{\mathrm{eff}}$ in %s" % x_instrument, r"$\rho_{\mathrm{eff}}$ in %s" % y_instrument, aspect = 1.0)
1045  axes.set_title(r"Effective SNR in %s vs.\ %s (SNR Factor = %g)" % (y_instrument, x_instrument, self.snrfactor))
1046  axes.loglog([x for x, y in points.injections_snreff], [y for x, y in points.injections_snreff], "rx")
1047  axes.loglog([x for x, y in points.background_snreff], [y for x, y in points.background_snreff], "kx")
1048  axes.loglog([x for x, y in points.zerolag_snreff], [y for x, y in points.zerolag_snreff], "bx")
1049  axes.legend(("Injections", "Background", "Zero-lag"), loc = "lower right")
1050  yield fig, "rho_%s_vs_%s" % (y_instrument, x_instrument), True
1051 
1052  fig, axes = create_plot(r"$D_{\mathrm{eff}}$ in %s" % x_instrument, r"$D_{\mathrm{eff}}$ in %s" % y_instrument, aspect = 1.0)
1053  axes.set_title(r"Effective Distance in %s vs.\ %s (Closed Box)" % (y_instrument, x_instrument))
1054  axes.loglog([x for x, y in points.injections_deff], [y for x, y in points.injections_deff], "rx")
1055  axes.loglog([x for x, y in points.background_deff], [y for x, y in points.background_deff], "kx")
1056  axes.legend(("Injections", "Background"), loc = "lower right")
1057  yield fig, "deff_%s_vs_%s" % (y_instrument, x_instrument), False
1058 
1059  fig, axes = create_plot(r"$D_{\mathrm{eff}}$ in %s" % x_instrument, r"$D_{\mathrm{eff}}$ in %s" % y_instrument, aspect = 1.0)
1060  axes.set_title(r"Effective Distance in %s vs.\ %s" % (y_instrument, x_instrument))
1061  axes.loglog([x for x, y in points.injections_deff], [y for x, y in points.injections_deff], "rx")
1062  axes.loglog([x for x, y in points.background_deff], [y for x, y in points.background_deff], "kx")
1063  axes.loglog([x for x, y in points.zerolag_deff], [y for x, y in points.zerolag_deff], "bx")
1064  axes.legend(("Injections", "Background", "Zero-lag"), loc = "lower right")
1065  yield fig, "deff_%s_vs_%s" % (y_instrument, x_instrument), True
1066 
1067 
1068 #
1069 # =============================================================================
1070 #
1071 # Rate vs. Threshold Plots
1072 #
1073 # =============================================================================
1074 #
1075 
1076 
1077 def sigma_region(mean, nsigma):
1078  return numpy.concatenate((mean - nsigma * numpy.sqrt(mean), (mean + nsigma * numpy.sqrt(mean))[::-1]))
1079 
1080 
1081 def create_farplot(axes, zerolag_stats, expected_count_x, expected_count_y, is_open_box, xlim = (None, None), max_events = 1000):
1082  #
1083  # isolate relevent data
1084  #
1085 
1086  zerolag_stats = zerolag_stats[:max_events]
1087 
1088  #
1089  # background. uncomment the two lines to make the background
1090  # stair-step-style like the observed counts
1091  #
1092 
1093  #expected_count_x = expected_count_x.repeat(2)[1:]
1094  #expected_count_y = expected_count_y.repeat(2)[:-1]
1095  line1, = axes.plot(expected_count_x, expected_count_y, 'k--', linewidth = 1)
1096 
1097  #
1098  # error bands
1099  #
1100 
1101  expected_count_x = numpy.concatenate((expected_count_x, expected_count_x[::-1]))
1102  line2, = axes.fill(expected_count_x, sigma_region(expected_count_y, 3.0).clip(0.001, max_events), alpha = 0.25, facecolor = [0.75, 0.75, 0.75])
1103  line3, = axes.fill(expected_count_x, sigma_region(expected_count_y, 2.0).clip(0.001, max_events), alpha = 0.25, facecolor = [0.5, 0.5, 0.5])
1104  line4, = axes.fill(expected_count_x, sigma_region(expected_count_y, 1.0).clip(0.001, max_events), alpha = 0.25, facecolor = [0.25, 0.25, 0.25])
1105 
1106  #
1107  # zero-lag
1108  #
1109 
1110  N = numpy.arange(1., len(zerolag_stats) + 1., dtype = "double")
1111  line5, = axes.plot(zerolag_stats.repeat(2)[1:], N.repeat(2)[:-1], 'k', linewidth = 2)
1112 
1113  #
1114  # legend
1115  #
1116 
1117  if is_open_box:
1118  axes.legend((line5, line1, line4, line3, line2), ("Zero-lag", r"$\langle N \rangle$", r"$\pm\sqrt{\langle N \rangle}$", r"$\pm 2\sqrt{\langle N \rangle}$", r"$\pm 3\sqrt{\langle N \rangle}$"), loc = "upper right")
1119  else:
1120  axes.legend((line5, line1, line4, line3, line2), (r"$\pi$ shift", r"$\langle N \rangle$", r"$\pm\sqrt{\langle N \rangle}$", r"$\pm 2\sqrt{\langle N \rangle}$", r"$\pm 3\sqrt{\langle N \rangle}$"), loc = "upper right")
1121 
1122  #
1123  # adjust bounds of plot
1124  #
1125 
1126  xlim = max(zerolag_stats.min(), xlim[0]), (2.**math.ceil(math.log(zerolag_stats.max(), 2.)) if xlim[1] is None else xlim[1])
1127  axes.set_xlim(xlim)
1128  axes.set_ylim((0.001, 10.**math.ceil(math.log10(max_events))))
1129 
1130 
1131 class RateVsThreshold(object):
1132  def __init__(self):
1133  self.background_ln_likelihood_ratio = []
1134  self.zerolag_ln_likelihood_ratio = []
1135  self.background_far = []
1136  self.zerolag_far = []
1137  self.background_fap = []
1138  self.zerolag_fap = []
1139  self.background_snr = []
1140  self.zerolag_snr = []
1141  self.farsegs = segments.segmentlistdict()
1142 
1143  def add_contents(self, contents):
1144  if contents.sim_inspiral_table is not None:
1145  # skip injection documents
1146  return
1147 
1148  self.farsegs |= contents.farsegs
1149 
1150  for ln_likelihood_ratio, far, fap, snr, is_background in connection.cursor().execute("""
1151 SELECT
1152  coinc_event.likelihood,
1153  coinc_inspiral.combined_far,
1154  coinc_inspiral.false_alarm_rate,
1155  coinc_inspiral.snr,
1156  EXISTS (
1157  SELECT
1158  *
1159  FROM
1160  time_slide
1161  WHERE
1162  time_slide.time_slide_id == coinc_event.time_slide_id
1163  AND time_slide.offset != 0
1164  )
1165 FROM
1166  coinc_inspiral
1167  JOIN coinc_event ON (
1168  coinc_event.coinc_event_id == coinc_inspiral.coinc_event_id
1169  )
1170 WHERE
1171  coinc_event.likelihood >= 0.
1172  """):
1173  if is_background:
1174  self.background_ln_likelihood_ratio.append(ln_likelihood_ratio)
1175  self.background_far.append(far)
1176  self.background_fap.append(fap)
1177  self.background_snr.append(snr)
1178  else:
1179  self.zerolag_ln_likelihood_ratio.append(ln_likelihood_ratio)
1180  self.zerolag_far.append(far)
1181  self.zerolag_fap.append(fap)
1182  self.zerolag_snr.append(snr)
1183 
1184  def finish(self):
1185  livetime = far.get_live_time(self.farsegs)
1186 
1187  fig, axes = create_plot(x_label = r"SNR", y_label = r"$\ln \Lambda$")
1188  axes.loglog(self.background_snr, self.background_ln_likelihood_ratio, "kx", label = "Background")
1189  axes.legend(loc = "upper left")
1190  axes.set_title(r"$\ln \Lambda$ vs.\ SNR Scatter Plot (Closed Box)")
1191  yield fig, "lr_vs_snr", False
1192  fig, axes = create_plot(x_label = r"SNR", y_label = r"$\ln \Lambda$")
1193  axes.loglog(self.background_snr, self.background_ln_likelihood_ratio, "kx", label = "Background")
1194  axes.loglog(self.zerolag_snr, self.zerolag_ln_likelihood_ratio, "bx", label = "Zero-lag")
1195  axes.legend(loc = "upper left")
1196  axes.set_title(r"$\ln \Lambda$ vs.\ SNR Scatter Plot")
1197  yield fig, "lr_vs_snr", True
1198 
1199  for ln_likelihood_ratio, fars, is_open_box in [(self.zerolag_ln_likelihood_ratio, self.zerolag_far, True), (self.background_ln_likelihood_ratio, self.background_far, False)]:
1200  if fars:
1201  fig, axes = create_plot(None, r"Number of Events")
1202  axes.loglog()
1203  # fars in ascending order --> ifars in descending order
1204  zerolag_stats = 1. / numpy.array(sorted(fars))
1205  expected_count_y = numpy.logspace(-7, numpy.log10(len(zerolag_stats)), 1000)
1206  expected_count_x = livetime / expected_count_y
1207  create_farplot(axes, zerolag_stats, expected_count_x, expected_count_y, is_open_box, xlim = (None, 2000. * livetime))
1208  if is_open_box:
1209  axes.set_title(r"Event Count vs.\ Inverse False-Alarm Rate Threshold")
1210  else:
1211  axes.set_title(r"Event Count vs.\ Inverse False-Alarm Rate Threshold (Closed Box)")
1212  axes.set_xlabel(r"Inverse False-Alarm Rate (s)")
1213  yield fig, "count_vs_ifar", is_open_box
1214 
1215  if ln_likelihood_ratio:
1216  fig, axes = create_plot(None, r"Number of Events")
1217  axes.semilogy()
1218 
1219  zerolag_stats = numpy.array(sorted(ln_likelihood_ratio, reverse = True))
1220 
1221  # we want to plot FAR(ln L) * livetime vs.
1222  # ln L, but we don't have access to the
1223  # ranking statistic data file where that
1224  # function is encoded. instead, we rely on
1225  # the FARs stored in each coinc, together
1226  # with the ln L assigned to each coinc, to
1227  # provide us with a collection of samples
1228  # of that function. to get more points, we
1229  # combine data from the zero-lag and
1230  # background coincs. in the future,
1231  # perhaps this program could be provided
1232  # with the marginalized ranking statistic
1233  # PDF data file
1234 
1235  expected_count_x = self.zerolag_ln_likelihood_ratio + self.background_ln_likelihood_ratio
1236  order = range(len(expected_count_x))
1237  order.sort(key = lambda i: expected_count_x[i], reverse = True)
1238  expected_count_x = numpy.array(expected_count_x)[order]
1239  expected_count_y = numpy.array(self.zerolag_far + self.background_far)[order] * livetime
1240 
1241  create_farplot(axes, zerolag_stats, expected_count_x, expected_count_y, is_open_box, xlim = (None, 23.), max_events = 10000)
1242  if is_open_box:
1243  axes.set_title(r"Event Count vs.\ Ranking Statistic Threshold")
1244  else:
1245  axes.set_title(r"Event Count vs.\ Ranking Statistic Threshold (Closed Box)")
1246  axes.set_xlabel(r"$\ln \Lambda$")
1247  yield fig, "count_vs_lr", is_open_box
1248 
1249 
1250 #
1251 # =============================================================================
1252 #
1253 # Main
1254 #
1255 # =============================================================================
1256 #
1257 
1258 
1259 #
1260 # Parse command line
1261 #
1262 
1263 
1264 options, filenames = parse_command_line()
1265 
1266 
1267 #
1268 # Initialize plots
1269 #
1270 
1271 
1272 # how many there could be, so we know how many digits for the filenames
1273 max_plot_groups = None
1274 
1275 def new_plots(plots = None, far_thresh = None):
1276  global max_plot_groups
1277  l = (
1278  SummaryTable(),
1279  MissedFoundPlots(far_thresh = far_thresh),
1280  ParameterAccuracyPlots(),
1281  BackgroundVsInjectionPlots(),
1282  BackgroundVsInjectionPlotsMulti(snrfactor = 50.0),
1283  RateVsThreshold(),
1284  InjectionParameterDistributionPlots(),
1285  )
1286  max_plot_groups = len(l)
1287  if plots is None:
1288  plots = range(len(l))
1289  return [l[i] for i in plots]
1290 
1291 plots = new_plots(options.plot_group, options.far_threshold)
1292 if options.plot_group is None:
1293  options.plot_group = range(len(plots))
1294 
1295 
1296 #
1297 # Process files
1298 #
1299 
1300 
1301 wiki = open(os.path.join(options.output_dir, "%s_%s" % (options.user_tag, "plotsummary.txt")),"w")
1302 
1303 for n, filename in enumerate(filenames):
1304  if options.verbose:
1305  print >>sys.stderr, "%d/%d: %s" % (n + 1, len(filenames), filename)
1306  wiki.write("=== %d/%d: %s ===\n\n" % (n + 1, len(filenames), filename))
1307  working_filename = dbtables.get_connection_filename(filename, tmp_path = options.tmp_space, verbose = options.verbose)
1308  connection = sqlite3.connect(working_filename)
1309  contents = CoincDatabase(connection, options.segments_name, veto_segments_name = options.vetoes_name, verbose = options.verbose, wiki = wiki, base = os.path.join(options.output_dir, options.user_tag))
1310  if contents.sim_inspiral_table is not None:
1311  create_sim_coinc_view(connection)
1312  for n, plot in zip(options.plot_group, plots):
1313  if options.verbose:
1314  print >>sys.stderr, "adding to plot group %d ..." % n
1315  plot.add_contents(contents)
1316  connection.close()
1317  dbtables.discard_connection_filename(filename, working_filename, verbose = options.verbose)
1318 
1319 
1320 #
1321 # Finish and write plots, deleting them as we go to save memory
1322 #
1323 
1324 
1325 n = 0
1326 filename_template = inspiral_pipe.T050017_filename("H1L1V1", "GSTLAL_INSPIRAL_PLOTSUMMARY_%s_%02d_%s_%s", contents.seglists.extent_all()[0], contents.seglists.extent_all()[1], "%s", path = options.output_dir)
1327 while len(plots):
1328  for fig, filename_fragment, is_open_box in plots.pop(0).finish():
1329  for format in options.format:
1330  if filename_fragment and fig:
1331  filename = filename_template % (options.user_tag, options.plot_group[n], filename_fragment, ("openbox" if is_open_box else "closedbox"), format)
1332  if options.verbose:
1333  print >>sys.stderr, "writing %s ..." % filename
1334  fig.savefig(filename)
1335  n += 1