LALInspiral 5.0.3.1-eeff03c
LALInspiralSBankOverlap.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2012 Nickolas Fotopoulos
3 * Copyright (C) 2016 Stephen Privitera
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <math.h>
22#include <complex.h>
23#include <lal/LALError.h>
24#include <lal/AVFactories.h>
25#include <lal/ComplexFFT.h>
26#include <lal/XLALError.h>
27#include <lal/FrequencySeries.h>
28#include <lal/LALInspiralSBankOverlap.h>
29#include <sys/types.h>
30
31#define MAX_NUM_WS 32 /* maximum number of workspaces */
32#define CHECK_OOM(ptr, msg) if (!(ptr)) { XLALPrintError((msg)); XLAL_ERROR_NULL(XLAL_ENOMEM); }
33
34/*
35 * set up workspaces
36 */
37
39 WS *workspace_cache = calloc(MAX_NUM_WS, sizeof(WS));
40 CHECK_OOM(workspace_cache, "unable to allocate workspace\n");
41 return workspace_cache;
42}
43
44void XLALDestroySBankWorkspaceCache(WS *workspace_cache) {
45 size_t k = MAX_NUM_WS;
46 for (;k--;) {
47 if (workspace_cache[k].n) {
48 XLALDestroyCOMPLEX8FFTPlan(workspace_cache[k].plan);
49 XLALDestroyCOMPLEX8Vector(workspace_cache[k].zf);
50 XLALDestroyCOMPLEX8Vector(workspace_cache[k].zt);
51 }
52 }
53 free(workspace_cache);
54}
55
56static WS *get_workspace(WS *workspace_cache, const size_t n) {
57 if (!n) {
58 lalAbortHook("%s: Zero size workspace requested\n", __func__);
59 return NULL;
60 }
61
62 /* if n already in cache, return it */
63 WS *ptr = workspace_cache;
64 while (ptr->n) {
65 if (ptr->n == n) return ptr;
66 if (++ptr - workspace_cache > MAX_NUM_WS) return NULL; /* out of space! */
67 }
68
69 /* if n not in cache, ptr now points at first blank entry */
71 CHECK_OOM(ptr->zf->data, "unable to allocate workspace array zf\n");
72 memset(ptr->zf->data, 0, n * sizeof(COMPLEX8));
73
75 CHECK_OOM(ptr->zf->data, "unable to allocate workspace array zt\n");
76 memset(ptr->zt->data, 0, n * sizeof(COMPLEX8));
77
78 ptr->n = n;
80 CHECK_OOM(ptr->plan, "unable to allocate plan");
81
82 return ptr;
83}
84
85/* by default, complex arithmetic will call built-in function __muldc3, which does a lot of error checking for inf and nan; just do it manually */
86static void multiply_conjugate(COMPLEX8 * restrict out, COMPLEX8 *a, COMPLEX8 *b, const size_t size) {
87 size_t k = 0;
88 for (;k < size; ++k) {
89 const float ar = crealf(a[k]);
90 const float br = crealf(b[k]);
91 const float ai = cimagf(a[k]);
92 const float bi = cimagf(b[k]);
93 __real__ out[k] = ar * br + ai * bi;
94 __imag__ out[k] = ar * -bi + ai * br;
95 }
96}
97
98static double abs_real(const COMPLEX8 x) {
99 const REAL8 re = crealf(x);
100 return re;
101}
102
103static double abs2(const COMPLEX8 x) {
104 const REAL8 re = crealf(x);
105 const REAL8 im = cimagf(x);
106 return re * re + im * im;
107}
108
109/* interpolate the peak with a parabolic interpolation */
110static double vector_peak_interp(const double ym1, const double y, const double yp1) {
111 const double dy = 0.5 * (yp1 - ym1);
112 const double d2y = 2. * y - ym1 - yp1;
113 return y + 0.5 * dy * dy / d2y;
114}
115
116/*
117 * Returns the match for two whitened, normalized, positive-frequency
118 * COMPLEX8FrequencySeries inputs.
119 */
121 size_t min_len = (inj->data->length <= tmplt->data->length) ? inj->data->length : tmplt->data->length;
122
123 /* get workspace for + and - frequencies */
124 size_t n = 2 * (min_len - 1); /* no need to integrate implicit zeros */
125 WS *ws = get_workspace(workspace_cache, n);
126 if (!ws) {
127 XLALPrintError("out of space in the workspace_cache\n");
129 }
130
131 /* compute complex SNR time-series in freq-domain, then time-domain */
132 /* Note that findchirp paper eq 4.2 defines a positive-frequency integral,
133 so we should only fill the positive frequencies (first half of zf). */
134 multiply_conjugate(ws->zf->data, inj->data->data, tmplt->data->data, min_len);
135 XLALCOMPLEX8VectorFFT(ws->zt, ws->zf, ws->plan); /* plan is reverse */
136
137 /* maximize over |z(t)|^2 */
138 COMPLEX8 *zdata = ws->zt->data;
139 size_t k = n;
140 ssize_t argmax = -1;
141 REAL8 max = 0.;
142 for (;k--;) {
143 REAL8 temp = abs2(zdata[k]);
144 if (temp > max) {
145 argmax = k;
146 max = temp;
147 }
148 }
149 if (max == 0.) return 0.;
150
151 /* refine estimate of maximum */
152 REAL8 result;
153 if (argmax == 0 || argmax == (ssize_t) n - 1)
154 result = max;
155 else
156 result = vector_peak_interp(abs2(zdata[argmax - 1]), abs2(zdata[argmax]), abs2(zdata[argmax + 1]));
157
158 /* compute match */
159 /* return 4. * inj->deltaF * sqrt(result) / n; */ /* inverse FFT = reverse / n */
160 return 4. * inj->deltaF * sqrt(result); /* Ajith, 2012-11-09: The division by n is inconsistent with the revised convention of the normalization in compute_sigmasq in SBank. I check this by computing the match of a normalized, whitened template with itself */
161}
162
163
164/*
165 Compute the overlap between a normalized template waveform h and a
166 normalized signal proposal maximizing over the template h's overall
167 amplitude. This is the most basic match function one can compute.
168*/
170 size_t min_len = (inj->data->length <= tmplt->data->length) ? inj->data->length : tmplt->data->length;
171
172 /* get workspace for + and - frequencies */
173 size_t n = 2 * (min_len - 1); /* no need to integrate implicit zeros */
174 WS *ws = get_workspace(workspace_cache, n);
175 if (!ws) {
176 XLALPrintError("out of space in the workspace_cache\n");
178 }
179
180 /* compute complex SNR time-series in freq-domain, then time-domain */
181 /* Note that findchirp paper eq 4.2 defines a positive-frequency integral,
182 so we should only fill the positive frequencies (first half of zf). */
183 multiply_conjugate(ws->zf->data, inj->data->data, tmplt->data->data, min_len);
184 XLALCOMPLEX8VectorFFT(ws->zt, ws->zf, ws->plan); /* plan is reverse */
185
186 /* maximize over |Re z(t)| */
187 COMPLEX8 *zdata = ws->zt->data;
188 size_t k = n;
189 REAL8 max = 0.;
190 for (;k--;) {
191 REAL8 temp = abs_real((zdata[k]));
192 if (temp > max) {
193 max = temp;
194 }
195 }
196 return 4. * inj->deltaF * max;
197}
198
199
200/*
201 Compute the overlap between a template waveform h and a signal
202 proposal assuming only the (2,2) mode and maximizing over the
203 template h's coalescence phase, overall amplitude and effective
204 polarization / sky position. The function assumes that the plus and
205 cross polarization hp and hc are both normalized to unity and that
206 hphccorr is the correlation between these normalized components.
207 */
208REAL8 XLALInspiralSBankComputeMatchMaxSkyLoc(const COMPLEX8FrequencySeries *hp, const COMPLEX8FrequencySeries *hc, const REAL8 hphccorr, const COMPLEX8FrequencySeries *proposal, WS *workspace_cache1, WS *workspace_cache2) {
209
210 /* FIXME: Add sanity checking for consistency of lengths in input */
211 /* What does this do? */
212 size_t min_len = (hp->data->length <= proposal->data->length) ? hp->data->length : proposal->data->length;
213
214 /* get workspace for + and - frequencies */
215 size_t n = 2 * (min_len - 1); /* no need to integrate implicit zeros */
216 WS *ws1 = get_workspace(workspace_cache1, n);
217 if (!ws1) {
218 XLALPrintError("out of space in the workspace_cache\n");
220 }
221 WS *ws2 = get_workspace(workspace_cache2, n);
222 if (!ws2) {
223 XLALPrintError("out of space in the workspace_cache\n");
225 }
226
227
228 /* compute complex SNR time-series in freq-domain, then time-domain */
229 /* Note that findchirp paper eq 4.2 defines a positive-frequency integral,
230 so we should only fill the positive frequencies (first half of zf). */
231 multiply_conjugate(ws1->zf->data, hp->data->data, proposal->data->data, min_len);
232 XLALCOMPLEX8VectorFFT(ws1->zt, ws1->zf, ws1->plan); /* plan is reverse */
233 multiply_conjugate(ws2->zf->data, hc->data->data, proposal->data->data, min_len);
234 XLALCOMPLEX8VectorFFT(ws2->zt, ws2->zf, ws2->plan);
235
236
237 /* COMPUTE DETECTION STATISTIC */
238
239 /* First start with constant values */
240 REAL8 delta = 2 * hphccorr;
241 REAL8 denom = 4 - delta * delta;
242 if (denom < 0)
243 {
244 fprintf(stderr, "DANGER WILL ROBINSON: CODE IS BROKEN!!\n");
245 }
246
247 /* Now the tricksy bit as we loop over time*/
248 COMPLEX8 *hpdata = ws1->zt->data;
249 COMPLEX8 *hcdata = ws2->zt->data;
250 size_t k = n;
251 /* FIXME: This is needed if we turn back on peak refinement. */
252 /*ssize_t argmax = -1;*/
253 REAL8 max = 0.;
254 for (;k--;) {
255 COMPLEX8 ratio = hcdata[k] / hpdata[k];
256 REAL8 ratio_real = creal(ratio);
257 REAL8 ratio_imag = cimag(ratio);
258 REAL8 beta = 2 * ratio_real;
259 REAL8 alpha = ratio_real * ratio_real + ratio_imag * ratio_imag;
260 REAL8 sqroot = alpha*alpha + alpha * (delta*delta - 2) + 1;
261 sqroot += beta * (beta - delta * (1 + alpha));
262 sqroot = sqrt(sqroot);
263 REAL8 brckt = 2*(alpha + 1) - beta*delta + 2*sqroot;
264 brckt = brckt / denom;
265 REAL8 det_stat_sq = abs2(hpdata[k]) * brckt;
266
267 if (det_stat_sq > max) {
268 /*argmax = k;*/
269 max = det_stat_sq;
270 }
271 }
272 if (max == 0.) return 0.;
273
274 /* FIXME: For now do *not* refine estimate of peak. */
275 /* REAL8 result;
276 if (argmax == 0 || argmax == (ssize_t) n - 1)
277 result = max;
278 else
279 result = vector_peak_interp(abs2(zdata[argmax - 1]), abs2(zdata[argmax]), abs2(zdata[argmax + 1])); */
280
281 /* Return match */
282 return 4. * proposal->deltaF * sqrt(max);
283}
284
285REAL8 XLALInspiralSBankComputeMatchMaxSkyLocNoPhase(const COMPLEX8FrequencySeries *hp, const COMPLEX8FrequencySeries *hc, const REAL8 hphccorr, const COMPLEX8FrequencySeries *proposal, WS *workspace_cache1, WS *workspace_cache2) {
286 /* FIXME: Add sanity checking for consistency of lengths in input */
287
288 /* What does this do? */
289 size_t min_len = (hp->data->length <= proposal->data->length) ? hp->data->length : proposal->data->length;
290
291 /* get workspace for + and - frequencies */
292 size_t n = 2 * (min_len - 1); /* no need to integrate implicit zeros */
293 WS *ws1 = get_workspace(workspace_cache1, n);
294 if (!ws1) {
295 XLALPrintError("out of space in the workspace_cache\n");
297 }
298 WS *ws2 = get_workspace(workspace_cache2, n);
299 if (!ws2) {
300 XLALPrintError("out of space in the workspace_cache\n");
302 }
303
304
305 /* compute complex SNR time-series in freq-domain, then time-domain */
306 /* Note that findchirp paper eq 4.2 defines a positive-frequency integral,
307 so we should only fill the positive frequencies (first half of zf). */
308 multiply_conjugate(ws1->zf->data, hp->data->data, proposal->data->data, min_len);
309 XLALCOMPLEX8VectorFFT(ws1->zt, ws1->zf, ws1->plan); /* plan is reverse */
310 multiply_conjugate(ws2->zf->data, hc->data->data, proposal->data->data, min_len);
311 XLALCOMPLEX8VectorFFT(ws2->zt, ws2->zf, ws2->plan);
312
313
314 /* COMPUTE DETECTION STATISTIC */
315
316 /* First start with constant values */
317 REAL8 denom = 1. - (hphccorr*hphccorr);
318 if (denom < 0)
319 {
320 fprintf(stderr, "DANGER WILL ROBINSON: CODE IS BROKEN!!\n");
321 }
322
323 /* Now the tricksy bit as we loop over time*/
324 COMPLEX8 *hpdata = ws1->zt->data;
325 COMPLEX8 *hcdata = ws2->zt->data;
326 size_t k = n;
327 /* FIXME: This is needed if we turn back on peak refinement. */
328 /*ssize_t argmax = -1;*/
329 REAL8 max = 0.;
330 REAL8 det_stat_sq;
331
332 for (;k--;) {
333 det_stat_sq = creal(hpdata[k])*creal(hpdata[k]);
334 det_stat_sq += creal(hcdata[k])*creal(hcdata[k]);
335 det_stat_sq -= 2*creal(hpdata[k])*creal(hcdata[k])*hphccorr;
336
337 det_stat_sq = det_stat_sq / denom;
338
339 if (det_stat_sq > max) {
340 /*argmax = k;*/
341 max = det_stat_sq;
342 }
343 }
344 if (max == 0.) return 0.;
345
346 /* FIXME: For now do *not* refine estimate of peak. */
347 /* REAL8 result;
348 if (argmax == 0 || argmax == (ssize_t) n - 1)
349 result = max;
350 else
351 result = vector_peak_interp(abs2(zdata[argmax - 1]), abs2(zdata[argmax])
352, abs2(zdata[argmax + 1])); */
353
354 /* Return match */
355 return 4. * proposal->deltaF * sqrt(max);
356}
void(* lalAbortHook)(const char *,...)
REAL8 XLALInspiralSBankComputeRealMatch(const COMPLEX8FrequencySeries *inj, const COMPLEX8FrequencySeries *tmplt, WS *workspace_cache)
static void multiply_conjugate(COMPLEX8 *restrict out, COMPLEX8 *a, COMPLEX8 *b, const size_t size)
static double abs_real(const COMPLEX8 x)
#define CHECK_OOM(ptr, msg)
static double vector_peak_interp(const double ym1, const double y, const double yp1)
REAL8 XLALInspiralSBankComputeMatch(const COMPLEX8FrequencySeries *inj, const COMPLEX8FrequencySeries *tmplt, WS *workspace_cache)
REAL8 XLALInspiralSBankComputeMatchMaxSkyLocNoPhase(const COMPLEX8FrequencySeries *hp, const COMPLEX8FrequencySeries *hc, const REAL8 hphccorr, const COMPLEX8FrequencySeries *proposal, WS *workspace_cache1, WS *workspace_cache2)
WS * XLALCreateSBankWorkspaceCache(void)
static WS * get_workspace(WS *workspace_cache, const size_t n)
#define MAX_NUM_WS
void XLALDestroySBankWorkspaceCache(WS *workspace_cache)
REAL8 XLALInspiralSBankComputeMatchMaxSkyLoc(const COMPLEX8FrequencySeries *hp, const COMPLEX8FrequencySeries *hc, const REAL8 hphccorr, const COMPLEX8FrequencySeries *proposal, WS *workspace_cache1, WS *workspace_cache2)
static double abs2(const COMPLEX8 x)
static double double delta
#define fprintf
COMPLEX8FFTPlan * XLALCreateReverseCOMPLEX8FFTPlan(UINT4 size, int measurelvl)
int XLALCOMPLEX8VectorFFT(COMPLEX8Vector *_LAL_RESTRICT_ output, const COMPLEX8Vector *_LAL_RESTRICT_ input, const COMPLEX8FFTPlan *plan)
void XLALDestroyCOMPLEX8FFTPlan(COMPLEX8FFTPlan *plan)
double REAL8
float complex COMPLEX8
static const INT4 a
COMPLEX8Vector * XLALCreateCOMPLEX8Vector(UINT4 length)
void XLALDestroyCOMPLEX8Vector(COMPLEX8Vector *vector)
#define XLAL_ERROR_REAL8(...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
XLAL_ENOMEM
list y
int n
x
double alpha
COMPLEX8Sequence * data
COMPLEX8 * data
COMPLEX8FFTPlan * plan
COMPLEX8Vector * zt
COMPLEX8Vector * zf