Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALBurst 2.0.7.1-b246709
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Macros Modules Pages
burca.py
Go to the documentation of this file.
1# Copyright (C) 2006--2011,2013,2015--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#
21# Preamble
22#
23# =============================================================================
24#
25
26
27from bisect import bisect_left, bisect_right
28import itertools
29import math
30import sys
31
32
33from igwn_ligolw import lsctables
34from igwn_segments import PosInfinity
35from . import snglcoinc
36
37
38__author__ = "Kipp Cannon <kipp.cannon@ligo.org>"
39from .git_version import date as __date__
40from .git_version import version as __version__
41
42
43#
44# =============================================================================
45#
46# CoincTables Customizations
47#
48# =============================================================================
49#
50
51
52#
53# For use with excess power.
54#
55
56
57ExcessPowerBBCoincDef = lsctables.CoincDef(search = "excesspower", search_coinc_type = 0, description = "sngl_burst<-->sngl_burst coincidences")
58
59
61 def __init__(self, xmldoc):
62 super(ExcessPowerCoincTables, self).__init__(xmldoc)
63
64 # find the multi_burst table or create one if not found
65 try:
66 self.multibursttable = lsctables.MultiBurstTable.get_table(xmldoc)
67 except ValueError:
68 self.multibursttable = lsctables.New(lsctables.MultiBurstTable, ("process_id", "duration", "central_freq", "bandwidth", "snr", "confidence", "amplitude", "coinc_event_id"))
69 xmldoc.childNodes[0].appendChild(self.multibursttable)
70
71 def make_multi_burst(self, process_id, coinc_event_id, events, offset_vector):
72 multiburst = self.multibursttable.RowType(
73 process_id = process_id,
74 coinc_event_id = coinc_event_id,
75 # populate the false alarm rate with none.
76 false_alarm_rate = None
77 )
78
79 # snr = root sum of ms_snr squares
80
81 multiburst.snr = math.sqrt(sum(event.ms_snr**2.0 for event in events))
82
83 # peak time = ms_snr squared weighted average of peak times
84 # (no attempt to account for inter-site delays).
85 # LIGOTimeGPS objects don't like being multiplied by
86 # things, so the first event's peak time is used as a
87 # reference epoch.
88
89 t = events[0].peak + offset_vector[events[0].ifo]
90 multiburst.peak = t + sum(event.ms_snr**2.0 * float(event.peak + offset_vector[event.ifo] - t) for event in events) / multiburst.snr**2.0
91
92 # duration = ms_snr squared weighted average of durations
93
94 multiburst.duration = sum(event.ms_snr**2.0 * event.duration for event in events) / multiburst.snr**2.0
95
96 # central_freq = ms_snr squared weighted average of peak
97 # frequencies
98
99 multiburst.central_freq = sum(event.ms_snr**2.0 * event.peak_frequency for event in events) / multiburst.snr**2.0
100
101 # bandwidth = ms_snr squared weighted average of bandwidths
102
103 multiburst.bandwidth = sum(event.ms_snr**2.0 * event.bandwidth for event in events) / multiburst.snr**2.0
104
105 # confidence = minimum of confidences
106
107 multiburst.confidence = min(event.confidence for event in events)
108
109 # "amplitude" = h_rss of event with highest confidence
110
111 multiburst.amplitude = max(events, key = lambda event: event.ms_confidence).ms_hrss
112
113 # done
114
115 return multiburst
116
117 def coinc_rows(self, process_id, time_slide_id, events, table_name):
118 coinc, coincmaps = super(ExcessPowerCoincTables, self).coinc_rows(process_id, time_slide_id, events, table_name)
119 coinc.insts = (event.ifo for event in events)
120 return coinc, coincmaps, self.make_multi_burst(process_id, coinc.coinc_event_id, events, self.time_slide_index[time_slide_id])
121
122 def append_coinc(self, coinc, coincmaps, multiburst):
123 coinc = super(ExcessPowerCoincTables, self).append_coinc(coinc, coincmaps)
124 multiburst.coinc_event_id = coinc.coinc_event_id
125 self.multibursttable.append(multiburst)
126 return coinc
127
128
129#
130# For use with string cusp
131#
132
133
134StringCuspBBCoincDef = lsctables.CoincDef(search = "StringCusp", search_coinc_type = 0, description = "sngl_burst<-->sngl_burst coincidences")
135
136
138 def coinc_rows(self, process_id, time_slide_id, events, table_name):
139 coinc, coincmaps = super(StringCuspCoincTables, self).coinc_rows(process_id, time_slide_id, events, table_name)
140 coinc.insts = (event.ifo for event in events)
141 return coinc, coincmaps
142
143
144#
145# =============================================================================
146#
147# Coincidence Generators
148#
149# =============================================================================
150#
151
152
153#
154# For use with excess power coincidence test
155#
156
157
159 class singlesqueue(snglcoinc.coincgen_doubles.singlesqueue):
160 @staticmethod
161 def event_time(event):
162 return event.peak
163
164
165 class get_coincs(object):
166 @staticmethod
168 # the largest difference between any event's peak
169 # time and either its start or stop times
170 if not events:
171 return 0.0
172 return max(max(float(event.peak - event.start), float(event.start + event.duration - event.peak)) for event in events)
173
174 @staticmethod
175 def comparefunc(a, offseta, b, coinc_window):
176 if abs(a.central_freq - b.central_freq) > (a.bandwidth + b.bandwidth) / 2:
177 return True
178
179 astart = a.start + offseta
180 bstart = b.start
181 if astart > bstart + b.duration + coinc_window:
182 # a starts after the end of b
183 return True
184
185 if bstart > astart + a.duration + coinc_window:
186 # b starts after the end of a
187 return True
188
189 # time-frequency times intersect
190 return False
191
192 def __init__(self, events):
193 self.events = events
194 self.times = tuple(map(ep_coincgen_doubles.singlesqueue.event_time, events))
195 # for this instance, replace the method with the
196 # pre-computed value
198
199 def __call__(self, event_a, offset_a, coinc_window):
200 # event_a's peak time
201 peak = event_a.peak
202
203 # difference between event_a's peak and start times
204 dt = float(peak - event_a.start)
205
206 # largest difference between event_a's peak time
207 # and either its start or stop times
208 dt = max(dt, event_a.duration - dt)
209
210 # add our own max_edge_peak_delta and the light
211 # travel time between the two instruments (when
212 # done, if event_a's peak time differs by more than
213 # this much from the peak time of an event in this
214 # list then it is *impossible* for them to be
215 # coincident)
216 dt += self.max_edge_peak_deltamax_edge_peak_delta + coinc_window
217
218 # apply time shift
219 peak += offset_a
220
221 # extract the subset of events from this list that
222 # pass coincidence with event_a (use bisection
223 # searches for the minimum and maximum allowed peak
224 # times to quickly identify a subset of the full
225 # list)
226 return [event_b for event_b in self.events[bisect_left(self.times, peak - dt) : bisect_right(self.times, peak + dt)] if not self.comparefunc(event_a, offset_a, event_b, coinc_window)]
227
228
229
230#
231# For use with string coincidence test
232#
233
234
236 class get_coincs(object):
237 def __init__(self, events):
238 self.events = events
239 self.times = tuple(map(string_coincgen_doubles.singlesqueue.event_time, events))
240
241 def __call__(self, event_a, offset_a, coinc_window):
242 peak = event_a.peak + offset_a
243 template_id = event_a.template_id
244 return [event for event in self.events[bisect_left(self.times, peak - coinc_window) : bisect_right(self.times, peak + coinc_window)] if event.template_id == template_id]
245
246
247#
248# =============================================================================
249#
250# Library API
251#
252# =============================================================================
253#
254
255
257 xmldoc,
258 process_id,
259 coincgen_doubles,
260 CoincTables,
261 coinc_definer_row,
262 delta_t,
263 ntuple_comparefunc = lambda events, offset_vector: False,
264 min_instruments = 2,
265 incremental = True,
266 verbose = False
267):
268 #
269 # prepare the coincidence table interface.
270 #
271
272 if verbose:
273 print("indexing ...", file=sys.stderr)
274 coinc_tables = CoincTables(xmldoc, coinc_definer_row)
275
276 #
277 # construct offset vector assembly graph
278 #
279
280 time_slide_graph = snglcoinc.TimeSlideGraph(coincgen_doubles, coinc_tables.time_slide_index, delta_t, min_instruments = min_instruments, verbose = verbose)
281
282 #
283 # collect events.
284 #
285
286 sngl_burst_table = lsctables.SnglBurstTable.get_table(xmldoc)
287 if not incremental:
288 # normal version: push everything into the graph, then
289 # pull out all coincs in one operation below using the
290 # final flush
291 for instrument, events in itertools.groupby(sorted(sngl_burst_table, key = lambda row: row.ifo), lambda event: event.ifo):
292 time_slide_graph.push(instrument, tuple(events), PosInfinity)
293 else:
294 # slower diagnostic version. simulate an online
295 # incremental analysis by pushing events into the graph in
296 # time order and collecting candidates as we go. we still
297 # do the final flush operation below.
298 for instrument, events in itertools.groupby(sorted(sngl_burst_table, key = lambda row: (row.peak, row.ifo)), lambda event: event.ifo):
299 events = tuple(events)
300 if time_slide_graph.push(instrument, events, max(event.peak for event in events)):
301 for node, events in time_slide_graph.pull(coinc_sieve = ntuple_comparefunc):
302 coinc_tables.append_coinc(*coinc_tables.coinc_rows(process_id, node.time_slide_id, events, "sngl_burst"))
303
304 #
305 # retrieve all remaining coincidences.
306 #
307
308 for node, events in time_slide_graph.pull(coinc_sieve = ntuple_comparefunc, flush = True):
309 coinc_tables.append_coinc(*coinc_tables.coinc_rows(process_id, node.time_slide_id, events, "sngl_burst"))
310
311 #
312 # done
313 #
314
315 return xmldoc
static double max(double a, double b)
Definition: EPFilters.c:43
static double min(double a, double b)
Definition: EPFilters.c:42
def make_multi_burst(self, process_id, coinc_event_id, events, offset_vector)
Definition: burca.py:71
def __init__(self, xmldoc)
Definition: burca.py:61
def append_coinc(self, coinc, coincmaps, multiburst)
Appends the coinc_event row object and coinc_event_map row objects to the coinc_event and coinc_event...
Definition: burca.py:122
def coinc_rows(self, process_id, time_slide_id, events, table_name)
From a process ID, a time slide ID, and a sequence of events (generator expressions are OK),...
Definition: burca.py:117
def coinc_rows(self, process_id, time_slide_id, events, table_name)
From a process ID, a time slide ID, and a sequence of events (generator expressions are OK),...
Definition: burca.py:138
def __call__(self, event_a, offset_a, coinc_window)
Definition: burca.py:199
def comparefunc(a, offseta, b, coinc_window)
Definition: burca.py:175
def __call__(self, event_a, offset_a, coinc_window)
Definition: burca.py:241
A convenience interface to the XML document's coincidence tables, allowing for easy addition of coinc...
Definition: snglcoinc.py:1470
Using a pair of singlesqueue objects, constructs pairs of coincident events from streams of partially...
Definition: snglcoinc.py:578
def burca(xmldoc, process_id, coincgen_doubles, CoincTables, coinc_definer_row, delta_t, ntuple_comparefunc=lambda events, False offset_vector, min_instruments=2, incremental=True, verbose=False)
Definition: burca.py:267