Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALBurst 2.0.7.1-b246709
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Macros Modules Pages
SnglBurstUtils.py
Go to the documentation of this file.
1# Copyright (C) 2005-2013,2016,2017-2019,2021 Kipp Cannon
2#
3# This program is free software; you can redistribute it and/or modify it
4# under the terms of the GNU General Public License as published by the
5# Free Software Foundation; either version 2 of the License, or (at your
6# option) any later version.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11# Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program; if not, write to the Free Software Foundation, Inc.,
15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17#
18# =============================================================================
19#
20# Preamble
21#
22# =============================================================================
23#
24
25
26import itertools
27import math
28import matplotlib
29matplotlib.rcParams.update({
30 "font.size": 8.0,
31 "axes.titlesize": 10.0,
32 "axes.labelsize": 10.0,
33 "xtick.labelsize": 8.0,
34 "ytick.labelsize": 8.0,
35 "legend.fontsize": 8.0,
36 "figure.dpi": 600,
37 "savefig.dpi": 600,
38 "text.usetex": True # render all text with TeX
39})
40from matplotlib import figure
41from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
42import re
43import sys
44
45
46from igwn_ligolw import lsctables
47from igwn_ligolw import dbtables
48from igwn_ligolw.utils import search_summary as ligolw_search_summary
49from igwn_ligolw.utils import segments as ligolw_segments
50from .offsetvector import offsetvector
51
52
53__author__ = "Kipp Cannon <kipp.cannon@ligo.org>"
54from .git_version import date as __date__
55from .git_version import version as __version__
56
57
58#
59# =============================================================================
60#
61# Database
62#
63# =============================================================================
64#
65
66
67class CoincDatabase(object):
68 def __init__(self, connection, live_time_program, search = "excesspower", veto_segments_name = None):
69 """
70 Compute and record some summary information about the
71 database. Call this after all the data has been inserted,
72 and before you want any of this information.
73 """
74
75 self.connection = connection
76 self.xmldoc = dbtables.get_xml(connection)
77
78 # find the tables
79 try:
80 self.sngl_burst_table = lsctables.SnglBurstTable.get_table(self.xmldoc)
81 except ValueError:
82 self.sngl_burst_table = None
83 try:
84 self.sim_burst_table = lsctables.SimBurstTable.get_table(self.xmldoc)
85 except ValueError:
86 self.sim_burst_table = None
87 try:
88 self.coinc_def_table = lsctables.CoincDefTable.get_table(self.xmldoc)
89 self.coinc_table = lsctables.CoincTable.get_table(self.xmldoc)
90 self.time_slide_table = lsctables.TimeSlideTable.get_table(self.xmldoc)
91 except ValueError:
92 self.coinc_def_table = None
93 self.coinc_table = None
94 self.time_slide_table = None
95 try:
96 self.multi_burst_table = lsctables.MultiBurstTable.get_table(self.xmldoc)
97 except ValueError:
98 self.multi_burst_table = None
99
100 # get the segment lists
101 self.seglists = ligolw_search_summary.segmentlistdict_fromsearchsummary(self.xmldoc, live_time_program).coalesce()
102 self.instruments = set(self.seglists.keys())
103 if veto_segments_name is not None:
104 self.vetoseglists = ligolw_segments.segmenttable_get_by_name(self.xmldoc, veto_segments_name).coalesce()
105 else:
106 self.vetoseglists = ligolw_segments.segments.segmentlistdict()
107
108 # determine a few coinc_definer IDs
109 # FIXME: don't hard-code the numbers
110 if self.coinc_def_table is not None:
111 try:
112 self.bb_definer_id = self.coinc_def_table.get_coinc_def_id(search, 0, create_new = False)
113 except KeyError:
114 self.bb_definer_id = None
115 try:
116 self.sb_definer_id = self.coinc_def_table.get_coinc_def_id(search, 1, create_new = False)
117 except KeyError:
118 self.sb_definer_id = None
119 try:
120 self.sce_definer_id = self.coinc_def_table.get_coinc_def_id(search, 2, create_new = False)
121 except KeyError:
122 self.sce_definer_id = None
123 try:
124 self.scn_definer_id = self.coinc_def_table.get_coinc_def_id(search, 3, create_new = False)
125 except KeyError:
126 self.scn_definer_id = None
127 else:
128 self.bb_definer_id = None
129 self.sb_definer_id = None
130 self.sce_definer_id = None
131 self.scn_definer_id = None
132
133
134 def get_noninjections(self):
135 """
136 Generator function to return
137
138 is_background, event_list, offsetvector
139
140 tuples by querying the coinc_event and sngl_burst tables in
141 the database. Only coincs corresponding to
142 sngl_burst<-->sngl_burst coincs will be retrieved.
143 """
144 cursor = self.connection.cursor()
145 for coinc_event_id, time_slide_id in self.connection.cursor().execute("""
146 SELECT
147 coinc_event_id,
148 time_slide_id
149 FROM
150 coinc_event
151 WHERE
152 coinc_def_id == ?
153 """, (self.bb_definer_id,)):
154 rows = [(self.sngl_burst_table.row_from_cols(row), row[-1]) for row in cursor.execute("""
155 SELECT
156 sngl_burst.*,
157 time_slide.offset
158 FROM
159 coinc_event_map
160 JOIN sngl_burst ON (
161 coinc_event_map.table_name == 'sngl_burst'
162 AND sngl_burst.event_id == coinc_event_map.event_id
163 )
164 JOIN time_slide ON (
165 time_slide.instrument == sngl_burst.ifo
166 )
167 WHERE
168 coinc_event_map.coinc_event_id == ?
169 AND time_slide.time_slide_id == ?
170 """, (coinc_event_id, time_slide_id))]
171 offsets = offsetvector((event.ifo, offset) for event, offset in rows)
172 yield any(offsets.values()), [event for event, offset in rows], offsets
173 cursor.close()
174
175
176 def get_injections(self):
177 """
178 Generator function to return
179
180 sim, event_list, offsetvector
181
182 tuples by querying the sim_burst, coinc_event and
183 sngl_burst tables in the database. Only coincs
184 corresponding to "exact" sim_burst<-->coinc_event coincs
185 will be retrieved.
186 """
187 cursor = self.connection.cursor()
188 for values in self.connection.cursor().execute("""
189 SELECT
190 sim_burst.*,
191 burst_coinc_event_map.event_id
192 FROM
193 sim_burst
194 JOIN coinc_event_map AS sim_coinc_event_map ON (
195 sim_coinc_event_map.table_name == 'sim_burst'
196 AND sim_coinc_event_map.event_id == sim_burst.simulation_id
197 )
198 JOIN coinc_event AS sim_coinc_event ON (
199 sim_coinc_event.coinc_event_id == sim_coinc_event_map.coinc_event_id
200 )
201 JOIN coinc_event_map AS burst_coinc_event_map ON (
202 burst_coinc_event_map.coinc_event_id == sim_coinc_event_map.coinc_event_id
203 AND burst_coinc_event_map.table_name == 'coinc_event'
204 )
205 WHERE
206 sim_coinc_event.coinc_def_id == ?
207 """, (self.sce_definer_id,)):
208 # retrieve the injection and the coinc_event_id
209 sim = self.sim_burst_table.row_from_cols(values)
210 coinc_event_id = values[-1]
211
212 # retrieve the list of the sngl_bursts in this
213 # coinc, and their time slide dictionary
214 rows = [(self.sngl_burst_table.row_from_cols(row), row[-1]) for row in cursor.execute("""
215 SELECT
216 sngl_burst.*,
217 time_slide.offset
218 FROM
219 sngl_burst
220 JOIN coinc_event_map ON (
221 coinc_event_map.table_name == 'sngl_burst'
222 AND coinc_event_map.event_id == sngl_burst.event_id
223 )
224 JOIN coinc_event ON (
225 coinc_event.coinc_event_id == coinc_event_map.coinc_event_id
226 )
227 JOIN time_slide ON (
228 coinc_event.time_slide_id == time_slide.time_slide_id
229 AND time_slide.instrument == sngl_burst.ifo
230 )
231 WHERE
232 coinc_event.coinc_event_id == ?
233 """, (coinc_event_id,))]
234 # pass the events to whatever wants them
235 yield sim, [event for event, offset in rows], offsetvector((event.ifo, offset) for event, offset in rows)
236 cursor.close()
237
238
239def summarize_coinc_database(contents, filename = None):
240 if filename is None:
241 filename = ""
242 else:
243 filename = "%s: " % filename
244 cursor = contents.connection.cursor()
245 print("%sdatabase stats:" % filename, file=sys.stderr)
246 for instrument, seglist in sorted(contents.seglists.items()):
247 print("\t%s%s livetime: %g s (%g%% vetoed)" % (filename, instrument, abs(seglist), 100.0 * float(abs(instrument in contents.vetoseglists and (seglist & contents.vetoseglists[instrument]) or 0.0)) / float(abs(seglist))), file=sys.stderr)
248 if contents.sngl_burst_table is not None:
249 print("\t%sburst events: %d" % (filename, len(contents.sngl_burst_table)), file=sys.stderr)
250 if contents.sim_burst_table is not None:
251 print("\t%sburst injections: %d" % (filename, len(contents.sim_burst_table)), file=sys.stderr)
252 if contents.time_slide_table is not None:
253 print("\t%stime slides: %d" % (filename, cursor.execute("SELECT COUNT(DISTINCT(time_slide_id)) FROM time_slide").fetchone()[0]), file=sys.stderr)
254 if contents.coinc_def_table is not None:
255 for description, n in cursor.execute("SELECT description, COUNT(*) FROM coinc_definer NATURAL JOIN coinc_event GROUP BY coinc_def_id ORDER BY description"):
256 print("\t%s%s: %d" % (filename, description, n), file=sys.stderr)
257 cursor.close()
258
259
260def coinc_sngl_bursts(contents, coinc_event_id):
261 for values in contents.connection.cursor().execute("""
262SELECT sngl_burst.* FROM
263 sngl_burst
264 JOIN coinc_event_map ON (
265 sngl_burst.event_id == coinc_event_map.event_id
266 AND coinc_event_map.table_name == 'sngl_burst'
267 )
268WHERE
269 coinc_event_map.coinc_event_id == ?
270 """, (coinc_event_id,)):
271 yield contents.sngl_burst_table.row_from_cols(values)
272
273
274#
275# =============================================================================
276#
277# Live Time Tools
278#
279# =============================================================================
280#
281
282
283def get_time_slides(connection):
284 """
285 Query the database for the IDs and offsets of all time slides, and
286 return two dictionaries one containing the all-zero time slides and
287 the other containing the not-all-zero time slides.
288 """
289 time_slides = dbtables.TimeSlideTable(connection = connection).as_dict()
290
291 zero_lag_time_slides = dict((time_slide_id, offsetvector) for time_slide_id, offsetvector in time_slides.items() if not any(offsetvector.values()))
292 background_time_slides = dict((time_slide_id, offsetvector) for time_slide_id, offsetvector in time_slides.items() if any(offsetvector.values()))
293
294 return zero_lag_time_slides, background_time_slides
295
296
297def time_slides_livetime(seglists, time_slides, verbose = False):
298 """
299 Given a sequence of time slides (each of which is an instrument -->
300 offset dictionary), use the segmentlistdict dictionary of segment
301 lists to compute the live time in each time slide. Return the sum
302 of the live times from all slides.
303 """
304 livetime = 0.0
305 old_offsets = seglists.offsets.copy()
306 N = len(time_slides)
307 if verbose:
308 print("computing the live time for %d time slides:" % N, file=sys.stderr)
309 for n, time_slide in enumerate(time_slides):
310 if verbose:
311 print("\t%.1f%%\r" % (100.0 * n / N), end=' ', file=sys.stderr)
312 seglists.offsets.update(time_slide)
313 livetime += float(abs(seglists.intersection(time_slide.keys())))
314 seglists.offsets.update(old_offsets)
315 if verbose:
316 print("\t100.0%", file=sys.stderr)
317 return livetime
318
319
320#
321# =============================================================================
322#
323# TeX Helpers
324#
325# =============================================================================
326#
327
328
329floatpattern = re.compile("([+-]?[.0-9]+)[Ee]([+-]?[0-9]+)")
330
331def latexnumber(s):
332 """
333 Convert a string of the form "d.dddde-dd" to "d.dddd \\times
334 10^{-dd}"
335
336 Example:
337
338 >>> import math
339 >>> print latexnumber("%.16e" % math.pi)
340 3.1415926535897931 \\times 10^{0}
341 """
342 m, e = floatpattern.match(s).groups()
343 return r"%s \times 10^{%d}" % (m, int(e))
344
345
346#
347# =============================================================================
348#
349# Plots
350#
351# =============================================================================
352#
353
354
355def make_burst_plot(x_label, y_label, width = 165.0):
356 """
357 width is in mm
358 """
359 fig = figure.Figure()
360 FigureCanvas(fig)
361 # width mm wide, golden ratio high
362 fig.set_size_inches(width / 25.4, width / 25.4 / ((1 + math.sqrt(5)) / 2))
363 axes = fig.gca()
364 axes.grid(True)
365 axes.set_xlabel(x_label)
366 axes.set_ylabel(y_label)
367 return fig, axes
def get_injections(self)
Generator function to return.
def __init__(self, connection, live_time_program, search="excesspower", veto_segments_name=None)
Compute and record some summary information about the database.
def get_noninjections(self)
Generator function to return.
Subclass of the dict built-in type for storing mappings of instrument to time offset.
Definition: offsetvector.py:56
def latexnumber(s)
Convert a string of the form "d.dddde-dd" to "d.dddd \\times 10^{-dd}".
def get_time_slides(connection)
Query the database for the IDs and offsets of all time slides, and return two dictionaries one contai...
def coinc_sngl_bursts(contents, coinc_event_id)
def summarize_coinc_database(contents, filename=None)
def make_burst_plot(x_label, y_label, width=165.0)
width is in mm
def time_slides_livetime(seglists, time_slides, verbose=False)
Given a sequence of time slides (each of which is an instrument --> offset dictionary),...