3 # Copyright (C) 2009-2013 Kipp Cannon, Chad Hanna, Drew Keppel
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.
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.
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.
20 # A program to produce a variety of plots from a gstlal inspiral analysis, e.g. IFAR plots, missed found, etc.
23 # =============================================================================
27 # =============================================================================
33 matplotlib.rcParams.update({
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,
45 from matplotlib
import figure
46 from matplotlib.backends.backend_agg
import FigureCanvasAgg as FigureCanvas
49 from optparse
import OptionParser
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
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
68 class SimInspiral(lsctables.SimInspiral):
71 return self.mass1 + self.mass2
75 return (self.mass1 * self.spin1z + self.mass2 * self.spin2z) / self.mtotal
78 class SnglInspiral(lsctables.SnglInspiral):
81 return self.mass1 + self.mass2
85 return self.mass1 * self.mass2 / self.mtotal**2.
89 return self.mtotal * self.eta**0.6
93 return (self.mass1 * self.spin1z + self.mass2 * self.spin2z) / self.mtotal
95 def get_effective_snr(self, fac):
96 return self.snr / (self.chisq / self.chisq_dof)**.5
98 lsctables.LIGOTimeGPS = LIGOTimeGPS
99 lsctables.SimInspiralTable.RowType = SimInspiral
100 lsctables.SnglInspiralTable.RowType = SnglInspiral
103 __author__ =
"Kipp Cannon <kipp.cannon@ligo.org>, Chad Hanna <channa@ligo.caltech.edu>"
109 # =============================================================================
113 # =============================================================================
117 def parse_command_line():
118 parser = OptionParser(
119 version =
"Name: %%prog\n%s" % git_version.verbose_msg
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...)
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()
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"]
148 if options.input_cache:
149 filenames.extend(c.path for c in map(lal.CacheEntry, open(options.input_cache)))
151 return options, filenames
155 # =============================================================================
159 # =============================================================================
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"):
166 Compute and record some summary information about the
171 self.connection = connection
172 xmldoc = dbtables.get_xml(connection)
174 cursor = connection.cursor()
178 self.sngl_inspiral_table = lsctables.SnglInspiralTable.get_table(xmldoc)
180 self.sngl_inspiral_table = None
182 self.sim_inspiral_table = lsctables.SimInspiralTable.get_table(xmldoc)
184 self.sim_inspiral_table = None
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)
190 self.coinc_def_table = None
191 self.coinc_table = None
192 self.time_slide_table = None
194 self.coinc_inspiral_table = lsctables.CoincInspiralTable.get_table(xmldoc)
196 self.coinc_inspiral_table = None
198 # determine a few coinc_definer IDs
199 # FIXME: don
't hard-code the numbers
200 if self.coinc_def_table is not None:
202 self.ii_definer_id = self.coinc_def_table.get_coinc_def_id("inspiral", 0, create_new = False)
204 self.ii_definer_id = None
206 self.si_definer_id = self.coinc_def_table.get_coinc_def_id("inspiral", 1, create_new = False)
208 self.si_definer_id = None
210 self.sc_definer_id = self.coinc_def_table.get_coinc_def_id("inspiral", 2, create_new = False)
212 self.sc_definer_id = None
214 self.ii_definer_id = None
215 self.si_definer_id = None
216 self.sc_definer_id = None
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")]
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()
228 self.veto_segments = segments.segmentlistdict()
229 self.seglists -= self.veto_segments
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)
236 print >>sys.stderr, "calculating background livetimes: ",
237 self.offset_vectors = db_thinca_rings.get_background_offset_vectors(connection)
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)
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)
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) )
282 # =============================================================================
286 # =============================================================================
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})
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")):
302 return roman(i, arabics[1:], romans[1:])
303 return romans[0] + roman(i - arabics[0], arabics, romans)
307 # width is in mm, default aspect ratio is the golden ratio
311 def create_plot(x_label = None, y_label = None, width = 165.0, aspect = None):
313 aspect = (1 + math.sqrt(5)) / 2
314 fig = figure.Figure()
316 fig.set_size_inches(width / 25.4, width / 25.4 / aspect)
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)
326 def create_sim_coinc_view(connection):
328 Construct a sim_inspiral --> best matching coinc_event mapping.
329 Only injections that match at least one coinc get an entry in this
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.
342 connection.cursor().execute("""
343 CREATE TEMPORARY TABLE
347 sim_inspiral.simulation_id AS simulation_id,
350 coinc_event.coinc_event_id
353 JOIN coinc_event_map AS b ON (
354 b.coinc_event_id == a.coinc_event_id
356 JOIN coinc_event ON (
357 b.table_name == 'coinc_event
'
358 AND b.event_id == coinc_event.coinc_event_id
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)
365 coinc_event.likelihood DESC
371 coinc_event_id IS NOT NULL
376 # =============================================================================
380 # =============================================================================
384 class SummaryTable(object):
387 self.bgcandidates = []
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
396 self.candidates += contents.connection.cursor().execute("""
398 coinc_inspiral.combined_far,
399 coinc_inspiral.false_alarm_rate,
400 coinc_event.likelihood,
402 coinc_inspiral.end_time + coinc_inspiral.end_time_ns * 1e-9,
404 coinc_inspiral.mchirp,
406 coinc_event.instruments,
408 group_concat(sngl_inspiral.ifo || ":" || sngl_inspiral.snr || ":" || sngl_inspiral.chisq || ":" || sngl_inspiral.mass1 || ":" || sngl_inspiral.mass2, " ")
411 JOIN coinc_event_map ON (
412 sngl_inspiral.event_id == coinc_event_map.event_id AND coinc_event_map.table_name == "sngl_inspiral"
415 coinc_event_map.coinc_event_id == coinc_inspiral.coinc_event_id
419 JOIN coinc_event ON (
420 coinc_event.coinc_event_id == coinc_inspiral.coinc_event_id
429 time_slide.time_slide_id == coinc_event.time_slide_id AND time_slide.offset != 0
436 self.bgcandidates += contents.connection.cursor().execute("""
438 coinc_inspiral.combined_far,
439 coinc_inspiral.false_alarm_rate,
440 coinc_event.likelihood,
442 coinc_inspiral.end_time + coinc_inspiral.end_time_ns * 1e-9,
444 coinc_inspiral.mchirp,
446 coinc_event.instruments,
448 group_concat(sngl_inspiral.ifo || ":" || sngl_inspiral.snr || ":" || sngl_inspiral.chisq || ":" || sngl_inspiral.mass1 || ":" || sngl_inspiral.mass2, " ")
451 JOIN coinc_event_map ON (
452 sngl_inspiral.event_id == coinc_event_map.event_id AND coinc_event_map.table_name == "sngl_inspiral"
455 coinc_event_map.coinc_event_id == coinc_inspiral.coinc_event_id
459 JOIN coinc_event ON (
460 coinc_event.coinc_event_id == coinc_inspiral.coinc_event_id
469 time_slide.time_slide_id == coinc_event.time_slide_id AND time_slide.offset != 0
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;
481 key = frozenset(lsctables.instrument_set_from_ifos(instruments))
482 self.num_trigs.setdefault(key,0)
483 self.num_trigs[key] += num
485 contents.connection.cursor().execute("DROP TABLE distinct_ifos")
487 for on_instruments in set(contents.background_livetime) | set(contents.zerolag_livetime):
488 self.livetime.setdefault(on_instruments, 0.0)
490 for on_instruments, livetime in contents.zerolag_livetime.items():
491 self.livetime[on_instruments] += livetime
493 def write_wiki_string(self, l, f, lt):
494 f.write("|| Rank || FAR (Hz) || FAP || ln Λ || Combined SNR || GPS End Time || <i>M</i><sub>total</sub> / M<sub>⊙</sub> || <i>M</i><sub>chirp</sub> / M<sub>⊙</sub> || Participating Instruments || On Instruments ||\n")
495 f.write("|| || || || || || Instrument || SNR || χ<sup>2</sup>/DOF || <i>m</i><sub>1</sub> / M<sub>⊙</sub> || <i>m</i><sub>2</sub> / M<sub>⊙</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) )
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)
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)),))
516 num = self.num_trigs[inst]
519 f.write("%d||\n" % (num,))
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))
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)
533 yield None, None, None
536 # =============================================================================
538 # Injection Parameter Distributions
540 # =============================================================================
544 class InjectionParameterDistributionPlots(object):
548 def add_contents(self, contents):
549 if contents.sim_inspiral_table is None:
552 for values in contents.connection.cursor().execute("""
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)
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,")
576 axes.plot(col1,col2, "k.")
577 minx, maxx = axes.get_xlim()
578 miny, maxy = axes.get_ylim()
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
586 # =============================================================================
590 # =============================================================================
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
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("""
612 JOIN coinc_inspiral ON (
613 coinc_inspiral.coinc_event_id == sim_coinc_map.coinc_event_id
616 sim_coinc_map.simulation_id == sim_inspiral.simulation_id
617 AND coinc_inspiral.combined_far < ?
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)
629 self.found_in[participating_instruments].append(sim)
631 self.found_in[participating_instruments] = [sim]
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")
644 fig, axes = create_plot(x_label, y_label)
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], ".")
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.")
657 axes.set_title(title)
658 yield fig, filename_fragment, False
660 def __init__(self, far_thresh):
661 self.far_thresh = far_thresh
664 def add_contents(self, contents):
665 self.base = contents.base
666 if contents.sim_inspiral_table is None:
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)
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")
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
684 # =============================================================================
688 # =============================================================================
692 class ParameterAccuracyPlots(object):
694 self.sim_sngl_pairs = {}
696 def add_contents(self, contents):
697 if contents.sim_inspiral_table is None:
698 # not an injections file
700 n_simcolumns = len(contents.sim_inspiral_table.columnnames)
701 for values in contents.connection.cursor().execute("""
707 JOIN sim_coinc_map ON (
708 sim_coinc_map.simulation_id == sim_inspiral.simulation_id
710 JOIN coinc_event_map ON (
711 coinc_event_map.coinc_event_id == sim_coinc_map.coinc_event_id
713 JOIN sngl_inspiral ON (
714 coinc_event_map.table_name == 'sngl_inspiral
'
715 AND coinc_event_map.event_id == sngl_inspiral.event_id
717 WHERE sngl_inspiral.snr > 8.0
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))
728 start = scipy.stats.mstats.mquantiles(arr, 0.01)
729 end = scipy.stats.mstats.mquantiles(arr, 0.99)
731 axes.hist(arr, numpy.linspace(start, end, 100))
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
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
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
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
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
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
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
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
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
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
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
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
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
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
806 # =============================================================================
808 # Background vs. Injections --- Single Instrument
810 # =============================================================================
814 class BackgroundVsInjectionPlots(object):
815 class Points(object):
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("""
835 sngl_inspiral.bank_chisq / bank_chisq_dof,
842 time_slide.time_slide_id == coinc_event.time_slide_id
843 AND time_slide.offset != 0
847 JOIN coinc_event_map ON (
848 coinc_event_map.coinc_event_id == coinc_event.coinc_event_id
850 JOIN sngl_inspiral ON (
851 coinc_event_map.table_name == 'sngl_inspiral
'
852 AND coinc_event_map.event_id == sngl_inspiral.event_id
855 coinc_event.coinc_def_id == ?
856 """, (contents.ii_definer_id,)):
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)
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)
871 for instrument, snr, chi2, bankveto, end_time, spin in contents.connection.cursor().execute("""
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
881 JOIN coinc_event_map ON (
882 coinc_event_map.coinc_event_id == sim_coinc_map.coinc_event_id
884 JOIN sngl_inspiral ON (
885 coinc_event_map.table_name == 'sngl_inspiral
'
886 AND coinc_event_map.event_id == sngl_inspiral.event_id
888 JOIN sim_inspiral AS sim ON sim.simulation_id == sim_coinc_map.simulation_id
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)
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)
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)
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
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
929 # =============================================================================
931 # Background vs. Injections --- Multi Instrument
933 # =============================================================================
937 class BackgroundVsInjectionPlotsMulti(object):
938 class Points(object):
940 self.background_snreff = []
941 self.injections_snreff = []
942 self.zerolag_snreff = []
943 self.background_deff = []
944 self.injections_deff = []
945 self.zerolag_deff = []
947 def __init__(self, snrfactor):
948 self.snrfactor = snrfactor
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("""
964 time_slide.time_slide_id == coinc_event.time_slide_id
965 AND time_slide.offset != 0
969 JOIN coinc_event_map AS coinc_event_map_x ON (
970 coinc_event_map_x.coinc_event_id == coinc_event.coinc_event_id
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
976 JOIN coinc_event_map AS coinc_event_map_y ON (
977 coinc_event_map_y.coinc_event_id == coinc_event.coinc_event_id
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
983 JOIN coinc_inspiral ON (
984 coinc_inspiral.coinc_event_id == coinc_event.coinc_event_id
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()
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))
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))
1004 for values in contents.connection.cursor().execute("""
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
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
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
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
1025 sngl_inspiral_x.ifo > sngl_inspiral_y.ifo
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))
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
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
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
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
1069 # =============================================================================
1071 # Rate vs. Threshold Plots
1073 # =============================================================================
1077 def sigma_region(mean, nsigma):
1078 return numpy.concatenate((mean - nsigma * numpy.sqrt(mean), (mean + nsigma * numpy.sqrt(mean))[::-1]))
1081 def create_farplot(axes, zerolag_stats, expected_count_x, expected_count_y, is_open_box, xlim = (None, None), max_events = 1000):
1083 # isolate relevent data
1086 zerolag_stats = zerolag_stats[:max_events]
1089 # background. uncomment the two lines to make the background
1090 # stair-step-style like the observed counts
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)
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])
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)
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")
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")
1123 # adjust bounds of plot
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])
1128 axes.set_ylim((0.001, 10.**math.ceil(math.log10(max_events))))
1131 class RateVsThreshold(object):
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()
1143 def add_contents(self, contents):
1144 if contents.sim_inspiral_table is not None:
1145 # skip injection documents
1148 self.farsegs |= contents.farsegs
1150 for ln_likelihood_ratio, far, fap, snr, is_background in connection.cursor().execute("""
1152 coinc_event.likelihood,
1153 coinc_inspiral.combined_far,
1154 coinc_inspiral.false_alarm_rate,
1162 time_slide.time_slide_id == coinc_event.time_slide_id
1163 AND time_slide.offset != 0
1167 JOIN coinc_event ON (
1168 coinc_event.coinc_event_id == coinc_inspiral.coinc_event_id
1171 coinc_event.likelihood >= 0.
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)
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)
1185 livetime = far.get_live_time(self.farsegs)
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
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)]:
1201 fig, axes = create_plot(None, r"Number of Events")
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))
1209 axes.set_title(r"Event Count vs.\ Inverse False-Alarm Rate Threshold")
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
1215 if ln_likelihood_ratio:
1216 fig, axes = create_plot(None, r"Number of Events")
1219 zerolag_stats = numpy.array(sorted(ln_likelihood_ratio, reverse = True))
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
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
1241 create_farplot(axes, zerolag_stats, expected_count_x, expected_count_y, is_open_box, xlim = (None, 23.), max_events = 10000)
1243 axes.set_title(r
"Event Count vs.\ Ranking Statistic Threshold")
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
1251 # =============================================================================
1255 # =============================================================================
1260 # Parse command line
1264 options, filenames = parse_command_line()
1272 # how many there could be, so we know how many digits for the filenames
1273 max_plot_groups = None
1275 def new_plots(plots = None, far_thresh = None):
1276 global max_plot_groups
1279 MissedFoundPlots(far_thresh = far_thresh),
1280 ParameterAccuracyPlots(),
1281 BackgroundVsInjectionPlots(),
1282 BackgroundVsInjectionPlotsMulti(snrfactor = 50.0),
1284 InjectionParameterDistributionPlots(),
1286 max_plot_groups = len(l)
1288 plots = range(len(l))
1289 return [l[i] for i in plots]
1291 plots = new_plots(options.plot_group, options.far_threshold)
1292 if options.plot_group is None:
1293 options.plot_group = range(len(plots))
1301 wiki = open(os.path.join(options.output_dir,
"%s_%s" % (options.user_tag,
"plotsummary.txt")),
"w")
1303 for n, filename in enumerate(filenames):
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):
1314 print >>sys.stderr,
"adding to plot group %d ..." % n
1315 plot.add_contents(contents)
1317 dbtables.discard_connection_filename(filename, working_filename,
verbose = options.
verbose)
1321 # Finish and write plots, deleting them as we go to save memory
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)
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)
1333 print >>sys.stderr,
"writing %s ..." % filename
1334 fig.savefig(filename)