gstlal-inspiral  0.4.2
 All Classes Namespaces Files Functions Variables Pages
cbc_template_iir.py
1 ######
2 #
3 # This code should read in a xml file and produce three matrices, a1, b0, delay
4 # that correspond to a bank of waveforms
5 #
6 #######
7 #
8 # Copyright (C) 2010-2012 Shaun Hooper
9 #
10 # This program is free software; you can redistribute it and/or modify it
11 # under the terms of the GNU General Public License as published by the
12 # Free Software Foundation; either version 2 of the License, or (at your
13 # option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful, but
16 # WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
18 # Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along
21 # with this program; if not, write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 
24 from pylal import spawaveform
25 import sys
26 import time
27 import numpy
28 import scipy
29 from scipy import integrate
30 from scipy import interpolate
31 import math
32 import lal
33 import pdb
34 import csv
35 from glue.ligolw import ligolw, lsctables, array, param, utils, types
36 from gstlal.pipeio import repack_complex_array_to_real, repack_real_array_to_complex
37 import pdb
38 
39 class XMLContentHandler(ligolw.LIGOLWContentHandler):
40  pass
41 
42 def Theta(eta, Mtot, t):
43  Tsun = lal.MTSUN_SI #4.925491e-6
44  theta = eta / (5.0 * Mtot * Tsun) * -t
45  return theta
46 
47 def freq(eta, Mtot, t):
48  theta = Theta(eta, Mtot, t)
49  Tsun = lal.MTSUN_SI #4.925491e-6
50  f = 1.0 / (8.0 * Tsun * scipy.pi * Mtot) * (
51  theta**(-3.0/8.0) +
52  (743.0/2688.0 + 11.0 /32.0 * eta) * theta**(-5.0 /8.0) -
53  3.0 * scipy.pi / 10.0 * theta**(-3.0 / 4.0) +
54  (1855099.0 / 14450688.0 + 56975.0 / 258048.0 * eta + 371.0 / 2048.0 * eta**2.0) * theta**(-7.0/8.0))
55  return f
56 
57 def Phase(eta, Mtot, t, phic = 0.0):
58  theta = Theta(eta, Mtot, t)
59  phi = phic - 2.0 / eta * (
60  theta**(5.0 / 8.0) +
61  (3715.0 /8064.0 +55.0 /96.0 *eta) * theta**(3.0/8.0) -
62  3.0 *scipy.pi / 4.0 * theta**(1.0/4.0) +
63  (9275495.0 / 14450688.0 + 284875.0 / 258048.0 * eta + 1855.0 /2048.0 * eta**2) * theta**(1.0/8.0))
64  return phi
65 
66 def Amp(eta, Mtot, t):
67  theta = Theta(eta, Mtot, t)
68  c = lal.C_SI #3.0e10
69  Tsun = lal.MTSUN_SI #4.925491e-6
70  Mpc = 1e6 * lal.PC_SI #3.08568025e24
71  f = 1.0 / (8.0 * Tsun * scipy.pi * Mtot) * (theta**(-3.0/8.0))
72  amp = - 4.0/Mpc * Tsun * c * (eta * Mtot ) * (Tsun * scipy.pi * Mtot * f)**(2.0/3.0);
73  return amp
74 
75 def waveform(m1, m2, fLow, fhigh, sampleRate):
76  deltaT = 1.0 / sampleRate
77  T = spawaveform.chirptime(m1, m2 , 4, fLow, fhigh)
78  tc = -spawaveform.chirptime(m1, m2 , 4, fhigh)
79  # the last sampling point of any waveform is always set
80  # at abs(t) >= delta. this is to avoid ill-condition of
81  # frequency when abs(t) < 1e-5
82  n_start = math.floor((tc-T) / deltaT + 0.5)
83  n_end = min(math.floor(tc/deltaT), -1)
84  t = numpy.arange(n_start, n_end+1, 1) * deltaT
85  Mtot = m1 + m2
86  eta = m1 * m2 / Mtot**2
87  f = freq(eta, Mtot, t)
88  amp = Amp(eta, Mtot, t);
89  phase = Phase(eta, Mtot, t);
90  return amp, phase, f
91 
92 def sigmasq2(mchirp, fLow, fhigh, psd_interp):
93  c = lal.C_SI #299792458
94  G = lal.G_SI #6.67259e-11
95  M = lal.MSUN_SI #1.98892e30
96  Mpc =1e6 * lal.PC_SI #3.0856775807e22
97  #mchirp = 1.221567#30787
98  const = numpy.sqrt((5.0 * math.pi)/(24.*c**3))*(G*mchirp*M)**(5./6.)*math.pi**(-7./6.)/Mpc
99  return const * numpy.sqrt(4.*integrate.quad(lambda x: x**(-7./3.) / psd_interp(x), fLow, fhigh)[0])
100 
101 # FIX ME: Change the following to actually read in the XML file
102 #
103 # Start Code
104 #
105 
106 
107 def get_iir_sample_rate(xmldoc):
108  pass
109 
110 def sample_rates_array_to_str(sample_rates):
111  return ",".join([str(a) for a in sample_rates])
112 
113 def sample_rates_str_to_array(sample_rates_str):
114  return numpy.array([int(a) for a in sample_rates_str.split(',')])
115 
116 
117 def get_fir_matrix(xmldoc, fFinal=None, pnorder=4, flower = 40, psd_interp=None, autocorrelation_length=101, verbose=False):
118  sngl_inspiral_table = lsctables.table.get_table(xmldoc, lsctables.SnglInspiralTable.tableName)
119  fFinal = max(sngl_inspiral_table.getColumnByName("f_final"))
120  sampleRate = int(2**(numpy.ceil(numpy.log2(fFinal)+1)))
121  flower = param.get_pyvalue(xmldoc, 'flower')
122  if verbose: print >> sys.stderr, "f_min = %f, f_final = %f, sample rate = %f" % (flower, fFinal, sampleRate)
123 
124  snrvec = []
125  Mlist = []
126 
127  if not (autocorrelation_length % 2):
128  raise ValueError, "autocorrelation_length must be odd (got %d)" % autocorrelation_length
129  autocorrelation_bank = numpy.zeros((len(sngl_inspiral_table), autocorrelation_length), dtype = "cdouble")
130 
131  for tmp, row in enumerate(sngl_inspiral_table):
132  m1 = row.mass1
133  m2 = row.mass2
134 
135  # make the waveform
136  amp, phase, f = waveform(m1, m2, flower, fFinal, sampleRate)
137  if psd_interp is not None:
138  amp /= psd_interp(f)**0.5 * 1e23
139 
140  length = int(2**numpy.ceil(numpy.log2(amp.shape[0])))
141  out = amp * numpy.exp(1j * phase)
142 
143  # normalize the fir coefficients
144  vec1 = numpy.zeros(length * 2, dtype=numpy.cdouble)
145  vec1[-len(out):] = out
146  norm1 = (1.0/numpy.sqrt(2.0))*((vec1 * numpy.conj(vec1)).sum()**0.5)
147  vec1 /= norm1
148  #vec1 = vec1[::-1]
149 
150  # store the coeffs.
151  Mlist.append(vec1.real)
152  Mlist.append(vec1.imag)
153 
154  # compute the SNR
155  #corr = scipy.ifft(scipy.fft(vec1) * numpy.conj(scipy.fft(vec1)))
156 
157  #FIXME this is actually the cross correlation between the original waveform and this approximation
158  #autocorrelation_bank[tmp,:] = numpy.concatenate((corr[(-autocorrelation_length/2+2):],corr[:autocorrelation_length/2+2]))
159 
160  max_len = max([len(i) for i in Mlist])
161  M = numpy.zeros((len(Mlist), max_len))
162 
163  for i, Am in enumerate(Mlist): M[i,:len(Am)] = Am
164 
165  return M, autocorrelation_bank
166 
167 def compute_autocorrelation_mask( autocorrelation ):
168  '''
169  Given an autocorrelation time series, estimate the optimal
170  autocorrelation length to use and return a matrix which masks
171  out the unwanted elements. FIXME TODO for now just returns
172  ones
173  '''
174  return numpy.ones( autocorrelation.shape, dtype="int" )
175 
176 
177 def makeiirbank(xmldoc, sampleRate = None, padding=1.1, epsilon=0.02, alpha=.99, beta=0.25, pnorder=4, flower = 40, psd_interp=None, output_to_xml = False, autocorrelation_length=201, downsample=False, verbose=False):
178 
179  sngl_inspiral_table=lsctables.table.get_table(xmldoc, lsctables.SnglInspiralTable.tableName)
180  fFinal = max(sngl_inspiral_table.getColumnByName("f_final"))
181  if sampleRate is None:
182  sampleRate = int(2**(numpy.ceil(numpy.log2(fFinal)+1)))
183 
184  if verbose: print >> sys.stderr, "f_min = %f, f_final = %f, sample rate = %f" % (flower, fFinal, sampleRate)
185 
186  Amat = {}
187  Bmat = {}
188  Dmat = {}
189  snrvec = []
190 
191 
192  # Check parity of autocorrelation length
193  if autocorrelation_length is not None:
194  if not (autocorrelation_length % 2):
195  raise ValueError, "autocorrelation_length must be odd (got %d)" % autocorrelation_length
196  autocorrelation_bank = numpy.zeros((len(sngl_inspiral_table), autocorrelation_length), dtype = "cdouble")
197  autocorrelation_mask = compute_autocorrelation_mask( autocorrelation_bank )
198  else:
199  autocorrelation_bank = None
200  autocorrelation_mask = None
201 
202 
203  for tmp, row in enumerate(sngl_inspiral_table):
204 
205  m1 = row.mass1
206  m2 = row.mass2
207  fFinal = row.f_final
208  if verbose: start = time.time()
209 
210  # work out the waveform frequency
211  #fFinal = spawaveform.ffinal(m1,m2)
212  #if fFinal > sampleRate / 2.0 / padding: fFinal = sampleRate / 2.0 / padding
213 
214  # make the waveform
215 
216  amp, phase, f = waveform(m1, m2, flower, fFinal, sampleRate)
217  if verbose:
218  print >> sys.stderr, "waveform %f (T = %f)" % ((time.time() - start), float(amp.shape[0]/(float(sampleRate))))
219  start = time.time()
220  if psd_interp is not None:
221  amp /= psd_interp(f)**0.5
222 
223  # make the iir filter coeffs
224  a1, b0, delay = spawaveform.iir(amp, phase, epsilon, alpha, beta, padding)
225  if verbose:
226  print >> sys.stderr, "create IIR bank %f" % (time.time() - start)
227  start = time.time()
228  # get the chirptime
229  length = int(2**numpy.ceil(numpy.log2(amp.shape[0]+autocorrelation_length)))
230  if verbose: print >> sys.stderr, "length = %d, amp length = %d, autocorrelation length = %d" % (length, amp.shape[0], autocorrelation_length)
231 
232  # get the IIR response
233  out = spawaveform.iirresponse(length, a1, b0, delay)
234  if verbose:
235  print >> sys.stderr, "create IIR response %f" % (time.time() - start)
236  start = time.time()
237  out = out[::-1]
238  u = numpy.zeros(length * 1, dtype=numpy.cdouble)
239  u[-len(out):] = out
240  norm1 = 1.0/numpy.sqrt(2.0)*((u * numpy.conj(u)).sum()**0.5)
241  u /= norm1
242 
243  # normalize the iir coefficients
244  b0 /= norm1
245 
246  # get the original waveform
247  out2 = amp * numpy.exp(1j * phase)
248  h = numpy.zeros(length * 1, dtype=numpy.cdouble)
249  h[-len(out2):] = out2
250  #norm2 = 1.0/numpy.sqrt(2.0)*((h * numpy.conj(h)).sum()**0.5)
251  #h /= norm2
252  #if output_to_xml: row.sigmasq = norm2**2*2.0*1e46/sampleRate/9.5214e+48
253 
254  norm2 = abs(numpy.dot(h, numpy.conj(h)))
255  h *= numpy.sqrt(2 / norm2)
256  if output_to_xml: row.sigmasq = 1.0 * norm2 / sampleRate
257  if verbose:
258  newsigma = sigmasq2(row.mchirp, flower, fFinal, psd_interp)
259  print>>sys.stderr, "norm2 = %e, sigma = %f, %f, %f" % (norm2, numpy.sqrt(row.sigmasq), newsigma, (numpy.sqrt(row.sigmasq)- newsigma)/newsigma)
260  print>>sys.stderr, "mchirp %fm, fFinal %f, row.f_final %f" % (row.mchirp, fFinal, row.f_final)
261  start = time.time()
262 
263  #FIXME this is actually the cross correlation between the original waveform and this approximation
264  autocorrelation_bank[tmp,:] = crosscorr(h, h, autocorrelation_length)/2.0
265  if verbose:
266  print>>sys.stderr, "auto correlation %f" % ((time.time() - start))
267  start = time.time()
268 
269  # compute the SNR
270  snr = abs(numpy.dot(u, numpy.conj(h)))/2.0
271  if verbose:
272  print>>sys.stderr, "dot product %f" % ((time.time() - start))
273  start = time.time()
274  snrvec.append(snr)
275  if verbose: print>>sys.stderr, "row %4.0d, m1 = %10.6f m2 = %10.6f, %4.0d filters, %10.8f match" % (tmp+1, m1,m2,len(a1), snr)
276 
277 
278  # store the match for later
279  if output_to_xml: row.snr = snr
280 
281  # get the filter frequencies
282  fs = -1. * numpy.angle(a1) / 2 / numpy.pi # Normalised freqeuncy
283  a1dict = {}
284  b0dict = {}
285  delaydict = {}
286 
287  if downsample:
288  # iterate over the frequencies and put them in the right downsampled bin
289  for i, f in enumerate(fs):
290  M = int(max(1, 2**-numpy.ceil(numpy.log2(f * 2.0 * padding)))) # Decimation factor
291  a1dict.setdefault(sampleRate/M, []).append(a1[i]**M)
292  newdelay = numpy.ceil((delay[i]+1)/(float(M)))
293  b0dict.setdefault(sampleRate/M, []).append(b0[i]*M**0.5*a1[i]**(newdelay*M-delay[i]))
294  delaydict.setdefault(sampleRate/M, []).append(newdelay)
295  #print>>sys.stderr, "sampleRate %4.0d, filter %3.0d, M %2.0d, f %10.9f, delay %d, newdelay %d" % (sampleRate, i, M, f, delay[i], newdelay)
296 
297  else:
298  a1dict[int(sampleRate)] = a1
299  b0dict[int(sampleRate)] = b0
300  delaydict[int(sampleRate)] = delay
301 
302  # store the coeffs
303  for k in a1dict.keys():
304  Amat.setdefault(k, []).append(a1dict[k])
305  Bmat.setdefault(k, []).append(b0dict[k])
306  Dmat.setdefault(k, []).append(delaydict[k])
307 
308 
309 
310  if verbose: print>>sys.stderr, "filters per sample rate (rate , num filters)\n",[(k,len(v)) for k,v in a1dict.items()]
311 
312 
313  A = {}
314  B = {}
315  D = {}
316  sample_rates = []
317 
318  max_rows = max([len(Amat[rate]) for rate in Amat.keys()])
319  for rate in Amat.keys():
320  sample_rates.append(rate)
321  # get ready to store the coefficients
322  max_len = max([len(i) for i in Amat[rate]])
323  DmatMin = min([min(elem) for elem in Dmat[rate]])
324  DmatMax = max([max(elem) for elem in Dmat[rate]])
325  if verbose: print>>sys.stderr, "rate %d, dmin %d, dmax %d, max_row %d, max_len %d" % (rate, DmatMin, DmatMax, max_rows, max_len)
326  A[rate] = numpy.zeros((max_rows, max_len), dtype=numpy.complex128)
327  B[rate] = numpy.zeros((max_rows, max_len), dtype=numpy.complex128)
328  D[rate] = numpy.zeros((max_rows, max_len), dtype=numpy.int)
329  D[rate].fill(DmatMin)
330 
331  for i, Am in enumerate(Amat[rate]): A[rate][i,:len(Am)] = Am
332  for i, Bm in enumerate(Bmat[rate]): B[rate][i,:len(Bm)] = Bm
333  for i, Dm in enumerate(Dmat[rate]): D[rate][i,:len(Dm)] = Dm
334  if output_to_xml:
335  #print 'a_%d' % (rate)
336  #print A[rate], type(A[rate])
337  #print repack_complex_array_to_real(A[rate])
338  #print array.from_array('a_%d' % (rate), repack_complex_array_to_real(A[rate]))
339  root = xmldoc.childNodes[0]
340  root.appendChild(array.from_array('a_%d' % (rate), repack_complex_array_to_real(A[rate])))
341  root.appendChild(array.from_array('b_%d' % (rate), repack_complex_array_to_real(B[rate])))
342  root.appendChild(array.from_array('d_%d' % (rate), D[rate]))
343 
344  if output_to_xml: # Create new document and add them together
345  root = xmldoc.childNodes[0]
346  root.appendChild(param.new_param('sample_rate', types.FromPyType[str], sample_rates_array_to_str(sample_rates)))
347  root.appendChild(param.new_param('flower', types.FromPyType[float], flower))
348  root.appendChild(param.new_param('epsilon', types.FromPyType[float], epsilon))
349  root.appendChild(array.from_array('autocorrelation_bank_real', autocorrelation_bank.real))
350  root.appendChild(array.from_array('autocorrelation_bank_imag', -autocorrelation_bank.imag))
351  root.appendChild(array.from_array('autocorrelation_mask', autocorrelation_mask))
352 
353  return A, B, D, snrvec
354 
355 def crosscorr(a, b, autocorrelation_length = 201):
356  af = scipy.fft(a)
357  bf = scipy.fft(b)
358  corr = scipy.ifft( af * numpy.conj( bf ))
359  return numpy.concatenate((corr[-(autocorrelation_length // 2):],corr[:(autocorrelation_length // 2 + 1)]))
360 
361 def innerproduct(a,b):
362 
363  n = a.length
364  a.append(zeros(n/2),complex)
365  a.extend(zeros(n/2),complex)
366 
367  b.append(zeros(n/2),complex)
368  b.extend(zeros(n/2),complex)
369 
370  af = fft(a)
371  bf = fft(b)
372 
373  cf = af * bf
374  c = ifft(cf)
375 
376  return max(abs(c))
377 
378 def smooth_and_interp(psd, width=1, length = 10):
379  data = psd.data
380  f = numpy.arange(len(psd.data)) * psd.deltaF
381  ln = len(data)
382  x = numpy.arange(-width*length, width*length)
383  sfunc = numpy.exp(-x**2 / 2.0 / width**2) / (2. * numpy.pi * width**2)**0.5
384  out = numpy.zeros(ln)
385  for i,d in enumerate(data[width*length:ln-width*length]):
386  out[i+width*length] = (sfunc * data[i:i+2*width*length]).sum()
387  return interpolate.interp1d(f, out)
388 
389 def get_matrices_from_xml(xmldoc):
390  root = xmldoc
391  sample_rates = [int(r) for r in param.get_pyvalue(root, 'sample_rate').split(',')]
392  A = {}
393  B = {}
394  D = {}
395  for sr in sample_rates:
396  A[sr] = repack_real_array_to_complex(array.get_array(root, 'a_%d' % (sr,)).array)
397  B[sr] = repack_real_array_to_complex(array.get_array(root, 'b_%d' % (sr,)).array)
398  D[sr] = array.get_array(root, 'd_%d' % (sr,)).array
399  autocorrelation_bank_real = array.get_array(root, 'autocorrelation_bank_real').array
400  autocorrelation_bank_imag = array.get_array(root, 'autocorrelation_bank_imag').array
401  autocorrelation_bank = autocorrelation_bank_real + (0+1j) * autocorrelation_bank_imag
402  autocorrelation_mask = array.get_array(root, 'autocorrelation_mask').array
403 
404  sngl_inspiral_table=lsctables.table.get_table(xmldoc, lsctables.SnglInspiralTable.tableName)
405  sigmasq = sngl_inspiral_table.getColumnByName("sigmasq").asarray()
406 
407  return A, B, D, autocorrelation_bank, autocorrelation_mask, sigmasq
408 
409 class Bank(object):
410  def __init__(self, bank_xmldoc, snr_threshold, logname = None, verbose = False):
411  self.template_bank_filename = None
412  self.snr_threshold = snr_threshold
413  self.logname = logname
414 
415  self.A, self.B, self.D, self.autocorrelation_bank, self.autocorrelation_mask, self.sigmasq = get_matrices_from_xml(bank_xmldoc)
416  #self.sigmasq=numpy.ones(len(self.autocorrelation_bank)) # FIXME: make sigmasq correct
417 
418  def get_rates(self, verbose = False):
419  bank_xmldoc = utils.load_filename(self.template_bank_filename, contenthandler = XMLContentHandler, verbose = verbose)
420  return [int(r) for r in param.get_pyvalue(bank_xmldoc, 'sample_rate').split(',')]
421 
422  # FIXME: remove set_template_bank_filename when no longer needed
423  # by trigger generator element
424  def set_template_bank_filename(self,name):
425  self.template_bank_filename = name
426 
427 def load_iirbank(filename, snr_threshold, contenthandler = XMLContentHandler, verbose = False):
428  bank_xmldoc = utils.load_filename(filename, contenthandler = contenthandler, verbose = verbose)
429 
430  bank = Bank.__new__(Bank)
431  bank = Bank(
432  bank_xmldoc,
433  snr_threshold,
434  verbose = verbose
435  )
436 
437  bank.set_template_bank_filename(filename)
438  return bank