LALBurst 2.0.7.1-eeff03c
lalburst_power_final.py
Go to the documentation of this file.
1##python
2#
3# Copyright (C) 2007 Kipp Cannon
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the
7# Free Software Foundation; either version 2 of the License, or (at your
8# option) any later version.
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13# Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19
20# Information extracted from data set:
21#
22# - from zero lag obtain zero lag event rate vs. amplitude threshold curve,
23# and amplitude of loudest event to use as final threshold
24#
25# - from time slides, obtain background event rate vs. amplitude threshold
26# curve. this is \mu_{0}(x) in Brady et al. upper limit paper.
27#
28# - numerically differentiate that curve to obtain \mu_{0}'(x). this is
29# done by first computing a spline approximation of the \mu_{0}(x) curve,
30# and then differentiating that curve.
31#
32# - from histogram of the times between events, confirm that background is
33# a Poisson process. but should this be of all background events, or only
34# background events with amplitudes above the final threshold? again, the
35# latter could be a problem if there aren't enough events in the time
36# slides above threshold
37#
38# - in each M_{sun} vs. centre frequency bin, measure the injection
39# recovery probability at the final threshold. this is \epsilon(x) in
40# Brady et al., and required two 2D M_{sun} vs. frequency binnings to
41# compute. all made injections go into the first 2D binning, and all
42# injections recovered with an amplitude above threshold go into the second
43# 2D binning. the ratio of the second binning's counts to those in the
44# first yields the detection efficiency (found / made).
45#
46# - in each M_{sun} vs. centre frequency bin, measure the injection
47# recovery probability density at the final threshold. this is
48# \epsilon'(x) in Brady et al., and requires one additional 2D M_{sun} vs.
49# frequency binning to compute. all recovered injections regardless of
50# amplitude go into this binning weighted by an amplitude window function.
51# the ratio of this third binnings' counts to those in the first above
52# yields the efficiency density if the amplitude window has been normalized
53# properly (so as to yield count per amplitude interval).
54
55
56#
57# =============================================================================
58#
59# Preamble
60#
61# =============================================================================
62#
63
64
65import bisect
66try:
67 from fpconst import PosInf, NegInf
68except ImportError:
69 # fpconst is not part of the standard library and might not be
70 # available
71 PosInf = float("+inf")
72 NegInf = float("-inf")
73import glob
74import heapq
75import math
76from matplotlib import patches
77import numpy
78from scipy import interpolate
79from scipy import optimize
80from optparse import OptionParser
81import sqlite3
82import sys
83
84
85from igwn_ligolw import dbtables
86from lal import iterutils
87from lal import rate
88from lalburst import git_version
89from lalburst import SimBurstUtils
90from lalburst import SnglBurstUtils
91
92
93__author__ = "Kipp Cannon <kipp.cannon@ligo.org>"
94__version__ = "git id %s" % git_version.id
95__date__ = git_version.date
96
97
98#
99# =============================================================================
100#
101# Command Line
102#
103# =============================================================================
104#
105
106
108 parser = OptionParser(
109 version = "Name: %%prog\n%s" % git_version.verbose_msg,
110 usage = "%prog [options] -i|--injections-glob pattern -b|--background-glob pattern",
111 description = "%prog performs the final, summary, stages of an upper-limit excess power search for burst-like gravitational waves."
112 )
113 parser.add_option("-b", "--background-glob", metavar = "pattern", default = [], action = "append", help = "Shell filename pattern for non-injection files (required).")
114 parser.add_option("-i", "--injections-glob", metavar = "pattern", default = [], action = "append", help = "Shell filename pattern for injection files (required).")
115 parser.add_option("-l", "--live-time-program", metavar = "program", default = "lalapps_power", help = "Set the name, as it appears in the process table, of the program whose search summary \"out\" segments define the search live time (default = \"lalapps_power\")")
116 parser.add_option("--open-box", action = "store_true", help = "Open the box. If this is not set, then instead of setting a threshold based on the loudest zero-lag coincidence, the threshold is set at the predicted loudest-event amplitude as derived from the background.")
117 parser.add_option("--confidence-contour-slope", metavar = "slope", type = "float", default = -12.0, help = "Set the slope of the confidence-likelihood joint threshold contours (default = -12.0).")
118 parser.add_option("--dump-scatter-data", action = "store_true", help = "Instead of computing upper limit, dump data files for confidence-likelihood scatter diagnostic scatter plot.")
119 parser.add_option("--plot-scatter-data", action = "store_true", help = "Instead of computing upper limit, load data files for confidence-likelihood scatter diagnostic scatter plot and generate scatter plot.")
120 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.")
121 parser.add_option("--upper-limit-confidence", metavar = "percent", type = "float", default = 90, help = "Set the confidence in percent of the upper limit to derive (default = 90.0).")
122 parser.add_option("--upper-limit-scale", metavar = "E|hrss", default = "E", help = "Select the scale on which to measure the upper limit. Allowed values are 'E' for an upper limit on rate of bursts by energy, and 'hrss' for an upper limit on rate of bursts by h_{rss}. (default = 'E')")
123 parser.add_option("--zero-lag-survivors", metavar = "number", type = "int", default = 0, help = "Set the final confidence-likelihood joint threshold so that this many events survive at zero lag. If n survivors are requested, then the threshold is set at exactly the amplitude of the n+1 loudest event, and events are discarded if their amplitudes are <= the threshold. A \"loudest-event\" upper limit is obtained by setting this to 0. The default is 0.")
124 parser.add_option("-v", "--verbose", action = "store_true", help = "be verbose")
125 options, filenames = parser.parse_args()
126
127 if not options.plot_scatter_data:
128 if not options.background_glob:
129 raise ValueError("missing required --background-glob argument")
130 if not options.injections_glob:
131 raise ValueError("missing required --injections-glob argument")
132
133 options.upper_limit_confidence /= 100.0
134
135 if options.upper_limit_scale not in ("E", "hrss"):
136 raise ValueError("invalid value '%s' for --upper-limit-scale" % options.upper_limit_scale)
137
138 return options, (filenames or [None])
139
140
141#
142# =============================================================================
143#
144# Re-usable Queries
145#
146# =============================================================================
147#
148
149
150#
151# An iterator that returns (id, likelihood, confidence, is_background)
152# tuples for all sngl_burst <--> sngl_burst coincs.
153#
154
155
157 return database.connection.cursor().execute("""
158SELECT
159 coinc_event.coinc_event_id,
160 coinc_event.likelihood,
161 multi_burst.confidence,
162 EXISTS (
163 SELECT
164 *
165 FROM
166 time_slide
167 WHERE
168 time_slide.time_slide_id == coinc_event.time_slide_id
169 AND time_slide.offset != 0
170 )
171FROM
172 coinc_event
173 JOIN multi_burst ON (
174 multi_burst.coinc_event_id == coinc_event.coinc_event_id
175 )
176WHERE
177 coinc_event.coinc_def_id == ?
178 """, (database.bb_definer_id,))
179
180
181#
182# Construct the temporary sim_coinc_map view that allows for quick mapping
183# from sim_burst to sngl_burst<-->sngl_burst coincs
184#
185
186
187def create_sim_coinc_map_view(connection):
188 connection.cursor().execute("""
189CREATE TEMPORARY VIEW
190 sim_coinc_map
191AS
192 SELECT
193 sim_burst.simulation_id AS simulation_id,
194 sim_coinc_event.coinc_def_id AS sim_coinc_def_id,
195 burst_coinc_event.coinc_event_id AS burst_coinc_event_id,
196 burst_coinc_event.likelihood AS burst_coinc_likelihood,
197 multi_burst.confidence AS burst_coinc_confidence,
198 coinc_detection_statistic(burst_coinc_event.likelihood, multi_burst.confidence) AS burst_coinc_amplitude
199 FROM
200 sim_burst
201 JOIN coinc_event_map AS a ON (
202 a.table_name == 'sim_burst'
203 AND a.event_id == sim_burst.simulation_id
204 )
205 JOIN coinc_event AS sim_coinc_event ON (
206 sim_coinc_event.coinc_event_id == a.coinc_event_id
207 )
208 JOIN coinc_event_map AS b ON (
209 b.coinc_event_id == a.coinc_event_id
210 )
211 JOIN coinc_event AS burst_coinc_event ON (
212 b.table_name == 'coinc_event'
213 AND b.event_id == burst_coinc_event.coinc_event_id
214 )
215 JOIN multi_burst ON (
216 multi_burst.coinc_event_id == burst_coinc_event.coinc_event_id
217 )
218 """)
219
220
221#
222# =============================================================================
223#
224# Measuring Confidence-Likelihood Density Contour Slope
225#
226# =============================================================================
227#
228
229
230#
231# Phase 1: dump data files.
232#
233
234
235def dump_confidence_likelihood_scatter_data(globs, live_time_program = "lalapps_power", tmp_path = None, verbose = False):
236 #
237 # Collect file names.
238 #
239
240 if verbose:
241 print("building file list ...", file=sys.stderr)
242 filenames = sorted(filename for g in globs for filename in glob.glob(g))
243
244 #
245 # Initialize storage.
246 #
247
248 injections = []
249 background = []
250 zero_lag = []
251
252 #
253 # Iterate over files.
254 #
255
256 for n, filename in enumerate(filenames):
257 #
258 # Open the database file.
259 #
260
261 if verbose:
262 print("%d/%d: %s" % (n + 1, len(filenames), filename), file=sys.stderr)
263 working_filename = dbtables.get_connection_filename(filename, tmp_path = tmp_path, verbose = verbose)
264 connection = sqlite3.connect(working_filename)
265 connection.create_function("coinc_detection_statistic", 2, coinc_detection_statistic)
266 database = SnglBurstUtils.CoincDatabase(connection, live_time_program)
267 if verbose:
268 SnglBurstUtils.summarize_coinc_database(database)
269
270 #
271 # Process database contents. Assume all files with
272 # sim_burst tables are the outputs of injection runs, and
273 # others aren't.
274 #
275
276 if database.sim_burst_table is None:
277 # non-injections
278 for id, l, c, is_background in bb_id_likelihood_confidence_background(database):
279 record = (coinc_detection_statistic(l, c), l, c)
280 if is_background:
281 if len(background) < 1e6:
282 heapq.heappush(background, record)
283 else:
284 heapq.heappushpop(background, record)
285 else:
286 if len(zero_lag) < 1e6:
287 heapq.heappush(zero_lag, record)
288 else:
289 heapq.heappushpop(zero_ag, record)
290 else:
291 # injections
292 create_sim_coinc_map_view(database.connection)
293 for a, l, c in database.connection.cursor().execute("""
294SELECT
295 burst_coinc_amplitude,
296 burst_coinc_likelihood,
297 burst_coinc_confidence
298FROM
299 sim_coinc_map
300WHERE
301 sim_coinc_def_id == ?
302 """, (database.sce_definer_id,)):
303 record = (-a, l, c)
304 if len(injections) < 1e6:
305 heapq.heappush(injections, record)
306 else:
307 heapq.heappushpop(injections, record)
308
309 #
310 # Done with this file.
311 #
312
313 database.connection.close()
314 dbtables.discard_connection_filename(filename, working_filename, verbose = verbose)
315
316 #
317 # Dump scatter plot data.
318 #
319
320 if verbose:
321 print("writing scatter plot data ...", file=sys.stderr)
322
323 f = file("lalburst_power_final_background_scatter.dat", "w")
324 for a, l, c in background:
325 print("%.16g %.16g" % (l, c), file=f)
326
327 f = file("lalburst_power_final_zero_lag_scatter.dat", "w")
328 for a, l, c in zero_lag:
329 print("%.16g %.16g" % (l, c), file=f)
330
331 f = file("lalburst_power_final_injections_scatter.dat", "w")
332 for a, l, c in injections:
333 print("%.16g %.16g" % (l, c), file=f)
334
335 if verbose:
336 print("done.", file=sys.stderr)
337
338
339#
340# Phase 2: generate scatter plot from data files.
341#
342
343
344def plot_confidence_likelihood_scatter_data(slope, verbose = False):
345 #
346 # Start plot
347 #
348
349 fig = SnglBurstUtils.figure.Figure()
350 SnglBurstUtils.FigureCanvas(fig)
351 fig.set_size_inches(6.5, 6.5 / ((1 + math.sqrt(5)) / 2))
352 axes = fig.gca()
353 axes.grid(True)
354 axes.loglog()
355 axes.set_xlabel(r"Confidence")
356 axes.set_ylabel(r"Likelihood Ratio")
357 axes.set_title(r"Likelihood Ratio vs.\ Confidence Scatter Plot")
358
359 #
360 # Plot scatter data
361 #
362
363 def read_and_plot(filename, colour, verbose = False):
364 if verbose:
365 print("reading '%s' ..." % filename, file=sys.stderr)
366 X = []
367 Y = []
368 for line in file(filename):
369 if line[0] == "#":
370 continue
371 y, x = map(float, line.strip().split())
372 X.append(x)
373 Y.append(y)
374 if verbose:
375 print("plotting ...", file=sys.stderr)
376 return axes.plot(X, Y, colour)
377
378 set1 = read_and_plot("lalburst_power_final_injections_scatter.dat", "r+", verbose = verbose)
379 set2 = read_and_plot("lalburst_power_final_background_scatter.dat", "k+", verbose = verbose)
380 #set3 = read_and_plot("lalburst_power_final_zero_lag_scatter.dat", "b+", verbose = verbose)
381
382 #axes.legend((set1, set2, set3), (r"Injections", r"Background (time slides)", r"Zero lag"), loc = "lower right")
383 axes.legend((set1, set2), (r"Injections", r"Background (time slides)"), loc = "lower right")
384
385 #
386 # Plot threshold contours
387 #
388
389 def fit(x, lnb):
390 return numpy.exp(slope * numpy.log(x) + lnb)
391
392 if verbose:
393 print("plotting contours ...", file=sys.stderr)
394 ymin, ymax = axes.get_ylim()
395 for lnb in range(10, 110, 10):
396 x = 10**numpy.arange(0.85, 3.0, 0.01)
397 y = fit(x, lnb)
398 x = numpy.compress((ymin <= y) & (y <= ymax), x)
399 y = numpy.compress((ymin <= y) & (y <= ymax), y)
400 axes.plot(x, y, "g-")
401
402 #
403 # Adjust plot range and add likelihood = 1.0 marker
404 #
405
406 axes.plot(axes.get_xlim(), (1, 1), "k-")
407 axes.set_xlim(10**0.85, 10**3)
408
409 #
410 # Write plot
411 #
412
413 if verbose:
414 print("writing 'lalburst_power_final_scatter.png' ...", file=sys.stderr)
415 fig.savefig("lalburst_power_final_scatter.png")
416
417 #
418 # Done
419 #
420
421 if verbose:
422 print("done.", file=sys.stderr)
423
424
425#
426# =============================================================================
427#
428# Rate vs. Threshold
429#
430# =============================================================================
431#
432
433
434#
435# Book-keeping class
436#
437
438
440 def __init__(self):
446
447
448 def update_from(self, contents, filename = "", verbose = False):
449 #
450 # Add the live times from this file to the totals.
451 #
452
453 if verbose:
454 print("measuring live time ...", file=sys.stderr)
455 zero_lag_time_slides, background_time_slides = SnglBurstUtils.get_time_slides(contents.connection)
456 self.zero_lag_live_time += SnglBurstUtils.time_slides_livetime(contents.seglists, zero_lag_time_slides.values(), verbose = verbose)
457 self.background_live_time += SnglBurstUtils.time_slides_livetime(contents.seglists, background_time_slides.values(), verbose = verbose)
458
459 #
460 # Iterate over burst<-->burst coincidences. Assume there
461 # are no injections in this file.
462 #
463
464 if verbose:
465 print("retrieving sngl_burst<-->sngl_burst coincs ...", file=sys.stderr)
466 for id, likelihood, confidence, is_background in bb_id_likelihood_confidence_background(contents):
467 record = coinc_detection_statistic(likelihood, confidence)
468 if is_background:
469 # remember the total number, but only keep
470 # the highest ranked
472 if len(self.background_amplitudes) < 1e7:
473 heapq.heappush(self.background_amplitudes, record)
474 else:
475 heapq.heappushpop(self.background_amplitudes, record)
476 else:
477 self.zero_lag_amplitudes.append((record, filename, id))
478 if verbose:
479 print("done", file=sys.stderr)
480
481
482 def finish(self, zero_lag_survivors, open_box = False, verbose = False):
483 # sort the amplitudes from highest to lowest.
484
485 self.background_amplitudes.sort(reverse = True)
486 if not open_box:
487 # just forget them for safety
488 self.zero_lag_amplitudes = []
489 else:
490 self.zero_lag_amplitudes.sort(reverse = True)
491
492 # compute the mean event rate vs. amplitude threshold as
493 # observed in the time slides. xcoords are the background
494 # event amplitudes, ycoords are the are the corresponding
495 # rates. this is \mu_{0}(x) in the Brady et al. paper, but
496 # the actual value at threshold is extracted from a
497 # smoothed approximation below.
498
499 self.background_rate_x = numpy.array(self.background_amplitudes, dtype = "double")
500 self.background_rate_y = numpy.arange(1, len(self.background_rate_x) + 1, dtype = "double") / self.background_live_time
501 self.background_rate_x = self.background_rate_x[::-1]
502 self.background_rate_y = self.background_rate_y[::-1]
503
504 # compute the 1-sigma vertical error bars. ycoords is the
505 # expected event rate measured from the background, times
506 # zero_lag_live_time is the expected event count at zero
507 # lag, the square root of which is the 1 sigma Poisson
508 # fluctuation in the expected zero lag event count, divided
509 # by zero_lag_live_time gives the 1 sigma Poisson
510 # fluctuation in the expected zero lag event rate.
511
513
514 # given the desired number of survivors at zero lag, find
515 # the "amplitude" threshold to cut coincs on. The
516 # interpretation is that only coincidences with an
517 # "amplitude" greater than (not equal to) this value are to
518 # be retained.
519
520 if open_box:
521 # obtain threshold from loudest zero-lag trigger
522 self.amplitude_threshold = self.zero_lag_amplitudes[zero_lag_survivors][0]
523 else:
524 # obtain threshold from background rate
525 self.amplitude_threshold = self.background_rate_x[-1 - bisect.bisect(self.background_rate_y[::-1], 1.0 / self.zero_lag_live_time)]
526
527 # compute cubic spline approximation of background event
528 # rate curve in the vicinity of the amplitude threshold.
529 # this is used to obtain a smoothed approximation of the
530 # background event rate and its first derivative, which are
531 # \mu_{0}(x) and \mu_{0}'(x) in the Brady et al. paper.
532 # because of the shape of the curve, it is more natural to
533 # approximate ln rate(threshold) instead of
534 # rate(threshold). the curve samples used to construct the
535 # spline are chosen by identifying the sample corresponding
536 # to the amplitude threshold, and then taking the samples
537 # on either side of that corresponding to +/- 1 order of
538 # magnitude in rate (a symmetric interval in ln(rate)). to
539 # obtain \mu_{0}'(x) from the spline, if
540 #
541 # y = ln rate(threshold)
542 #
543 # then
544 #
545 # dy/dthreshold = 1/rate drate/dthreshold
546 #
547 # so
548 #
549 # drate/dthreshold = rate * dy/dthreshold
550 #
551 # the uncertainty in \mu_{0}(x) is computed the same way as
552 # background_rate_dy above, except using \mu_{0}(x) as the
553 # rate and using the total background live time instead of
554 # the zero lag live time.
555
556 index = bisect.bisect(self.background_rate_x, self.amplitude_threshold)
557 lo = len(self.background_rate_x) - (len(self.background_rate_x) - index) * 10
558 hi = len(self.background_rate_x) - (len(self.background_rate_x) - index) / 10
559 spline = interpolate.UnivariateSpline(self.background_rate_x[lo:hi], numpy.log(self.background_rate_y[lo:hi]), k = 3)
560 self.background_rate_approximant = lambda x: numpy.exp(spline(x))
561
562 self.mu_0 = math.exp(spline(self.amplitude_threshold))
563 self.dmu_0 = numpy.sqrt(self.mu_0 * self.background_live_time) / self.background_live_time
564 self.mu_0primed = self.mu_0 * spline(self.amplitude_threshold, nu = 1)
565
566 # compute the mean event rate vs. amplitude threshold as
567 # observed at zero-lag. to make the plots nicer, don't
568 # include zero lag events whose amplitudes are below the
569 # lowest amplitude recorded for a background event. again,
570 # the x co-ordinates come out in decreasing order (the were
571 # sorted that way above), but for convenience we reverse
572 # the arrays so that the x co-ordinates are in increasing
573 # order.
574
575 self.zero_lag_rate_x = numpy.array([zero_lag_amplitude[0] for zero_lag_amplitude in self.zero_lag_amplitudes], dtype = "double")
576 self.zero_lag_rate_y = numpy.arange(1, len(self.zero_lag_rate_x) + 1, dtype = "double") / self.zero_lag_live_time
577 self.zero_lag_rate_x = self.zero_lag_rate_x[::-1]
578 self.zero_lag_rate_y = self.zero_lag_rate_y[::-1]
579 index = bisect.bisect(self.zero_lag_rate_x, self.background_rate_x[0])
580 self.zero_lag_rate_x = self.zero_lag_rate_x[index:]
581 self.zero_lag_rate_y = self.zero_lag_rate_y[index:]
582
583
584#
585# Measure threshold
586#
587
588
589def measure_threshold(filenames, n_survivors, live_time_program = "lalapps_power", tmp_path = None, open_box = False, verbose = False):
590 #
591 # Initialize the book-keeping object.
592 #
593
594 rate_vs_threshold_data = RateVsThresholdData()
595
596 #
597 # Iterate over non-injection files.
598 #
599
600 for n, filename in enumerate(filenames):
601 #
602 # Open the database file.
603 #
604
605 if verbose:
606 print("%d/%d: %s" % (n + 1, len(filenames), filename), file=sys.stderr)
607 working_filename = dbtables.get_connection_filename(filename, tmp_path = tmp_path, verbose = verbose)
608 database = SnglBurstUtils.CoincDatabase(sqlite3.connect(str(working_filename)), live_time_program)
609 if verbose:
610 SnglBurstUtils.summarize_coinc_database(database)
611
612 #
613 # Process database contents.
614 #
615
616 rate_vs_threshold_data.update_from(database, filename = filename, verbose = verbose)
617
618 #
619 # Done with this file.
620 #
621
622 database.connection.close()
623 dbtables.discard_connection_filename(filename, working_filename, verbose = verbose)
624
625 #
626 # Determine likelihood threshold.
627 #
628
629 if verbose:
630 print("finishing rate vs. threshold measurement ...", file=sys.stderr)
631 rate_vs_threshold_data.finish(n_survivors, open_box = open_box, verbose = verbose)
632
633 #
634 # Done.
635 #
636
637 return rate_vs_threshold_data
638
639
640#
641# Print results.
642#
643
644
645def print_rate_vs_threshold_data(rate_vs_threshold_data, confidence_contour_slope):
646 print(file=sys.stderr)
647 print("=== Threshold Summary ===", file=sys.stderr)
648 print(file=sys.stderr)
649 print("threshold definition: ln likelihood > %.16g ln confidence + %.16g" % (confidence_contour_slope, rate_vs_threshold_data.amplitude_threshold), file=sys.stderr)
650 print("total live time in background = %.16g s" % rate_vs_threshold_data.background_live_time, file=sys.stderr)
651 print("total live time at zero lag = %.16g s" % rate_vs_threshold_data.zero_lag_live_time, file=sys.stderr)
652 print("number of coincs in background = %d" % rate_vs_threshold_data.n_background_amplitudes, file=sys.stderr)
653 print("average number of background coincs per zero lag live time = %.16g" % (rate_vs_threshold_data.n_background_amplitudes / rate_vs_threshold_data.background_live_time * rate_vs_threshold_data.zero_lag_live_time), file=sys.stderr)
654 print("number of coincs at zero lag = %d" % len(rate_vs_threshold_data.zero_lag_amplitudes), file=sys.stderr)
655 print("at threshold, \\mu_{0} = %.16g Hz +/- %.16g Hz" % (rate_vs_threshold_data.mu_0, rate_vs_threshold_data.dmu_0), file=sys.stderr)
656 print("at threshold, \\mu_{0}' = %.16g Hz / unit of amplitude" % rate_vs_threshold_data.mu_0primed, file=sys.stderr)
657 print(file=sys.stderr)
658 print("100 Highest-Ranked Zero Lag Events", file=sys.stderr)
659 print("----------------------------------", file=sys.stderr)
660 print(file=sys.stderr)
661 print("Detection Statistic\tFilename\tID", file=sys.stderr)
662 for amplitude, filename, id in rate_vs_threshold_data.zero_lag_amplitudes[:100]:
663 print("%.16g\t%s\t%s" % (amplitude, filename, id), file=sys.stderr)
664 print(file=sys.stderr)
665
666
667#
668# Plot
669#
670
671
672def plot_rate_vs_threshold(data):
673 """
674 Input is a RateVsThresholdData instance. Output is a matplotlib
675 Figure instance.
676 """
677 # start the rate vs. threshold plot
678
679
680 print(file=sys.stderr)
681 print("plotting event rate ...", file=sys.stderr)
682
683 fig, axes = SnglBurstUtils.make_burst_plot(r"Detection Statistic Threshold", r"Mean Event Rate (Hz)")
684 axes.semilogy()
685
686 # draw the background rate 1-sigma error bars as a shaded polygon
687 # clipped to the plot. warning: the error bar polygon is not
688 # *really* clipped to the axes' bounding box, the result will be
689 # incorrect if the number of sample points is small.
690
691 ymin = 1e-9
692 ymax = 1e0
693 poly_x = numpy.concatenate((data.background_rate_x, data.background_rate_x[::-1]))
694 poly_y = numpy.concatenate((data.background_rate_y + 1 * data.background_rate_dy, (data.background_rate_y - 1 * data.background_rate_dy)[::-1]))
695 axes.add_patch(patches.Polygon(numpy.column_stack((poly_x, numpy.clip(poly_y, ymin, ymax))), edgecolor = "k", facecolor = "k", alpha = 0.3))
696
697 # draw the mean background event rate
698
699 line1 = axes.plot(data.background_rate_x, data.background_rate_y, "k-")
700
701 # draw the smoothed approximation to the mean background event rate
702
703 #index = bisect.bisect(data.background_rate_x, data.amplitude_threshold)
704 #lo = len(data.background_rate_x) - (len(data.background_rate_x) - index) * 10
705 #hi = len(data.background_rate_x) - (len(data.background_rate_x) - index) / 10
706 #x = data.background_rate_x[lo:hi]
707 #line2 = axes.plot(x, data.background_rate_approximant(x), "b-")
708
709 # draw the mean zero-lag event rate. if the box is closed, these
710 # arrays will be empty.
711
712 line3 = axes.plot(data.zero_lag_rate_x, data.zero_lag_rate_y, "r-")
713
714 # draw a vertical marker indicating the threshold
715
716 axes.axvline(data.amplitude_threshold, color = "k")
717
718 #axes.set_ylim((ymin, ymax))
719 #axes.xaxis.grid(True, which="minor")
720 #axes.yaxis.grid(True, which="minor")
721
722 # add a legend and a title
723
724 axes.legend((line1, line3), (r"Background (time slides)", r"Zero lag"))
725 axes.set_title(r"Mean Event Rate vs.\ Detection Statistic Threshold")
726
727 # done rate vs. threshold plot
728
729 #print >>sys.stderr, "writing lalburst_power_final_rate_vs_threshold.pdf ..."
730 #fig.savefig("lalburst_power_final_rate_vs_threshold.pdf")
731 print("writing lalburst_power_final_rate_vs_threshold.png ...", file=sys.stderr)
732 fig.savefig("lalburst_power_final_rate_vs_threshold.png")
733
734 # start rate vs. threshold residual plot
735
736 fig, axes = SnglBurstUtils.make_burst_plot(r"Detection Statistic Threshold", r"Mean Event Rate Residual (standard deviations)")
737
738 # construct a linear interpolator for the backround rate data so
739 # that we can evaluate it at the x co-ordinates of the zero-lag
740 # curve samples
741
742 interp_y = interpolate.interpolate.interp1d(data.background_rate_x, data.background_rate_y, kind = "linear", bounds_error = False)
743 interp_dy = interpolate.interpolate.interp1d(data.background_rate_x, data.background_rate_dy, kind = "linear", bounds_error = False)
744
745 # plot the residual
746
747 axes.plot(data.zero_lag_rate_x, (data.zero_lag_rate_y - interp_y(data.zero_lag_rate_x)) / interp_dy(data.zero_lag_rate_x), "k-")
748
749 # add a title
750
751 axes.set_title(r"Mean Event Rate Residual vs.\ Detection Statistic Threshold")
752
753 # done rate vs. threshold residual plot
754
755 #print >>sys.stderr, "writing lalburst_power_final_rate_vs_threshold_residual.pdf ..."
756 #fig.savefig("lalburst_power_final_rate_vs_threshold_residual.pdf")
757 print("writing lalburst_power_final_rate_vs_threshold_residual.png ...", file=sys.stderr)
758 fig.savefig("lalburst_power_final_rate_vs_threshold_residual.png")
759
760 # done
761
762
763#
764# =============================================================================
765#
766# Efficiency
767#
768# =============================================================================
769#
770
771
772#
773# Diagnostic plots
774#
775
776
777def diagnostic_plot(z, bins, title, ylabel, filename):
778 print("generating %s ..." % filename, file=sys.stderr)
779 fig, axes = SnglBurstUtils.make_burst_plot("Centre Frequency (Hz)", ylabel)
780 axes.loglog()
781 xcoords, ycoords = bins.centres()
782 cset = axes.contour(xcoords, ycoords, numpy.transpose(z), 9)
783 cset.clabel(inline = True, fontsize = 5, fmt = r"$%g$", colors = "k")
784 axes.set_title(title)
785 fig.savefig(filename)
786
787
788#
789# Book-keeping class
790#
791
792
793class EfficiencyData(SimBurstUtils.Efficiency_hrss_vs_freq):
794 def __init__(self, *args):
795 SimBurstUtils.Efficiency_hrss_vs_freq.__init__(self, *args)
797
798
799 def add_contents(self, contents, threshold):
800 if self.instruments != contents.instruments:
801 raise ValueError("this document contains instruments %s, but we want %s" % ("+".join(contents.instruments), "+".join(self.instruments)))
802
803 # iterate over all sims
804
805 offsetvectors = contents.time_slide_table.as_dict()
806 create_sim_coinc_map_view(contents.connection)
807 for values in contents.connection.cursor().execute("""
808SELECT
809 sim_burst.*,
810 (
811 SELECT
812 MAX(sim_coinc_map.burst_coinc_amplitude)
813 FROM
814 sim_coinc_map
815 WHERE
816 sim_coinc_map.simulation_id == sim_burst.simulation_id
817 AND sim_coinc_map.sim_coinc_def_id == ?
818 )
819FROM
820 sim_burst
821 """, (contents.scn_definer_id,)):
822 sim = contents.sim_burst_table.row_from_cols(values)
823 detection_statistic = values[-1]
824 amplitude = self.amplitude_func(sim, None)
825 if SimBurstUtils.on_instruments(sim, contents.seglists, offsetvectors[sim.time_slide_id]) == self.instruments:
826 # injection was made
827 self.injected_x.append(sim.frequency)
828 self.injected_y.append(amplitude)
829 if detection_statistic is not None:
830 # injection was recovered (found at
831 # all)
832 self.recovered_xyz.append((sim.frequency, amplitude, detection_statistic))
833 if detection_statistic > threshold:
834 # injection was found above
835 # threshold
836 self.found_x.append(sim.frequency)
837 self.found_y.append(amplitude)
838 elif (detection_statistic is not None) and (detection_statistic > threshold):
839 # injection was found, and found above
840 # threshold, but not "injected" because it
841 # lies outside of the output segments.
842 # this is not unusual, in particular
843 # because we are only looking for coincs
844 # that are "nearby" an injection which can
845 # mean several seconds.
846 print("odd, injection %s was found but not injected ..." % sim.simulation_id, file=sys.stderr)
847
848
849 def finish(self, threshold):
850 # bin the injections
851
852 self._bin_events()
853
854 # use the same binning for the found injection density as
855 # was constructed for the efficiency
856
857 self.found_density = rate.BinnedArray(self.efficiency.denominator.bins)
858
859 # construct the amplitude weighting function
860
861 amplitude_weight = rate.BinnedArray(rate.NDBins((rate.LinearBins(threshold - 100, threshold + 100, 10001),)))
862
863 # gaussian window's width is the number of bins
864 # corresponding to 10 units of amplitude, which is computed
865 # by dividing 10 by the "volume" of the bin corresponding
866 # to threshold. index is the index of the element in
867 # amplitude_weight corresponding to the threshold.
868
869 index, = amplitude_weight.bins[threshold,]
870 window = rate.gaussian_window(10.0 / amplitude_weight.bins.volumes()[index])
871 window /= 10 * window[(len(window) - 1) / 2]
872
873 # set the window data into the BinnedArray object. the
874 # Gaussian peaks on the middle element of the window, which
875 # we want to place at index in the amplitude_weight array.
876
877 lo = index - (len(window) - 1) / 2
878 hi = lo + len(window)
879 if lo < 0 or hi > len(amplitude_weight.array):
880 raise ValueError("amplitude weighting window too large")
881 amplitude_weight.array[lo:hi] = window
882
883 # store the recovered injections in the found density bins
884 # weighted by amplitude
885
886 for x, y, z in self.recovered_xyz:
887 try:
888 weight = amplitude_weight[z,]
889 except IndexError:
890 # beyond the edge of the window
891 weight = 0.0
892 self.found_density[x, y] += weight
893
894 # the efficiency is only valid up to the highest energy
895 # that has been injected. this creates problems later on
896 # so, instead, for each frequency, identify the highest
897 # energy that has been measured and copy the values for
898 # that bin's numerator and denominator into the bins for
899 # all higher energies. do the same for the counts in the
900 # found injection density bins.
901 #
902 # numpy.indices() returns two arrays array, the first of
903 # which has each element set equal to its x index, the
904 # second has each element set equal to its y index, we keep
905 # the latter. meanwhile numpy.roll() cyclically permutes
906 # the efficiency bins down one along the y (energy) axis.
907 # from this, the conditional finds bins where the
908 # efficiency is greater than 0.9 but <= 0.9 in the bin
909 # immediately above it in energy. we select the elements
910 # from the y index array where the conditional is true, and
911 # then use numpy.max() along the y direction to return the
912 # highest such y index for each x index, which is a 1-D
913 # array. finally, enumerate() is used to iterate over x
914 # index and corresponding y index, and if the y index is
915 # not negative (was found) the value from that x-y bin is
916 # copied to all higher bins.
917
918 n = self.efficiency.numerator.array
919 d = self.efficiency.denominator.array
920 f = self.found_density.array
921 bady = -1
922 for x, y in enumerate(numpy.max(numpy.where((d > 0) & (numpy.roll(d, -1, axis = 1) <= 0), numpy.indices(d.shape)[1], bady), axis = 1)):
923 if y != bady:
924 n[x, y + 1:] = n[x, y]
925 d[x, y + 1:] = d[x, y]
926 f[x, y + 1:] = f[x, y]
927
928 # now do the same for the bins at energies below those that
929 # have been measured.
930
931 bady = d.shape[1]
932 for x, y in enumerate(numpy.min(numpy.where((d > 0) & (numpy.roll(d, 1, axis = 1) <= 0), numpy.indices(d.shape)[1], bady), axis = 1)):
933 if y != bady:
934 n[x, 0:y] = n[x, y]
935 d[x, 0:y] = d[x, y]
936 f[x, 0:y] = f[x, y]
937
938 diagnostic_plot(self.efficiency.numerator.array, self.efficiency.denominator.bins, r"Efficiency Numerator (Before Averaging)", self.amplitude_lbl, "lalburst_power_final_efficiency_numerator_before.png")
939 diagnostic_plot(self.efficiency.denominator.array, self.efficiency.denominator.bins, r"Efficiency Denominator (Before Averaging)", self.amplitude_lbl, "lalburst_power_final_efficiency_denominator_before.png")
940 diagnostic_plot(self.found_density.array, self.efficiency.denominator.bins, r"Injections Lost / Unit of Threshold (Before Averaging)", self.amplitude_lbl, "lalburst_power_final_found_density_before.png")
941
942 # smooth the efficiency bins and the found injection
943 # density bins using the same 2D window.
944
945 window = rate.gaussian_window(self.window_size_x, self.window_size_y)
946 window /= window[tuple((numpy.array(window.shape, dtype = "double") - 1) / 2)]
947 rate.filter_binned_ratios(self.efficiency, window)
948 rate.filter_array(self.found_density.array, window)
949
950 diagnostic_plot(self.efficiency.numerator.array, self.efficiency.denominator.bins, r"Efficiency Numerator (After Averaging)", self.amplitude_lbl, "lalburst_power_final_efficiency_numerator_after.png")
951 diagnostic_plot(self.efficiency.denominator.array, self.efficiency.denominator.bins, r"Efficiency Denominator (After Averaging)", self.amplitude_lbl, "lalburst_power_final_efficiency_denominator_after.png")
952 diagnostic_plot(self.found_density.array, self.efficiency.denominator.bins, r"Injections Lost / Unit of Threshold (After Averaging)", self.amplitude_lbl, "lalburst_power_final_found_density_after.png")
953
954 # compute the uncertainties in the efficiency and its
955 # derivative by assuming these to be the binomial counting
956 # fluctuations in the numerators.
957
958 p = self.efficiency.ratio()
959 self.defficiency = numpy.sqrt(p * (1 - p) / self.efficiency.denominator.array)
960 p = self.found_density.array / self.efficiency.denominator.array
961 self.dfound_density = numpy.sqrt(p * (1 - p) / self.efficiency.denominator.array)
962
963
964#
965# Measure efficiency
966#
967
968
969def measure_efficiency(filenames, threshold, live_time_program = "lalapps_power", upper_limit_scale = "E", tmp_path = None, verbose = False):
970 # FIXME: instruments are hard-coded. bad bad bad. sigh...
971 if upper_limit_scale == "E":
972 efficiency = EfficiencyData(("H1", "H2", "L1"), (lambda sim, instrument: sim.egw_over_rsquared), r"Equivalent Isotropic Energy ($M_{\odot} / \mathrm{pc}^{2}$)", 0.1)
973 elif upper_limit_scale == "hrss":
974 efficiency = EfficiencyData(("H1", "H2", "L1"), (lambda sim, instrument: sim.hrss), r"$h_{\mathrm{rss}}$", 0.1)
975 else:
976 raise ValueError("bad upper_limit_scale %s" % repr(upper_limit_scale))
977
978 #
979 # Iterate over injection files.
980 #
981
982 for n, filename in enumerate(filenames):
983 #
984 # Open the database file.
985 #
986
987 if verbose:
988 print("%d/%d: %s" % (n + 1, len(filenames), filename), file=sys.stderr)
989 working_filename = dbtables.get_connection_filename(filename, tmp_path = tmp_path, verbose = verbose)
990 connection = sqlite3.connect(str(working_filename))
991 connection.create_function("coinc_detection_statistic", 2, coinc_detection_statistic)
992 database = SnglBurstUtils.CoincDatabase(connection, live_time_program)
993 if verbose:
994 SnglBurstUtils.summarize_coinc_database(database)
995
996 #
997 # Process database contents.
998 #
999
1000 efficiency.add_contents(database, threshold)
1001
1002 #
1003 # Done with this file.
1004 #
1005
1006 database.connection.close()
1007 dbtables.discard_connection_filename(filename, working_filename, verbose = verbose)
1008
1009 #
1010 # Compute efficiency from the data that have been collected
1011 #
1012
1013 if verbose:
1014 print("binning and smoothnig efficiency data ...", file=sys.stderr)
1015 efficiency.finish(threshold)
1016
1017 #
1018 # Done
1019 #
1020
1021 return efficiency
1022
1023
1024#
1025# Plot efficiency data
1026#
1027
1028
1029def plot_efficiency_data(efficiency_data):
1030 print("plotting efficiency curves ...", file=sys.stderr)
1031
1032 # use the stock plotting routing in SimBurstUtils for the
1033 # efficiency contour plot
1034
1035 fig = SimBurstUtils.plot_Efficiency_hrss_vs_freq(efficiency_data)
1036 #fig.gca().set_ylim((3e-17, 3e-10))
1037
1038 # done
1039
1040 #print >>sys.stderr, "writing lalburst_power_final_efficiency.pdf ..."
1041 #fig.savefig("lalburst_power_final_efficiency.pdf")
1042 print("writing lalburst_power_final_efficiency.png ...", file=sys.stderr)
1043 fig.savefig("lalburst_power_final_efficiency.png")
1044
1045
1046#
1047# =============================================================================
1048#
1049# Rate Upper Limit
1050#
1051# =============================================================================
1052#
1053
1054
1055#
1056# Non-linear root finding routine to solve for the rate
1057#
1058
1059
1060def mu_p_epsilon(xi, p):
1061 """
1062 Given the background correction factor $\\xi$, and the upper limit
1063 confidence p, solve
1064
1065 $p = 1 - \\ee^{-\\mu_{p} \\epsilon} (1 + \\xi \\mu_{p} \\epsilon)$
1066
1067 for ($\\mu_{p} \\epsilon$), the product of the foreground rate
1068 upper limit times the efficiency. Note that this product is always
1069 well defined by this relation (i.e., even when the efficiency is
1070 0).
1071 """
1072 def f(mue):
1073 return math.exp(-mue) * (1 + xi * mue) - (1 - p)
1074 def fprime(mue):
1075 return -math.exp(-mue) * (1 + xi * mue - xi)
1076 return optimize.newton(f, 1, fprime = fprime, tol = 1e-8)
1077
1078
1079#
1080# Compute the rate upper limit array
1081#
1082
1083
1084class RateData(object):
1085 def __init__(self, efficiency_data, confidence):
1086 self.rate_array = rate.BinnedArray(efficiency_data.efficiency.denominator.bins)
1087 self.amplitude_lbl = efficiency_data.amplitude_lbl
1088 self.confidence = confidence
1089
1090
1091def rate_upper_limit(efficiency_data, mu_0primed, zero_lag_live_time, p):
1092 print("computing rate upper limit ...", file=sys.stderr)
1093
1094 # initialize the rate upper limit array, giving it the same binning
1095 # as the efficiency array.
1096
1097 rate_data = RateData(efficiency_data, p)
1098
1099 # efficiency
1100
1101 e = efficiency_data.efficiency.ratio()
1102
1103 # the found density is the count of injections recovered with an
1104 # "amplitude" falling within a range of 1 unit centred on the
1105 # amplitude threshold, and is therefore the number of events lost
1106 # from the efficiency's numerator as the threshold is increased by
1107 # 1 unit. if t is the threshold, f is the number found, m is the
1108 # number made, and e = f / m is the efficiency, then
1109 #
1110 # e / (de/dt) = 1 / (d ln e / dt)
1111 #
1112 # and
1113 #
1114 # d ln e / dt = d/dt (ln f - ln m)
1115 # = d ln f / dt
1116 # = (1 / f) df/dt
1117 #
1118 # so
1119 #
1120 # e / (de/dt) = f / (df/dt)
1121 #
1122 # f are the counts in the efficiency's numerator bins, and because
1123 # the found density's bins are the count of events lost as the
1124 # threshold is increased they are -df/dt.
1125
1126 e_over_eprimed = efficiency_data.efficiency.numerator.array / -efficiency_data.found_density.array
1127
1128 # background rate correction factor, \xi in Brady et al, also a
1129 # function of frequency and energy.
1130
1131 xi = 1.0 / (1.0 - e_over_eprimed * abs(mu_0primed * zero_lag_live_time))
1132 xi = numpy.where(numpy.isnan(e_over_eprimed), 1.0, xi)
1133
1134 diagnostic_plot(xi, rate_data.rate_array.bins, r"Background Correction Factor $\xi$", rate_data.amplitude_lbl, "lalburst_power_final_xi.png")
1135
1136 # compute the rate upper limit
1137
1138 for xy in iterutils.MultiIter(*map(xrange, rate_data.rate_array.array.shape)):
1139 rate_data.rate_array.array[xy] = mu_p_epsilon(xi[xy], p)
1140 diagnostic_plot(rate_data.rate_array.array, rate_data.rate_array.bins, r"$\mu_{%.2g} \epsilon$" % p, rate_data.amplitude_lbl, "lalburst_power_final_mu_p_epsilon.png")
1141 rate_data.rate_array.array /= zero_lag_live_time * e
1142
1143 # done
1144
1145 return rate_data
1146
1147
1148#
1149# Plot the rate upper limit array
1150#
1151
1152
1154 print("plotting rate upper limit ...", file=sys.stderr)
1155
1156 #
1157 # contour plot in frequency-energy plane
1158 #
1159
1160 fig, axes = SnglBurstUtils.make_burst_plot("Centre Frequency (Hz)", rate_data.amplitude_lbl)
1161 axes.loglog()
1162
1163 xcoords, ycoords = rate_data.rate_array.centres()
1164 # zvals = log_{10}(R_{0.9} / 1 Hz)
1165 zvals = numpy.log10(numpy.clip(rate_data.rate_array.array, 0, 1) / 1.0)
1166 cset = axes.contour(xcoords, ycoords, numpy.transpose(zvals), 19)
1167 cset.clabel(inline = True, fontsize = 5, fmt = r"$%g$", colors = "k")
1168
1169 #axes.set_ylim((3e-17, 3e-10))
1170
1171 axes.set_title(r"%g\%% Confidence Rate Upper Limit ($\log_{10} R_{%g} / 1\,\mathrm{Hz}$)" % (100 * rate_data.confidence, rate_data.confidence))
1172
1173 #print >>sys.stderr, "writing lalburst_power_final_upper_limit_1.pdf ..."
1174 #fig.savefig("lalburst_power_final_upper_limit_1.pdf")
1175 print("writing lalburst_power_final_upper_limit_1.png ...", file=sys.stderr)
1176 fig.savefig("lalburst_power_final_upper_limit_1.png")
1177
1178 #
1179 # rate vs. energy curve at a sample frequency
1180 #
1181
1182 fig, axes = SnglBurstUtils.make_burst_plot(rate_data.amplitude_lbl, r"Rate Upper Limit (Hz)")
1183 axes.loglog()
1184
1185 zvals = rate_data.rate_array[110, :]
1186 max = min(zvals) * 1e4
1187 ycoords = numpy.compress(zvals <= max, ycoords)
1188 zvals = numpy.compress(zvals <= max, zvals)
1189 max = min(ycoords) * 1e6
1190 zvals = numpy.compress(ycoords <= max, zvals)
1191 ycoords = numpy.compress(ycoords <= max, ycoords)
1192 axes.plot(ycoords, zvals, "k-")
1193
1194 axes.set_title(r"%g\%% Confidence Rate Upper Limit at $110\,\mathrm{Hz}$" % (100 * rate_data.confidence))
1195
1196 #print >>sys.stderr, "writing lalburst_power_final_upper_limit_2.pdf ..."
1197 #fig.savefig("lalburst_power_final_upper_limit_2.pdf")
1198 print("writing lalburst_power_final_upper_limit_2.png ...", file=sys.stderr)
1199 fig.savefig("lalburst_power_final_upper_limit_2.png")
1200
1201
1202#
1203# =============================================================================
1204#
1205# Main
1206#
1207# =============================================================================
1208#
1209
1210
1211#
1212# Command line.
1213#
1214
1215
1216options, filenames = parse_command_line()
1217
1218
1219#
1220# The "amplitude" of a coinc, the statistic on which we threshold. This
1221# function is defined here, like this, so that the slope of the
1222# confidence-likelihood isodensity contours doesn't have to be carried
1223# around. For example, we can export this function to sqlite3 as a
1224# two-argument function so that we do not have to pass the slope parameter
1225# into SQL queries.
1226#
1227
1228
1229def coinc_detection_statistic(likelihood, confidence, m = options.confidence_contour_slope):
1230 # In the 2-D confidence--likelihood parameter space, the background
1231 # isodensity contours for high confidence, high likelihood,
1232 # coincident n-tuples are found to be approximated by the family
1233 # of curves given by
1234 #
1235 # likelihood = b * confidence^m
1236 #
1237 # or
1238 #
1239 # ln likelihood = m ln confidence + ln b
1240 #
1241 # where m is a constant and b parameterizes the family of curves.
1242 # Injections are found to have high "b" values, and noise low "b"
1243 # values. We use b (actually ln b) as the final, combined,
1244 # statistic measuring the "goodness" of a coincident n-tuple.
1245 # Given the likelihood ratio and confidence of a coincident
1246 # n-tuple, this function computes and returns ln b, which is
1247 # sometimes referred to as the "amplitude" of a coinc in this code
1248 # following the language of Brady et al.
1249
1250 if likelihood <= 0:
1251 # log() doesn't like 0, so we handle this case separately.
1252 return NegInf
1253
1254 if 1.0 / likelihood <= 0:
1255 # this time likelihood == +inf, which can happen when there
1256 # are regions of parameter space where no noise is ever,
1257 # *ever*, seen.
1258 return PosInf
1259
1260 return math.log(likelihood) - m * math.log(confidence)
1261
1262
1263#
1264# Preparatory work?
1265#
1266
1267
1268if options.dump_scatter_data:
1269 print("=== Confidence-Likelihood Scatter Dump ===", file=sys.stderr)
1270 print(file=sys.stderr)
1271 dump_confidence_likelihood_scatter_data(options.background_glob + options.injections_glob, live_time_program = options.live_time_program, tmp_path = options.tmp_space, verbose = options.verbose)
1272 print(file=sys.stderr)
1273 print("=== Done ===", file=sys.stderr)
1274 sys.exit(0)
1275
1276
1277if options.plot_scatter_data:
1278 print("=== Confidence-Likelihood Scatter Plot ===", file=sys.stderr)
1279 print(file=sys.stderr)
1280 plot_confidence_likelihood_scatter_data(options.confidence_contour_slope, verbose = options.verbose)
1281 print(file=sys.stderr)
1282 print("=== Done ===", file=sys.stderr)
1283 sys.exit(0)
1284
1285
1286#
1287# Accumulate the statistics required to extract rate vs. threshold
1288# information, and measure the amplitude of the n_survivors+1 loudest
1289# event.
1290#
1291
1292
1293print("=== Threshold ===", file=sys.stderr)
1294print(file=sys.stderr)
1295print("\t\tBOX IS %s" % (options.open_box and "OPEN!!" or "CLOSED!!"), file=sys.stderr)
1296print(file=sys.stderr)
1297
1298if options.verbose:
1299 print("building file list ...", file=sys.stderr)
1300filenames = sorted(filename for g in options.background_glob for filename in glob.glob(g))
1301if not filenames:
1302 raise ValueError("no background/zero lag files found")
1303
1304if True:
1305 rate_vs_threshold_data = measure_threshold(filenames, options.zero_lag_survivors, live_time_program = options.live_time_program, tmp_path = options.tmp_space, open_box = options.open_box, verbose = options.verbose)
1306 print_rate_vs_threshold_data(rate_vs_threshold_data, options.confidence_contour_slope)
1307 plot_rate_vs_threshold(rate_vs_threshold_data)
1308else:
1309 # fake the rate-vs-threshold data to skip straight to injections
1310 rate_vs_threshold_data = RateVsThresholdData()
1311 rate_vs_threshold_data.zero_lag_live_time = 1163382.623777472
1312 rate_vs_threshold_data.amplitude_threshold = 49.3975937091782
1313 rate_vs_threshold_data.mu_0primed = -2.000347702238217e-07
1314
1315print("done.", file=sys.stderr)
1316print(file=sys.stderr)
1317
1318
1319#
1320# Efficiency
1321#
1322
1323
1324print(file=sys.stderr)
1325print("=== Efficiency ==", file=sys.stderr)
1326print(file=sys.stderr)
1327
1328if options.verbose:
1329 print("building file list ...", file=sys.stderr)
1330filenames = sorted(filename for g in options.injections_glob for filename in glob.glob(g))
1331if not filenames:
1332 raise ValueError("no injection files found")
1333
1334efficiency_data = measure_efficiency(filenames, rate_vs_threshold_data.amplitude_threshold, live_time_program = options.live_time_program, upper_limit_scale = options.upper_limit_scale, tmp_path = options.tmp_space, verbose = options.verbose)
1335
1336print(file=sys.stderr)
1337print("=== Efficiency Summary ===", file=sys.stderr)
1338print(file=sys.stderr)
1339
1340plot_efficiency_data(efficiency_data)
1341
1342print("done.", file=sys.stderr)
1343print(file=sys.stderr)
1344
1345
1346#
1347# Rate upper limit
1348#
1349
1350
1351print(file=sys.stderr)
1352print("=== Rate Upper Limit ===", file=sys.stderr)
1353print(file=sys.stderr)
1354
1355rate_data = rate_upper_limit(efficiency_data, rate_vs_threshold_data.mu_0primed, rate_vs_threshold_data.zero_lag_live_time, options.upper_limit_confidence)
1356plot_rate_upper_limit(rate_data)
1357
1358print("done.", file=sys.stderr)
1359print(file=sys.stderr)
1360
1361
1362#
1363# Done
1364#
1365
1366
1367print(file=sys.stderr)
1368print("=== Done ===", file=sys.stderr)
1369print(file=sys.stderr)
static double min(double a, double b)
Definition: EPFilters.c:42
def add_contents(self, contents, threshold)
def __init__(self, efficiency_data, confidence)
def finish(self, zero_lag_survivors, open_box=False, verbose=False)
def update_from(self, contents, filename="", verbose=False)
def dump_confidence_likelihood_scatter_data(globs, live_time_program="lalapps_power", tmp_path=None, verbose=False)
def coinc_detection_statistic(likelihood, confidence, m=options.confidence_contour_slope)
def bb_id_likelihood_confidence_background(database)
def measure_threshold(filenames, n_survivors, live_time_program="lalapps_power", tmp_path=None, open_box=False, verbose=False)
def diagnostic_plot(z, bins, title, ylabel, filename)
def plot_rate_vs_threshold(data)
Input is a RateVsThresholdData instance.
def measure_efficiency(filenames, threshold, live_time_program="lalapps_power", upper_limit_scale="E", tmp_path=None, verbose=False)
def print_rate_vs_threshold_data(rate_vs_threshold_data, confidence_contour_slope)
def plot_rate_upper_limit(rate_data)
def create_sim_coinc_map_view(connection)
def mu_p_epsilon(xi, p)
Given the background correction factor $\xi$, and the upper limit confidence p, solve.
def plot_confidence_likelihood_scatter_data(slope, verbose=False)
def rate_upper_limit(efficiency_data, mu_0primed, zero_lag_live_time, p)
def plot_efficiency_data(efficiency_data)