Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALBurst 2.0.7.1-8a6b96f
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Macros Modules Pages
SimBurstUtils.py
Go to the documentation of this file.
1# Copyright (C) 2007,2016--2018,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
27import math
28import sys
29
30
31import lal
32from lal import rate
33
34
35from . import SnglBurstUtils
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# Misc.
47#
48# =============================================================================
49#
50
51
52def on_instruments(sim, seglists, offsetvector):
53 """
54 Return a set of the names of the instruments that were on at the
55 time of the injection.
56 """
57 return set(instrument for instrument, seglist in seglists.items() if sim.time_at_instrument(instrument, offsetvector) in seglist)
58
59
60#
61# =============================================================================
62#
63# Amplitudes in Instrument
64#
65# =============================================================================
66#
67
68
69def hrss_in_instrument(sim, instrument, offsetvector):
70 """
71 Given an injection and an instrument, compute and return the h_rss
72 of the injection as should be observed in the instrument. That is,
73 project the waveform onto the instrument, and return the root
74 integrated strain squared.
75 """
76 # FIXME: this function is really only correct for sine-Gaussian
77 # injections. that's OK because I only quote sensitivities in
78 # units of hrss when discussing sine-Gaussians.
79 #
80 # the problem is the following. first,
81 #
82 # h = F+ h+ + Fx hx
83 #
84 # so
85 #
86 # h^{2} = F+^2 h+^2 + Fx^2 hx^2 + 2 F+ Fx h+ hx
87 #
88 # which means to calculate the hrss in the instrument you need to
89 # know: mean-square h in the + polarization, mean-square h in the
90 # x polarization, and the correlation between the polarizations <h+
91 # hx>. these could be recorded in the sim_burst table, but they
92 # aren't at present.
93
94 # semimajor and semiminor axes of polarization ellipse
95
96 a = 1.0 / math.sqrt(2.0 - sim.pol_ellipse_e**2)
97 b = a * math.sqrt(1.0 - sim.pol_ellipse_e**2)
98
99 # hrss in plus and cross polarizations
100
101 hplusrss = sim.hrss * (a * math.cos(sim.pol_ellipse_angle) - b * math.sin(sim.pol_ellipse_angle))
102 hcrossrss = sim.hrss * (b * math.cos(sim.pol_ellipse_angle) + a * math.sin(sim.pol_ellipse_angle))
103
104 # antenna response factors
105
106 fplus, fcross = lal.ComputeDetAMResponse(
107 lal.cached_detector_by_prefix[instrument].response,
108 sim.ra,
109 sim.dec,
110 sim.psi,
111 lal.GreenwichMeanSiderealTime(sim.time_at_instrument(instrument, offsetvector))
112 )
113
114 # hrss in detector
115
116 return math.sqrt((fplus * hplusrss)**2 + (fcross * hcrossrss)**2)
117
118
119def string_amplitude_in_instrument(sim, instrument, offsetvector):
120 """
121 Given a string cusp injection and an instrument, compute and return
122 the amplitude of the injection as should be observed in the
123 instrument.
124 """
125 assert sim.waveform == "StringCusp"
126
127 # antenna response factors
128
129 fplus, fcross = lal.ComputeDetAMResponse(
130 lal.cached_detector_by_prefix[instrument].response,
131 sim.ra,
132 sim.dec,
133 sim.psi,
134 lal.GreenwichMeanSiderealTime(sim.time_at_instrument(instrument, offsetvector))
135 )
136
137 # amplitude in detector
138
139 return fplus * sim.amplitude
140
141
142#
143# =============================================================================
144#
145# Efficiency Contours
146#
147# =============================================================================
148#
149
150
151#
152# typical width of a string cusp signal's autocorrelation function when the
153# signal is normalized to the interferometer noise (whitened). this is
154# used to provide a window around each injection for the purpose of
155# identifying burst events that match the injection. this number is
156# O(1/f_{bucket}).
157#
158
159
160stringcusp_autocorrelation_width = .016 # seconds
161
162
163#
164# used to find burst events near injections for the purpose of producing a
165# short list of coinc events for use in more costly comparisons
166#
167
168
169burst_is_near_injection_window = 2.0 # seconds
170
171
172#
173# h_{rss} vs. peak frequency
174#
175
176
178 def __init__(self, instruments, amplitude_func, amplitude_lbl, error):
179 self.instruments = set(instruments)
180 self.amplitude_func = amplitude_func
181 self.amplitude_lbl = amplitude_lbl
182 self.error = error
183 self.injected_x = []
184 self.injected_y = []
185 self.found_x = []
186 self.found_y = []
187
188
189 def add_contents(self, contents):
190 # FIXME: the first left outer join can yield multiple
191 # rows.
192 cursor = contents.connection.cursor()
193 for values in contents.connection.cursor().execute("""
194SELECT
195 sim_burst.*,
196 coinc_event.coinc_event_id
197FROM
198 sim_burst
199 -- The rest of this join can yield at most 1 row for each sim_burst
200 -- row
201 LEFT OUTER JOIN coinc_event_map ON (
202 coinc_event_map.table_name == 'sim_burst'
203 AND coinc_event_map.event_id == sim_burst.simulation_id
204 )
205 LEFT OUTER JOIN coinc_event ON (
206 coinc_event.coinc_event_id == coinc_event_map.coinc_event_id
207 )
208WHERE
209 coinc_event.coinc_def_id == ?
210 """, (contents.sb_definer_id,)):
211 sim = contents.sim_burst_table.row_from_cols(values)
212 coinc_event_id = values[-1]
213 instruments = set(cursor.execute("""
214SELECT
215 sngl_burst.ifo
216FROM
217 coinc_event_map
218 JOIN sngl_burst ON (
219 coinc_event_map.table_name == 'sngl_burst'
220 AND coinc_event_map.event_id == sngl_burst.event_id
221 )
222WHERE
223 coinc_event_map.coinc_event_id == ?
224 """, (coinc_event_id,)))
225 found = self.instruments.issubset(instruments)
226 # FIXME: this following assumes all injections are
227 # done at zero lag (which is correct, for now, but
228 # watch out for this)
229 if injection_was_made(sim, contents.seglists, self.instruments):
230 for instrument in self.instruments:
231 amplitude = self.amplitude_func(sim, instrument)
232 self.injected_x.append(sim.frequency)
233 self.injected_y.append(amplitude)
234 if found:
235 self.found_x.append(sim.frequency)
236 self.found_y.append(amplitude)
237 elif found:
238 print("odd, injection %s was found in %s but not injected..." % (sim.simulation_id, "+".join(self.instruments)), file=sys.stderr)
239
240 def _bin_events(self, binning = None):
241 # called internally by finish()
242 if binning is None:
243 minx, maxx = min(self.injected_x), max(self.injected_x)
244 miny, maxy = min(self.injected_y), max(self.injected_y)
245 binning = rate.NDBins((rate.LogarithmicBins(minx, maxx, 256), rate.LogarithmicBins(miny, maxy, 256)))
246
247 self.efficiency = rate.BinnedRatios(binning)
248
249 for xy in zip(self.injected_x, self.injected_y):
250 self.efficiency.incdenominator(xy)
251 for xy in zip(self.found_x, self.found_y):
252 self.efficiency.incnumerator(xy)
253
254 # 1 / error^2 is the number of injections that need to be
255 # within the window in order for the fractional uncertainty
256 # in that number to be = error. multiplying by
257 # bins_per_inj tells us how many bins the window needs to
258 # cover, and taking the square root translates that into
259 # the window's length on a side in bins. because the
260 # contours tend to run parallel to the x axis, the window
261 # is dilated in that direction to improve resolution.
262
263 bins_per_inj = self.efficiency.used() / float(len(self.injected_x))
264 self.window_size_x = self.window_size_y = math.sqrt(bins_per_inj / self.error**2)
265 self.window_size_x *= math.sqrt(2)
266 self.window_size_y /= math.sqrt(2)
267 if self.window_size_x > 100 or self.window_size_y > 100:
268 # program will take too long to run
269 raise ValueError("smoothing filter too large (not enough injections)")
270
271 print("The smoothing window for %s is %g x %g bins" % ("+".join(self.instruments), self.window_size_x, self.window_size_y), end=' ', file=sys.stderr)
272 print("which is %g%% x %g%% of the binning" % (100.0 * self.window_size_x / binning[0].n, 100.0 * self.window_size_y / binning[1].n), file=sys.stderr)
273
274 def finish(self, binning = None):
275 # compute the binning if needed, and set the injections
276 # into the numerator and denominator bins. also compute
277 # the smoothing window's parameters.
278
279 self._bin_events(binning)
280
281 # smooth the efficiency data.
282 print("Sum of numerator bins before smoothing = %g" % self.efficiency.numerator.array.sum(), file=sys.stderr)
283 print("Sum of denominator bins before smoothing = %g" % self.efficiency.denominator.array.sum(), file=sys.stderr)
284 rate.filter_binned_ratios(self.efficiency, rate.gaussian_window(self.window_size_x, self.window_size_y))
285 print("Sum of numerator bins after smoothing = %g" % self.efficiency.numerator.array.sum(), file=sys.stderr)
286 print("Sum of denominator bins after smoothing = %g" % self.efficiency.denominator.array.sum(), file=sys.stderr)
287
288 # regularize to prevent divide-by-zero errors
289 self.efficiency.regularize()
290
291
292def plot_Efficiency_hrss_vs_freq(efficiency):
293 """
294 Generate a plot from an Efficiency_hrss_vs_freq instance.
295 """
296 fig, axes = SnglBurstUtils.make_burst_plot("Frequency (Hz)", efficiency.amplitude_lbl)
297 axes.loglog()
298
299 xcoords, ycoords = efficiency.efficiency.centres()
300 zvals = efficiency.efficiency.ratio()
301 cset = axes.contour(xcoords, ycoords, zvals.T, (.1, .2, .3, .4, .5, .6, .7, .8, .9))
302 cset.clabel(inline = True, fontsize = 5, fmt = r"$%%g \pm %g$" % efficiency.error, colors = "k")
303 axes.set_title(r"%s Injection Detection Efficiency (%d of %d Found)" % ("+".join(sorted(efficiency.instruments)), len(efficiency.found_x), len(efficiency.injected_x)))
304 return fig
305
306
307#
308# =============================================================================
309#
310# Source Co-ordinates
311#
312# =============================================================================
313#
314
315
316#
317# Location of the galactic core
318# ra = 27940.04 s = 7 h 45 m 40.04 s
319# dec = -29o 00' 28.1"
320#
321
322
323MW_CENTER_J2000_RA_RAD = 2.0318570464121519
324MW_CENTER_J2000_DEC_RAD = -0.50628171572274738
static double max(double a, double b)
Definition: EPFilters.c:43
static double min(double a, double b)
Definition: EPFilters.c:42
def __init__(self, instruments, amplitude_func, amplitude_lbl, error)
def on_instruments(sim, seglists, offsetvector)
Return a set of the names of the instruments that were on at the time of the injection.
def string_amplitude_in_instrument(sim, instrument, offsetvector)
Given a string cusp injection and an instrument, compute and return the amplitude of the injection as...
def plot_Efficiency_hrss_vs_freq(efficiency)
Generate a plot from an Efficiency_hrss_vs_freq instance.
def hrss_in_instrument(sim, instrument, offsetvector)
Given an injection and an instrument, compute and return the h_rss of the injection as should be obse...