1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 from __future__ import division
24 import re,numpy,math,subprocess,scipy,sys
25
26 from glue.ligolw import ligolw,table,lsctables,utils
27 from glue.ligolw.utils import process as ligolw_process
28 from glue import segments
29
30 from pylal.xlal.datatypes.ligotimegps import LIGOTimeGPS
31 import lal as XLALConstants
32 from pylal.dq import dqTriggerUtils
33
34 from matplotlib import use
35 use('Agg')
36 from pylab import hanning
37
38
39 import warnings
40 warnings.filterwarnings("ignore")
41 from scipy import signal as signal
42 from scipy import interpolate
43
44 from matplotlib import mlab
45 from glue import git_version
46
47 __author__ = "Duncan Macleod <duncan.macleod@astro.cf.ac.uk>"
48 __version__ = "git id %s" % git_version.id
49 __date__ = git_version.date
50
51 """
52 This module provides a bank of useful functions for manipulating triggers and trigger files for data quality investigations.
53 """
54
55
56
57
58
60
61 """
62 Execute shell command and capture standard output and errors.
63 Returns tuple "(stdout,stderr)".
64 """
65
66 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
67 shell=isinstance(command, str))
68 out, err = p.communicate()
69
70 return out,err
71
72
73
74
75
77
78 """
79 Read generic injection file object file containing injections of the given
80 type string. Returns an 'Sim' lsctable of the corresponding type.
81
82 Arguments:
83
84 file : file object
85 type : [ "inspiral" | "burst" | "ringdown" ]
86
87 Keyword arguments:
88
89 ifo : [ "G1" | "H1" | "H2" | "L1" | "V1" ]
90 """
91
92
93 type = type.lower()
94
95
96 xml = re.compile('(xml$|xml.gz$)')
97 if re.search(xml,file.name):
98 xmldoc,digest = utils.load_fileobj(file)
99 injtable = table.get_table(xmldoc,'sim_%s:table' % (type))
100
101
102 else:
103 cchar = re.compile('[#%<!()_\[\]{}:;\'\"]+')
104
105
106 injtable = lsctables.New(lsctables.__dict__['Sim%sTable' % (type.title())])
107 if type=='inspiral':
108 columns = ['geocent_end_time.geocent_end_time_ns',\
109 'h_end_time.h_end_time_ns',\
110 'l_end_time.l_end_time_ns',\
111 'v_end_time.v_end_time_ns',\
112 'distance']
113 for line in file.readlines():
114 if re.match(cchar,line):
115 continue
116
117 inj = lsctables.SimInspiral()
118
119 sep = re.compile('[\s,=]+')
120 data = sep.split(line)
121
122 inj.geocent_end_time = int(data[0].split('.')[0])
123 inj.geocent_end_time_ns = int(data[0].split('.')[1])
124 inj.h_end_time = int(data[1].split('.')[0])
125 inj.h_end_time_ns = int(data[1].split('.')[1])
126 inj.l_end_time = int(data[2].split('.')[0])
127 inj.l_end_time_ns = int(data[2].split('.')[1])
128 inj.v_end_time = int(data[3].split('.')[0])
129 inj.v_end_time_ns = int(data[3].split('.')[1])
130 inj.distance = float(data[4])
131
132 injtable.append(inj)
133
134 if type=='burst':
135 if file.readlines()[0].startswith('filestart'):
136
137 file.seek(0)
138
139 snrcol = { 'G1':23, 'H1':19, 'L1':21, 'V1':25 }
140
141 for line in file.readlines():
142 inj = lsctables.SimBurst()
143
144 sep = re.compile('[\s,=]+')
145 data = sep.split(line)
146
147
148
149 if 'burstgps' in data:
150 idx = data.index('burstgps')+1
151 geocent = LIGOTimeGPS(data[idx])
152
153 inj.time_geocent_gps = geocent.seconds
154 inj.time_geocent_gps_ns = geocent.nanoseconds
155 else:
156 continue
157
158
159
160
161
162
163 if 'freq' in data:
164 idx = data.index('freq')+1
165 inj.frequency = float(data[idx])
166 else:
167 continue
168
169
170 if ifo and 'snr%s' % ifo in data:
171 idx = data.index('snr%s' % ifo)+1
172 inj.amplitude = float(data[idx])
173 elif 'rmsSNR' in data:
174 idx = data.index('rmsSNR')+1
175 inj.amplitude = float(data[idx])
176 else:
177 continue
178
179 if 'phi' in data:
180 idx = data.index('phi' )+1
181 inj.ra = float(data[idx])*24/(2*math.pi)
182
183 if 'theta' in data:
184 idx = data.index('theta' )+1
185 inj.ra = 90-(float(data[idx])*180/math.pi)
186
187 if ifo and 'hrss%s' % ifo in data:
188 idx = data.index('hrss%s' % ifo)+1
189 inj.hrss = float(data[idx])
190 elif 'hrss' in data:
191 idx = data.index('hrss')+1
192 inj.hrss = float(data[idx])
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207 injtable.append(inj)
208
209 else:
210
211 file.seek(0)
212 for line in file.readlines():
213 inj = lsctables.SimBurst()
214
215 sep = re.compile('[\s,]+')
216 data = sep.split(line)
217
218 geocent = LIGOTimeGPS(data[0])
219 inj.time_geocent_gps = geocent.seconds
220 inj.time_geocent_gps_ns = geocent.nanoseconds
221
222 injtable.append(inj)
223
224 injections = table.new_from_template(injtable)
225 if not start: start = 0
226 if not end: end = 9999999999
227 span = segments.segmentlist([ segments.segment(start, end) ])
228 get_time = dqTriggerUtils.def_get_time(injections.tableName)
229 injections.extend(inj for inj in injtable if get_time(inj) in span)
230
231 return injections
232
233
234
235
236
237 -def blrms(data, sampling, average=1, band=None, ripple_db=50, width=2.0,\
238 remove_mean=False, return_filter=False, verbose=False):
239
240 """
241 This function will calculate the band-limited root-mean-square of the given
242 data, using averages of the given length in the given [fmin,fmax] band
243 with a kaiser window.
244
245 Options are included to offset the data, and weight frequencies given a
246 dict object of (frequency:weight) pairs.
247
248 Arguments:
249
250 data : numpy.ndarray
251 array of data points
252 sampling : int
253 number of data points per second
254
255 Keyword arguments:
256
257 average : float
258 length of rms in seconds
259 band : tuple
260 [fmin, fmax] for bandpass
261 ripple_db : int
262 Attenuation in the stop band, in dB
263 width : float
264 Desired width of the transition from pass to stop, in Hz
265 remove_mean : boolean
266 verbose : boolean
267 """
268
269 nyq = sampling/2
270
271
272 if band==None:
273 band=[0,sampling/2]
274 fmin = float(band[0])
275 fmax = float(band[1])
276
277 if verbose:
278 sys.stdout.write("Calculating BLRMS in band %s-%s Hz...\n" % (fmin, fmax))
279
280
281
282
283
284 if remove_mean:
285 data = data-data.mean()
286 if verbose: sys.stdout.write("Data mean removed.\n")
287
288
289
290
291 if return_filter:
292 data, filter = bandpass(data, sampling, fmin, fmax, ripple_db=ripple_db,\
293 width=width, return_filter=True, verbose=verbose)
294 else:
295 data = bandpass(data, sampling, fmin, fmax, ripple_db=ripple_db,\
296 width=width, return_filter=False, verbose=verbose)
297
298
299
300
301
302
303
304 numsamp = int(average*sampling)
305 numaverage = numpy.ceil(len(data)/sampling/average)
306 output = numpy.empty(numaverage)
307
308
309 for i in xrange(len(output)):
310
311
312 idxmin = i*sampling*average
313 idxmax = idxmin + numsamp
314
315
316 chunk = data[idxmin:idxmax]
317
318
319 output[i] = (chunk**2).mean()**(1/2)
320
321 if verbose: sys.stdout.write("RMS calculated for %d averages.\n"\
322 % len(output))
323
324 if return_filter:
325 return output, filter
326 else:
327 return output
328
329
330
331
332
333 -def bandpass(data, sampling, fmin, fmax, ripple_db=50, width=2.0,\
334 return_filter=False, verbose=False):
335
336 """
337 This function will bandpass filter data in the given [fmin,fmax] band
338 using a kaiser window.
339
340 Arguments:
341
342 data : numpy.ndarray
343 array of data points
344 sampling : int
345 number of data points per second
346 fmin : float
347 frequency of lowpass
348 fmax : float
349 frequency of highpass
350
351 Keyword arguments:
352
353 ripple_db : int
354 Attenuation in the stop band, in dB
355 width : float
356 Desired width of the transition from pass to stop, in Hz
357 return_filter: boolean
358 Return filter
359 verbose : boolean
360 """
361
362
363 order, beta = signal.kaiserord(ripple_db, width*2/sampling)
364
365 lowpass = signal.firwin(order, fmin*2/sampling, window=('kaiser', beta))
366 highpass = - signal.firwin(order, fmax*2/sampling, window=('kaiser', beta))
367 highpass[order//2] = highpass[order//2] + 1
368
369 bandpass = -(lowpass + highpass); bandpass[order//2] = bandpass[order//2] + 1
370
371
372 data = signal.lfilter(bandpass,1.0,data)
373 data = data[::-1]
374 data = signal.lfilter(bandpass,1.0,data)
375 data = data[::-1]
376
377 if verbose: sys.stdout.write("Bandpass filter applied to data.\n")
378
379 if return_filter:
380 return data, bandpass
381 else:
382 return data
383
384
385
386
387
388 -def lowpass(data, sampling, fmin, ripple_db=50, width=2.0,\
389 return_filter=False, verbose=False):
390
391 """
392 This function will lowpass filter data in the given fmin band
393 using a kaiser window.
394
395 Arguments:
396
397 data : numpy.ndarray
398 array of data points
399 sampling : int
400 number of data points per second
401 fmin : float
402 frequency of lowpass
403
404 Keyword arguments:
405
406 ripple_db : int
407 Attenuation in the stop band, in dB
408 width : float
409 Desired width of the transition from pass to stop, in Hz
410 return_filter: boolean
411 Return filter
412 verbose : boolean
413 """
414
415
416 order, beta = signal.kaiserord(ripple_db, width*2/sampling)
417
418 lowpass = signal.firwin(order, fmin*2/sampling, window=('kaiser', beta))
419
420
421 data = signal.lfilter(lowpass,1.0,data)
422 data = data[::-1]
423 data = signal.lfilter(lowpass,1.0,data)
424 data = data[::-1]
425
426 if verbose: sys.stdout.write("Lowpass filter applied to data.\n")
427
428 if return_filter:
429 return data, lowpass
430 else:
431 return data
432
433
434
435
436
437 -def highpass(data, sampling, fmax, ripple_db=50, width=2.0,\
438 return_filter=False, verbose=False):
439
440 """
441 This function will highpass filter data in the given fmax band
442 using a kaiser window.
443
444 Arguments:
445
446 data : numpy.ndarray
447 array of data points
448 sampling : int
449 number of data points per second
450 fmax : float
451 frequency of highpass
452
453 Keyword arguments:
454
455 ripple_db : int
456 Attenuation in the stop band, in dB
457 width : float
458 Desired width of the transition from pass to stop, in Hz
459 return_filter: boolean
460 Return filter
461 verbose : boolean
462 """
463
464
465 order, beta = signal.kaiserord(ripple_db, width*2/sampling)
466
467 highpass = - signal.firwin(order, fmax*2/sampling, window=('kaiser', beta))
468 highpass[order//2] = highpass[order//2] + 1
469
470
471 data = signal.lfilter(highpass,1.0,data)
472 data = data[::-1]
473 data = signal.lfilter(highpass,1.0,data)
474 data = data[::-1]
475
476 if verbose: sys.stdout.write("Highpass filter applied to data.\n")
477
478 if return_filter:
479 return data, highpass
480 else:
481 return data
482
483
484
485
486
487 -def spectrum(data, sampling, NFFT=256, overlap=0.5,\
488 window='hanning', detrender=mlab.detrend_linear,\
489 sides='onesided', scale='PSD'):
490
491 numpoints = len(data)
492 numoverlap = int(sampling * (1.0 - overlap))
493
494 if isinstance(window,str):
495 window=window.lower()
496
497 win = signal.get_window(window, NFFT)
498
499
500 spec,freq = mlab.psd(data, NFFT=NFFT, Fs=sampling, noverlap=numoverlap,\
501 window=win, sides=sides, detrend=detrender)
502
503
504 scale = scale.lower()
505 if scale == 'asd':
506 spec = numpy.sqrt(spec) * numpy.sqrt(2 / (sampling*sum(win**2)))
507 elif scale == 'psd':
508 spec *= 2/(sampling*sum(win**2))
509 elif scale == 'as':
510 spec = nump.sqrt(spec) * numpy.sqrt(2) / sum(win)
511 elif scale == 'ps':
512 spec = spec * 2 / (sum(win)**2)
513
514 return freq, spec.flatten()
515
516
517
518
519
611
612
613
614
615
633
634
635
636
637
708
709
710
711
712
714
715 """
716 Apply window function to data set, defaults to Hanning window.
717 """
718
719
720 if window == None:
721 window = scipy.signal.hanning(len(series))
722
723
724 assert len(series)==len(window), 'Window and data must be same shape'
725
726
727 sumofsquares = (window**2).sum()
728 assert sumofsquares > 0, 'Sum of squares of window non-positive.'
729
730
731 norm = (len(window)/sumofsquares)**(1/2)
732
733
734 return series * window * norm
735
736
737
738
739
741
742 """
743 Calculate power spectum of given series
744 """
745
746 if sides!='onesided':
747 raise NotImplementedError('Only one sided spectrum implemented for the moment')
748
749
750 tmp = numpy.fft.fft(series, n=len(series))
751
752
753 if sides=='onesided':
754 spec = numpy.empty(len(tmp)//2+1)
755 elif sides=='twosided':
756 spec = numpy.empty(len(tmp))
757
758
759 spec[0] = tmp[0]**2
760
761
762 s = (len(series)+1)//2
763 spec[1:s] = 2 * (tmp[1:s].real**2 + tmp[1:s].imag**2)
764
765
766 if len(series) % 2 == 0:
767 spec[len(series)/2] = tmp[len(series)/2]**2
768
769 return spec
770
771
772
773
774
775 -def inspiral_range(f, S, rho=8, m1=1.4, m2=1.4, fmin=30, fmax=4096,\
776 horizon=False):
777
778 """
779 Calculate inspiral range for a given spectrum.
780 """
781
782 Mpc = 10**6 * XLALConstants.PC_SI
783
784
785 mtot = m1 + m2;
786 reducedmass = m1*m2/mtot;
787 mchirp = reducedmass**(3/5)*mtot**(2/5);
788
789
790 mchirp *= XLALConstants.MSUN_SI * XLALConstants.G_SI /\
791 XLALConstants.C_SI**2
792 pre = (5 * XLALConstants.C_SI**(1/3) * mchirp**(5/3) * 1.77**2) /\
793 (96 * numpy.pi ** (4/3) * rho**2)
794
795
796 fisco = XLALConstants.C_SI**3/XLALConstants.G_SI/XLALConstants.MSUN_SI/\
797 6**1.5/numpy.pi/mtot
798
799
800 condition = (f >= fmin) & (f < min(fmax,fisco))
801 S = S[condition]
802 f = f[condition]
803
804
805 integrand = (f**(-7/3))/S
806
807
808 result = scipy.integrate.trapz(integrand, f)
809
810 R = (pre*result) ** 0.5 / Mpc
811
812 if horizon: R *= 2.26
813
814 return R
815
816
817
818
819
821 """
822 Calculate GRB-like or supernov-like burst range for a given spectrum and background trigger SNR at a given time as a function of freqeucy.
823 """
824
825 Mpc = 10**6 * XLALConstants.PC_SI
826
827
828 A = (((XLALConstants.G_SI * (E*XLALConstants.MSUN_SI) * 2/5)/(XLALConstants.PI**2 * XLALConstants.C_SI))**(1/2))/Mpc
829 R = A/ (rho * S**(1/2) * f)
830
831 return R
832
833
834
835
836
837 -def burst_range(f, S, rho=8, E=1e-2, fmin=64, fmax=500):
838 """
839 Calculate GRB-like or supernova-like burst range for a given spectrum
840 and background trigger SNR.
841 """
842
843
844 condition = (f>=fmin) & (f<fmax)
845 S2 = S[condition]
846 f2 = f[condition]
847
848
849 FOM1 = scipy.integrate.trapz(f_dependent_burst_range(f2, S2, rho, E)**3, f2)
850 FOM2 = FOM1/(fmax-fmin)
851
852 return FOM2**(1/3)
853
854 -def burst_sg_range(f, S, centralFreq, Q, rho=8, E=1e-2, fmin=64, fmax=500):
855 """
856 Calculate range for sine-Gaussians for a given spectrum
857 and background trigger SNR, assuming isotropic GW emission (unphysical but simple)
858 """
859
860
861 condition = (f>=fmin) & (f<fmax)
862 S2 = S[condition]
863 f2 = f[condition]
864
865
866 Mpc = 10**6 * XLALConstants.PC_SI
867 1/centralFreq
868 A = (((XLALConstants.G_SI * (E*XLALConstants.MSUN_SI) )/(XLALConstants.PI**2 * XLALConstants.C_SI))**(1/2))/Mpc/centralFreq
869 sigmaSq = Q**2 / (4 * XLALConstants.PI**2 * centralFreq**2)
870 sg = numpy.exp( - (f2 - centralFreq)**2 * sigmaSq / 2)
871 normSG = scipy.integrate.trapz(sg**2, f2)**(1/2)
872 sg = sg/normSG
873 R = A * sg / (rho * S2**(1/2) )
874
875
876 FOM1 = scipy.integrate.trapz(R**2, f2)
877
878
879
880 return FOM1**(1/2)*0.36
881