LALSimulation  5.4.0.1-fe68b98
LALSimUtils.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2015 J. Creighton, K. Cannon
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with with program; see the file COPYING. If not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301 USA
18  */
19 
20 #include <math.h>
21 #include <lal/LALStdlib.h>
22 #include <lal/LALConstants.h>
23 #include <lal/TimeSeries.h>
24 #include <lal/FrequencySeries.h>
25 #include <lal/TimeFreqFFT.h>
26 #include <lal/Units.h>
27 #include <lal/LALSimUtils.h>
28 
29 #include "check_series_macros.h"
30 
31 /**
32  * Creates a new interpolated psd from an existing psd (if necessary).
33  *
34  * The returned frequency series will have the specified values of @p f0, @p
35  * deltaF, and @p length. If the original frequency series @p old had the same
36  * values, this routine simply returns a pointer to the @p old. Otherwise,
37  * this routine allocates a new frequency series.
38  *
39  * The interpolation in performed is linear-in-log (since a psd is
40  * positive-definite). Any component of the psd that is 0 is taken to be
41  * invalid, and any component of the returned psd that would depend on a 0
42  * component of the original psd is also set to 0. Likewise, any components of
43  * the new psd that would require extrapolation of the original psd are set to
44  * zero. The routines defined here adhere to the convention that frequency
45  * components in which the psd is 0 are ignored.
46  *
47  * @warning If this routines returns a pointer to the original frequency
48  * series, it discards the const qualifier.
49  *
50  * @note The calling routine must test if the pointer to the psd returned by
51  * this routine is the same as the pointer to the psd passed to this routine to
52  * determine if the returned psd needs to be freed separately.
53  */
54 static REAL8FrequencySeries * create_interpolated_psd(double f0, double deltaF, size_t length, const REAL8FrequencySeries *old)
55 {
57  size_t k, kmin, kmax;
58 
59  /* see if we can get away without interpolating */
60  if (fabs(deltaF - old->deltaF) < LAL_REAL8_EPS) {
61  int first;
62 
63  /* do we need to do anything? */
64  if (fabs(f0 - old->f0) < LAL_REAL8_EPS && length == old->data->length)
65  return (REAL8FrequencySeries*)(uintptr_t)(old); /* bad: discards const qual */
66 
67  /* is this just an integer shift / resize? */
68  first = round(f0 - old->f0) / deltaF;
69  if (fabs(first * deltaF - f0 + old->f0) < LAL_REAL8_EPS) {
70  /* is new a subset of old? */
71  if (first >=0 && old->data->length >= length + first)
72  new = XLALCutREAL8FrequencySeries(old, first, length);
73  else {
74  /* copy and resize */
75  new = XLALCutREAL8FrequencySeries(old, 0, old->data->length);
76  new = XLALResizeREAL8FrequencySeries(new, first, length);
77  }
78  return new;
79  }
80  }
81 
82  /* we have to actually interpolate... */
83  new = XLALCreateREAL8FrequencySeries(old->name, &old->epoch, f0, deltaF, &old->sampleUnits, length);
84 
85  /* determine the limits of where interpolation can occur */
86  if (f0 < old->f0)
87  kmin = ceil((old->f0 - f0) / deltaF);
88  else
89  kmin = 0;
90 
91  if (f0 + length * deltaF > old->f0 + old->data->length * old->deltaF)
92  kmax = floor((old->f0 + old->data->length * old->deltaF - f0) / deltaF);
93  else
94  kmax = length;
95 
96  /* zero out any invalid regions */
97  for (k = 0; k < kmin; ++k)
98  new->data->data[k] = 0.0;
99  for (k = kmax; k < length; ++k)
100  new->data->data[k] = 0.0;
101 
102  for (k = kmin; k < kmax; ++k) {
103  double x, ix;
104  double y0, y1;
105  size_t i;
106 
107  x = modf((f0 + k * deltaF - old->f0) / old->deltaF, &ix);
108  i = (size_t)(ix);
109 
110  /* safety checks are probably not necessary */
111  y0 = ix < 0.0 ? 0.0 : old->data->data[i];
112  y1 = i >= old->data->length - 1 ? 0.0 : old->data->data[i+1];
113 
114  if (y0 == 0.0 || y1 == 0.0)
115  new->data->data[k] = 0.0; /* invalid data */
116  else /* linear-in-log interpolation */
117  new->data->data[k] = exp((1.0 - x) * log(y0) + x * log(y1));
118  }
119 
120  return new;
121 }
122 
123 /**
124  * @brief Computes the sense-monitor range for a binary neutron star standard
125  * siren signal for a given one-sided detector noise power spectral density.
126  *
127  * The "Standard Siren" is a restricted (0 pN in amplitude) gravitational
128  * waveform produced by a binary neutron star system comprised of two neutron
129  * stars, each having a mass of 1.4 Msun. A circular inspiral of point
130  * particles is also assumed. The "Sense-Monitor Range" is corresponds to
131  * the range measure reported by the LIGO control room monitor @c SenseMonitor.
132  * This range is the radius of a sphere that has contains as many sources as
133  * the number that would produce a characteristic signal-to-noise ratio 8 in a
134  * detector under the assumption of a homogeneous distribution of sources
135  * having random orientations. No cosmological effects are included in this
136  * sense-monitor range measure. The sense-monitor range \f$\cal R\f$ is
137  * related to the horizon distance \f$D_{\rm hor}\f$ by
138  * \f$D_{\rm hor} \approx 2.26478\cal R\f$.
139  *
140  * @note
141  * The siren has a maximum gravitational wave frequency equal to twice the
142  * innermost stable cirucluar orbit (ISCO) frequency for a Schwarzschild black
143  * hole of the same total mass as the system. This upper frequency is
144  * \f[ f_{\rm isco} = \frac{c^3}{\pi 6^{3/2} G M} = 1570 {\rm Hz} \f]
145  * where \f$M=2.8M_\odot\f$. If @p f_max is above this ISCO frequency, or
146  * negative, then the ISCO frequency is used; otherwise @p f_max is used.
147  *
148  * @note
149  * Other implementations have used a different value for the ISCO frequency.
150  * The SenseMonitor adopts a dimensionless orbital frequency value of
151  * \f$Mf_{\rm orb}=0.0098325\f$ in geometric units for the equal mass case,
152  * while Table I of Kidder, Will, and Wiseman, Physical Review D @b 47 3281
153  * (1993) http://dx.doi.org/10.1103/PhysRevD.47.3281 gives a value of 0.00933.
154  * For comparison, the Schwarzschild ISCO is 16% larger:
155  * \f$Mf_{\rm orb}=1/(2\pi 6^{3/2})=0.0108\f$.
156  *
157  * See XLALMeasureStandardSirenHorizonDistance() for a description of the
158  * horizon distance.
159  *
160  * See XLALMeasureSNRFD() for further discussion about characteristic
161  * signal-to-noise ratios.
162  *
163  * @sa Appendix D of
164  * Bruce Allen, Warren G. Anderson, Patrick R. Brady, Duncan A. Brown, and
165  * Jolien D. E. Creighton, "FINDCHIRP: An algorithm for detection of
166  * gravitational waves from inspiraling compact binaries", Phys. Rev. D @b 85,
167  * 122006 (2012) http://dx.doi.org/10.1103/PhysRevD.85.122006
168  *
169  * @param[in] psd The one-sided detector strain noise power spectral density.
170  * @param[in] f_min The lower bound of the frequency band over which the
171  * signal-to-noise ratio will be computed; set to 0 or a negative value for no
172  * lower bound.
173  * @param[in] f_max The upper bound of the frequency band over which the
174  * signal-to-noise ratio will be computed; set to a negative value for
175  * no upper bound.
176  * @returns The sense-monitor range in meters.
177  * @retval LAL_REAL8_FAIL_NAN Failure.
178  */
180 {
181  double horizon_distance = XLALMeasureStandardSirenHorizonDistance(psd, f_min, f_max);
182  if (XLAL_IS_REAL8_FAIL_NAN(horizon_distance))
184  return horizon_distance / LAL_HORIZON_DISTANCE_OVER_SENSEMON_RANGE;
185 }
186 
187 /**
188  * @brief Computes the horizon distance for a binary neutron star standard
189  * siren signal for a given one-sided detector noise power spectral density.
190  *
191  * The "Standard Siren" is a restricted (0 pN in amplitude) gravitational
192  * waveform produced by a binary neutron star system comprised of two neutron
193  * stars, each having a mass of 1.4 Msun. A circular inspiral of point
194  * particles is also assumed. The horizon distance is the distance at which
195  * such a system that is optimally oriented (face on) and located (at an
196  * interferometer's zenith) would induce a characteristic signal-to-noise
197  * ratio of 8. No cosmological effects are included in this horizon distance
198  * measure.
199  *
200  * @note
201  * The siren has a maximum gravitational wave frequency equal to twice the
202  * innermost stable cirucluar orbit (ISCO) frequency for a Schwarzschild black
203  * hole of the same total mass as the system. This upper frequency is
204  * \f[ f_{\rm isco} = \frac{c^3}{\pi 6^{3/2} G M} = 1570 {\rm Hz} \f]
205  * where \f$M=2.8M_\odot\f$. If @p f_max is above this ISCO frequency, or
206  * negative, then the ISCO frequency is used; otherwise @p f_max is used.
207  *
208  * @note
209  * Other implementations have used a different value for the ISCO frequency.
210  * The SenseMonitor adopts a dimensionless orbital frequency value of
211  * \f$Mf_{\rm orb}=0.0098325\f$ in geometric units for the equal mass case,
212  * while Table I of Kidder, Will, and Wiseman, Physical Review D @b 47 3281
213  * (1993) http://dx.doi.org/10.1103/PhysRevD.47.3281 gives a value of 0.00933.
214  * For comparison, the Schwarzschild ISCO is 16% larger:
215  * \f$Mf_{\rm orb}=1/(2\pi 6^{3/2})=0.0108\f$.
216  *
217  * See XLALMeasureSNRFD() for further discussion about characteristic
218  * signal-to-noise ratios.
219  *
220  * @sa Appendix D of
221  * Bruce Allen, Warren G. Anderson, Patrick R. Brady, Duncan A. Brown, and
222  * Jolien D. E. Creighton, "FINDCHIRP: An algorithm for detection of
223  * gravitational waves from inspiraling compact binaries", Phys. Rev. D @b 85,
224  * 122006 (2012) http://dx.doi.org/10.1103/PhysRevD.85.122006
225  *
226  * @param[in] psd The one-sided detector strain noise power spectral density.
227  * @param[in] f_min The lower bound of the frequency band over which the
228  * signal-to-noise ratio will be computed; set to 0 or a negative value for no
229  * lower bound.
230  * @param[in] f_max The upper bound of the frequency band over which the
231  * signal-to-noise ratio will be computed; set to a negative value for
232  * no upper bound.
233  * @returns The horizon distance in meters.
234  * @retval LAL_REAL8_FAIL_NAN Failure.
235  */
237 {
238  double Mpc = 1e6 * LAL_PC_SI;
239  double snr_8 = 8.0;
240  double snr;
242  if (XLAL_IS_REAL8_FAIL_NAN(snr))
244  return Mpc * snr / snr_8;
245 }
246 
247 /**
248  * @brief Computes the characteristic signal-to-noise for a binary neutron star
249  * standard siren signal located at an effective distance of 1 Mpc for a given
250  * one-sided detector noise power spectral density.
251  *
252  * The "Standard Siren" is a restricted (0 pN in amplitude) gravitational
253  * waveform produced by a binary neutron star system comprised of two neutron
254  * stars, each having a mass of 1.4 Msun, that is optimally oriented (face on)
255  * and located (at an interferometer's zenith) at a distance of 1 Mpc. A
256  * circular inspiral of point particles is also assumed. No cosmological
257  * effects are included in this standard siren.
258  *
259  * @note
260  * The siren has a maximum gravitational wave frequency equal to twice the
261  * innermost stable cirucluar orbit (ISCO) frequency for a Schwarzschild black
262  * hole of the same total mass as the system. This upper frequency is
263  * \f[ f_{\rm isco} = \frac{c^3}{\pi 6^{3/2} G M} = 1570 {\rm Hz} \f]
264  * where \f$M=2.8M_\odot\f$. If @p f_max is above this ISCO frequency, or
265  * negative, then the ISCO frequency is used; otherwise @p f_max is used.
266  *
267  * @note
268  * Other implementations have used a different value for the ISCO frequency.
269  * The SenseMonitor adopts a dimensionless orbital frequency value of
270  * \f$Mf_{\rm orb}=0.0098325\f$ in geometric units for the equal mass case,
271  * while Table I of Kidder, Will, and Wiseman, Physical Review D @b 47 3281
272  * (1993) http://dx.doi.org/10.1103/PhysRevD.47.3281 gives a value of 0.00933.
273  * For comparison, the Schwarzschild ISCO is 16% larger:
274  * \f$Mf_{\rm orb}=1/(2\pi 6^{3/2})=0.0108\f$.
275  *
276  * Implements Eq. (D1) of FINDCHIRP for a 1.4 Msun + 1.4 Msun binary neutron
277  * star standard siren at an effective distance 1 Mpc, but with the specified
278  * limits on the integral.
279  *
280  * @sa Appendix D of
281  * Bruce Allen, Warren G. Anderson, Patrick R. Brady, Duncan A. Brown, and
282  * Jolien D. E. Creighton, "FINDCHIRP: An algorithm for detection of
283  * gravitational waves from inspiraling compact binaries", Phys. Rev. D @b 85,
284  * 122006 (2012) http://dx.doi.org/10.1103/PhysRevD.85.122006
285  *
286  * @param[in] psd The one-sided detector strain noise power spectral density.
287  * @param[in] f_min The lower bound of the frequency band over which the
288  * signal-to-noise ratio will be computed; set to 0 or a negative value for no
289  * lower bound.
290  * @param[in] f_max The upper bound of the frequency band over which the
291  * signal-to-noise ratio will be computed; set to a negative value for
292  * the Schwarzschild ISCO upper bound.
293  * @returns The characteristic signal-to-noise ratio of a binary neutron star
294  * standard siren at an effective distance of 1 Mpc.
295  * @retval LAL_REAL8_FAIL_NAN Failure.
296  */
298 {
299  LALUnit strainSquaredPerHertzUnit = { 0, { 0, 0, 1, 0, 0, 2, 0}, { 0, 0, 0, 0, 0, 0, 0} };
300  LALUnit snrUnits;
301  size_t k, k_min, k_max;
302  double e = 0.0;
303  double sum = 0.0;
304  double prefac = 1.0;
305 
306  /* standard siren constants */
307  double Mpc = 1e6 * LAL_PC_SI;
308  double m1 = 1.4; /* in solar masses */
309  double m2 = 1.4; /* in solar masses */
310  double M = m1 + m2;
311  double mu = m1 * m2 / M;
312  double f_isco = pow(6.0, -1.5) / (LAL_PI * M * LAL_MTSUN_SI);
313  double A_1_Mpc;
314 
315  /* make sure that the psd has correct units
316  * and also extract any unit prefactor */
318  if (!XLALUnitIsDimensionless(&snrUnits))
319  XLAL_ERROR_REAL8(XLAL_EINVAL, "Incorrect PSD units.");
320  else
321  prefac = XLALUnitPrefactor(&snrUnits);
322 
323  /* make sure that f_isco is used by default */
324  if (f_max < 0 || f_max > f_isco)
325  f_max = f_isco;
326 
327  /* use smallest possible f_min if f_min is below that value */
328  if (f_min < 0 || f_min <= psd->f0)
329  f_min = psd->f0;
330 
331  /* find the (inclusive) limits of the summation */
332  k_min = round((f_min - psd->f0)/psd->deltaF);
333  if (k_min == 0)
334  k_min = 1;
335 
336  k_max = round((f_max - psd->f0)/psd->deltaF);
337  if (k_max > psd->data->length - 2) {
338  k_max = psd->data->length - 2;
339  }
340 
341  /* domain error if integral is empty */
342  if (k_max < k_min)
343  XLAL_ERROR_REAL8(XLAL_EDOM, "Maximum frequency is below minimum frequency");
344 
345  /* Kahan's compensated summation algorithm. The summation is done
346  * from lowest to highest frequency under the assumption that high
347  * frequency components tend to add more to the magnitude of the
348  * derivative. Note that because only half the components of the
349  * Fourier transform are stored a factor of 2 is added after the
350  * sum. The DC component should only count once, but it does not
351  * contribute anything to the sum so no special case is required to
352  * handle it. */
353 
354  for (k = k_min; k <= k_max; ++k) {
355  double tmp = sum;
356  double f = psd->f0 + k * psd->deltaF;
357  double x;
358 
359  /* make sure that psd is valid at this point */
360  /* if it is 0 then it must be infinity! */
361  if (isinf(psd->data->data[k]) || psd->data->data[k] <= 0.0)
362  continue; /* no contribution from this component */
363 
364  /* what we want to add = f^{-7/3}/S(f) + "error
365  * from last iteration" */
366  x = pow(f, -7.0/3.0) / psd->data->data[k] + e;
367  /* add */
368  sum += x;
369  /* negative of what was actually added */
370  e = tmp - sum;
371  /* what didn't get added, add next time */
372  e += x;
373  }
374 
375  /* because we've only summed the positive frequency components */
376  sum *= 2;
377 
378  sum *= 2.0 * psd->deltaF * prefac;
379 
380  /* from Eq. (3.4b) of FINDCHIRP */
381  A_1_Mpc = -sqrt(5.0 / 24.0 / LAL_PI);
382  A_1_Mpc *= (LAL_G_SI * LAL_MSUN_SI / LAL_C_SI / LAL_C_SI / Mpc);
383  A_1_Mpc *= pow(LAL_PI * LAL_G_SI * LAL_MSUN_SI / LAL_C_SI / LAL_C_SI / LAL_C_SI, -1.0/6.0);
384  A_1_Mpc *= sqrt(mu);
385  A_1_Mpc *= pow(M, 1.0/3.0);
386 
387  return fabs(A_1_Mpc) * sqrt(sum);
388 }
389 
390 /**
391  * @brief Measures the characteristic signal-to-noise ratio of a gravitational
392  * waveform represented in the frequency domain.
393  *
394  * This routine measures the characteristic signal-to-noise ratio of a signal
395  * @p htilde for a detector with a given strain noise power spectral density
396  * @p psd. Only frequency components of the signal between @p f_mim and
397  * @p f_max are included in this measurement. If @p f_min is zero or negative,
398  * no lower bound is imposed. If @p f_max is negative, no upper bound is
399  * imposed.
400  *
401  * The term @e characteristic is used to indicate that this signal-to-noise
402  * ratio is the expected value of the signal-to-noise ratio that would be
403  * measured for that signal my a perfectly-matched filter in a detector having
404  * stationary Gaussian noise with the specified power spectral density. The
405  * signal-to-noise ratio actually recorded by a matched filter is a random
406  * variable that depends on the particular instance of the noise. Thus the
407  * @e characteristic signal-to-noise ratio returned by this routine is a
408  * property of the signal and the statistical properties of the detector noise.
409  *
410  * The @e square of the signal-to-noise ratio is given by
411  * \f[
412  * (\mbox{signal-to-noise ratio})^2 =
413  * 4 \int_{f_{\rm min}}^{f_{\rm max}} \frac{|\tilde{h}(f)|^2}{S_h(f)}\,df
414  * \f]
415  * where \f$S_h(f)\f$ is the one-sided detector strain noise power spectral
416  * density and \f$\tilde{h}(f)\f$ is the Fourier transform of the signal
417  * strain time series \f$h(t)\f$
418  * \f[
419  * \tilde{h}(f) = \int_{-\infty}^\infty h(t) e^{-2\pi ift}\,dt.
420  * \f]
421  * The one-sided strain noise power spectral density is defined by
422  * \f$\langle\tilde{n}(f)\tilde{n}^\ast(f')\rangle=\frac{1}{2}S_h(|f|)\delta(f-f')\f$
423  * where \f$\tilde{n}(f)\f$ is the Fourier transform of the detector noise
424  * process \f$n(t)\f$.
425  *
426  * The discrete versions of these equations are
427  * \f[
428  * (\mbox{signal-to-noise ratio})^2 = 4 \Delta f
429  * \sum_{k=k_{\rm min}}^{k_{\rm max}} |\tilde{h}[k]|^2 / S_h[k]
430  * \f]
431  * where \f$\tilde{h}[k]\f$ for \f$0\le k<N\f$ is the discrete Fourier
432  * transform of the discrete time series \f$h[j]=h(j\Delta t)\f$ for
433  * \f$0\le j<N\f$:
434  * \f[
435  * \tilde{h}[k] = \Delta t \sum_{j=0}^{N-1} h[j] e^{-2\pi ijk/N}
436  * \f]
437  * (note the factor of \f$\Delta t\f$). Here \f$\Delta t\f$ is the sampling
438  * interval in time, \f$\Delta f=1/(N\Delta t)\f$ is the sampling interval in
439  * frequency, and \f$N\f$ is the number of points in the time series. The
440  * discrete one-sided detector strain noise power spectral density is
441  * \f$S_h[k]=2\Delta f\langle|\tilde{n}[k]|^2\rangle\f$ where
442  * \f$\tilde{n}[k]\f$ is the discrete Fourier transform of the detector noise
443  * process \f$n[j]\f$.
444  *
445  * The limits of the summation are \f$k_{\rm min}=f_{\rm min}/\Delta f\f$ and
446  * \f$k_{\rm max}=f_{\rm max}/\Delta f\f$, both rounded to the nearest integer.
447  * If \f$k_{\rm min}\f$ is less than 0, it is set to 0. If \f$k_{\rm max}\f$
448  * is negative or greater than \f$N/2\f$ rounded down, it is set to \f$N/2\f$
449  * rounded down. This ends up double-counting the DC and a possible Nyquist
450  * component, but it is assumed these terms have negligible contribution to the
451  * sum and there is no special case to handle them separately (most likely,
452  * the detector will have no sensitivity to those components anyway).
453  *
454  * This routine will accept frequency series \f$\tilde{h}\f$ and \f$S_h\f$ that
455  * have different frequency resolutions (i.e., different \f$\Delta f\f$). In
456  * this case, a new frequency series \f$S_h\f$ is computed with the same
457  * resolution as \f$\tilde{h}\f$ by interpolation. A simple linear
458  * interpolation in the logarithm of the power spectral density is used. Any
459  * points where the power spectral density is zero are considered to be invalid
460  * and are omitted from the sum.
461  *
462  * @param[in] htilde The Fourier transform of the signal strain.
463  * @param[in] psd The one-sided detector strain noise power spectral density.
464  * @param[in] f_min The lower bound of the frequency band over which the
465  * signal-to-noise ratio will be computed; set to 0 or a negative value for no
466  * lower bound.
467  * @param[in] f_max The upper bound of the frequency band over which the
468  * signal-to-noise ratio will be computed; set to a negative value for
469  * no upper bound.
470  * @returns The characteristic signal to noise ratio.
471  * @retval LAL_REAL8_FAIL_NAN Failure.
472  */
473 double XLALMeasureSNRFD(const COMPLEX16FrequencySeries *htilde, const REAL8FrequencySeries *psd, double f_min, double f_max)
474 {
475  LALUnit snrUnits;
477  size_t k, k_min, k_max;
478  double e = 0.0;
479  double sum = 0.0;
480  double prefac = 1.0;
481 
482  XLAL_CHECK_REAL8(htilde && psd, XLAL_EFAULT);
483  XLAL_CHECK_REAL8(htilde->f0 >= 0.0 && psd->f0 >= 0.0, XLAL_EINVAL, "Can only handle non-negative frequencies");
484 
485  /* make sure that snr units will be dimensionless,
486  *
487  * snr ~ deltaF * |htilde|^2 / psd
488  *
489  * and also compute snr unit prefactor */
490  XLALUnitSquare(&snrUnits, &htilde->sampleUnits);
491  XLALUnitDivide(&snrUnits, &snrUnits, &psd->sampleUnits);
492  XLALUnitMultiply(&snrUnits, &snrUnits, &lalHertzUnit);
493  if (!XLALUnitIsDimensionless(&snrUnits))
494  XLAL_ERROR_REAL8(XLAL_EINVAL, "Incompatible frequency series: incorrect sample units");
495  else
496  prefac = XLALUnitPrefactor(&snrUnits);
497 
498  /* find the (inclusive) limits of the summation */
499  if (f_min < 0 || f_min <= htilde->f0)
500  k_min = 0;
501  else
502  k_min = round((f_min - htilde->f0)/htilde->deltaF);
503 
504  if (f_max < 0 || f_max >= htilde->f0 + htilde->data->length * htilde->deltaF)
505  k_max = htilde->data->length - 1;
506  else {
507  k_max = round((f_max - htilde->f0)/htilde->deltaF);
508  if (k_max >= htilde->data->length - 1) {
509  k_max = htilde->data->length - 1;
510  }
511  }
512 
513  /* interpolate psd if necessary */
514  S = create_interpolated_psd(htilde->f0, htilde->deltaF, htilde->data->length, psd);
515  if (!S)
517 
518  /* Kahan's compensated summation algorithm. The summation is done
519  * from lowest to highest frequency under the assumption that high
520  * frequency components tend to add more to the magnitude of the
521  * derivative. Note that because only half the components of the
522  * Fourier transform are stored a factor of 2 is added after the
523  * sum. The DC component should only count once, but it does not
524  * contribute anything to the sum so no special case is required to
525  * handle it. */
526 
527  for (k = k_min; k <= k_max; ++k) {
528  double tmp = sum;
529  double x;
530 
531  /* make sure that psd is valid at this point */
532  /* if it is 0 then it must be infinity! */
533  if (isinf(S->data->data[k]) || S->data->data[k] <= 0.0)
534  continue; /* no contribution from this component */
535 
536  /* what we want to add = |\tilde{h}(f)|^{2}/S(f) + "error
537  * from last iteration" */
538  x = pow(cabs(htilde->data->data[k]), 2) / S->data->data[k] + e;
539  /* add */
540  sum += x;
541  /* negative of what was actually added */
542  e = tmp - sum;
543  /* what didn't get added, add next time */
544  e += x;
545  }
546 
547  /* because we've only summed the positive frequency components,
548  * multiply sum by two; note that we assume that the DC and Nyquist
549  * components do not contribute significantly to the sum, so there is
550  * no special case to handle these separately */
551  sum *= 2;
552 
553  sum *= 2.0 * htilde->deltaF * prefac;
554 
555  if (S != psd)
557 
558  return sqrt(sum);
559 }
560 
561 /**
562  * @brief Measures the characteristic signal-to-noise ratio of a gravitational
563  * waveform.
564  *
565  * This routine measures the characteristic signal-to-noise ratio of a signal
566  * @p h for a detector with a given strain noise power spectral density @p psd.
567  * Only frequency components of the signal between @p f_mim and @p f_max are
568  * included in this measurement. If @p f_min is zero or negative, no lower
569  * bound is imposed. If @p f_max is negative, no upper bound is imposed.
570  *
571  * A copy of the strain time series @p h is zero padded up to the next power of
572  * two in length and then Fourier transformed; the routine XLALMeasureSNRFD()
573  * is then used to compute the characteristic signal-to-noise ratio. See
574  * XLALMeasureSNRFD() for further details.
575  *
576  * @param[in] h The strain time series of the signal.
577  * @param[in] psd The one-sided detector strain noise power spectral density.
578  * @param[in] f_min The lower bound of the frequency band over which the
579  * signal-to-noise ratio will be computed; set to 0 or a negative value for no
580  * lower bound.
581  * @param[in] f_max The upper bound of the frequency band over which the
582  * signal-to-noise ratio will be computed; set to a negative value for
583  * no upper bound.
584  * @returns The characteristic signal to noise ratio.
585  * @retval LAL_REAL8_FAIL_NAN Failure.
586  */
587 double XLALMeasureSNR(const REAL8TimeSeries *h, const REAL8FrequencySeries *psd, double f_min, double f_max)
588 {
589  REAL8FFTPlan *plan;
590  REAL8TimeSeries *hpadded;
591  COMPLEX16FrequencySeries *htilde;
592  size_t new_length;
593  int length_exp;
594  double snr;
595 
596  XLAL_CHECK_REAL8(h && psd, XLAL_EFAULT);
597 
598  /* zero-pad to the next power of two in length */
599  frexp(h->data->length, &length_exp);
600  new_length = (size_t)ldexp(1.0, length_exp);
601 
602  /* create a new time series padded at the end.
603  * first, copy the series, then resize it. */
604  hpadded = XLALCutREAL8TimeSeries(h, 0, h->data->length);
605  hpadded = XLALResizeREAL8TimeSeries(hpadded, 0, new_length);
606  htilde = XLALCreateCOMPLEX16FrequencySeries(NULL, &hpadded->epoch, 0.0, 0.0, &lalDimensionlessUnit, hpadded->data->length / 2 + 1);
607  plan = XLALCreateForwardREAL8FFTPlan(hpadded->data->length, 0);
608  if (!hpadded || !plan || !htilde) {
613  }
614 
615  if (XLALREAL8TimeFreqFFT(htilde, hpadded, plan) < 0) {
620  }
623 
624  snr = XLALMeasureSNRFD(htilde, psd, f_min, f_max);
625 
627 
628  if (XLAL_IS_REAL8_FAIL_NAN(snr))
630 
631  return snr;
632 }
static REAL8FrequencySeries * create_interpolated_psd(double f0, double deltaF, size_t length, const REAL8FrequencySeries *old)
Creates a new interpolated psd from an existing psd (if necessary).
Definition: LALSimUtils.c:54
static REAL8 sum(REAL8 *arr1, REAL8 *arr2, int k)
REAL8 M
Definition: bh_qnmode.c:133
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
static LALUnit strainSquaredPerHertzUnit
REAL8FrequencySeries * XLALCreateREAL8FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
REAL8FrequencySeries * XLALResizeREAL8FrequencySeries(REAL8FrequencySeries *series, int first, size_t length)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
REAL8FrequencySeries * XLALCutREAL8FrequencySeries(const REAL8FrequencySeries *series, size_t first, size_t length)
void XLALDestroyREAL8FrequencySeries(REAL8FrequencySeries *series)
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
#define LAL_C_SI
#define LAL_MSUN_SI
#define LAL_REAL8_EPS
#define LAL_PI
#define LAL_MTSUN_SI
#define LAL_PC_SI
#define LAL_G_SI
double XLALMeasureStandardSirenHorizonDistance(const REAL8FrequencySeries *psd, double f_min, double f_max)
Computes the horizon distance for a binary neutron star standard siren signal for a given one-sided d...
Definition: LALSimUtils.c:236
double XLALMeasureStandardSirenSenseMonitorRange(const REAL8FrequencySeries *psd, double f_min, double f_max)
Computes the sense-monitor range for a binary neutron star standard siren signal for a given one-side...
Definition: LALSimUtils.c:179
#define LAL_HORIZON_DISTANCE_OVER_SENSEMON_RANGE
Ratio of horizon distance to sense-monitor range.
Definition: LALSimUtils.h:56
double XLALMeasureSNR(const REAL8TimeSeries *h, const REAL8FrequencySeries *psd, double f_min, double f_max)
Measures the characteristic signal-to-noise ratio of a gravitational waveform.
Definition: LALSimUtils.c:587
double XLALMeasureSNRFD(const COMPLEX16FrequencySeries *htilde, const REAL8FrequencySeries *psd, double f_min, double f_max)
Measures the characteristic signal-to-noise ratio of a gravitational waveform represented in the freq...
Definition: LALSimUtils.c:473
double XLALMeasureStandardSirenSNR(const REAL8FrequencySeries *psd, double f_min, double f_max)
Computes the characteristic signal-to-noise for a binary neutron star standard siren signal located a...
Definition: LALSimUtils.c:297
void XLALDestroyREAL8FFTPlan(REAL8FFTPlan *plan)
REAL8FFTPlan * XLALCreateForwardREAL8FFTPlan(UINT4 size, int measurelvl)
int XLALREAL8TimeFreqFFT(COMPLEX16FrequencySeries *freq, const REAL8TimeSeries *tser, const REAL8FFTPlan *plan)
REAL8TimeSeries * XLALResizeREAL8TimeSeries(REAL8TimeSeries *series, int first, size_t length)
REAL8TimeSeries * XLALCutREAL8TimeSeries(const REAL8TimeSeries *series, size_t first, size_t length)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
int XLALUnitIsDimensionless(const LALUnit *unit)
REAL8 XLALUnitPrefactor(const LALUnit *unit)
const LALUnit lalHertzUnit
const LALUnit lalDimensionlessUnit
LALUnit * XLALUnitDivide(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
LALUnit * XLALUnitMultiply(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
LALUnit * XLALUnitSquare(LALUnit *output, const LALUnit *input)
#define XLAL_ERROR_REAL8(...)
#define XLAL_ERROR(...)
#define XLAL_CHECK_REAL8(assertion,...)
#define XLAL_IS_REAL8_FAIL_NAN(val)
XLAL_EFAULT
XLAL_EFUNC
XLAL_EDOM
XLAL_EINVAL
list x
list mu
COMPLEX16Sequence * data
COMPLEX16 * data
REAL8Sequence * data
CHAR name[LALNameLength]
REAL8Sequence * data
LIGOTimeGPS epoch
REAL8 * data
double f_min
Definition: unicorn.c:22
double f_max
Definition: unicorn.c:23