LALSimulation  5.4.0.1-fe68b98
LALSimSGWB.c
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2011 Jolien Creighton
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 <complex.h>
21 #include <math.h>
22 #include <stdio.h>
23 #include <gsl/gsl_linalg.h>
24 #include <gsl/gsl_rng.h>
25 #include <gsl/gsl_randist.h>
26 
27 #include <lal/LALConstants.h>
28 #include <lal/LALDetectors.h>
29 #include <lal/Date.h>
30 #include <lal/FrequencySeries.h>
31 #include <lal/Sequence.h>
32 #include <lal/TimeFreqFFT.h>
33 #include <lal/Units.h>
34 #include <lal/LALSimSGWB.h>
35 
36 #include <lal/LALSimReadData.h>
37 
38 /*
39  * This routine generates a single segment of data. Note that this segment is
40  * generated in the frequency domain and is inverse Fourier transformed into
41  * the time domain; consequently the data is periodic in the time domain.
42  */
43 static int XLALSimSGWBSegment(REAL8TimeSeries **h, const LALDetector *detectors, size_t numDetectors, const REAL8FrequencySeries *OmegaGW, double H0, gsl_rng *rng)
44 {
45 # define CLEANUP_AND_RETURN(errnum) do { \
46  if (htilde) for (i = 0; i < numDetectors; ++i) XLALDestroyCOMPLEX16FrequencySeries(htilde[i]); \
47  XLALFree(htilde); XLALDestroyREAL8FFTPlan(plan); gsl_matrix_free(R); \
48  if (errnum) XLAL_ERROR(errnum); else return 0; \
49  } while (0)
50  REAL8FFTPlan *plan = NULL;
51  COMPLEX16FrequencySeries **htilde = NULL;
52  gsl_matrix *R = NULL;
54  double psdfac;
55  double deltaF;
56  size_t length;
57  size_t i, j, k;
58 
59  epoch = h[0]->epoch;
60  length = h[0]->data->length;
61  deltaF = 1.0 / (length * h[0]->deltaT);
62  psdfac = 0.3 * pow(H0 / LAL_PI, 2.0);
63 
64  R = gsl_matrix_alloc(numDetectors, numDetectors);
65  if (! R)
67 
68  plan = XLALCreateReverseREAL8FFTPlan(length, 0);
69  if (! plan)
71 
72  /* allocate frequency series for the various detector strains */
73  htilde = LALCalloc(numDetectors, sizeof(*htilde));
74  if (! htilde)
76  for (i = 0; i < numDetectors; ++i) {
77  htilde[i] = XLALCreateCOMPLEX16FrequencySeries(h[i]->name, &epoch, 0.0, deltaF, &lalSecondUnit, length/2 + 1);
78  if (! htilde[i])
80 
81  /* correct units */
82  XLALUnitMultiply(&htilde[i]->sampleUnits, &htilde[i]->sampleUnits, &h[i]->sampleUnits);
83 
84  /* set data to zero */
85  memset(htilde[i]->data->data, 0, htilde[i]->data->length * sizeof(*htilde[i]->data->data));
86  }
87 
88  /* compute frequencies (excluding DC and Nyquist) */
89  for (k = 1; k < length/2; ++k) {
90  double f = k * deltaF;
91  double sigma = 0.5 * sqrt(psdfac * OmegaGW->data->data[k] * pow(f, -3.0) / deltaF);
92 
93  /* construct correlation matrix at this frequency */
94  /* diagonal elements of correlation matrix are unity */
95  gsl_matrix_set_identity(R);
96  /* now do the off-diagonal elements */
97  for (i = 0; i < numDetectors; ++i)
98  for (j = i + 1; j < numDetectors; ++j) {
100  /* if the two sites are the same, the overlap reduciton
101  * function will be unity, but this will cause problems
102  * for the cholesky decomposition; a hack is to make it
103  * unity only to single precision */
104  if (fabs(Rij - 1.0) < LAL_REAL4_EPS)
105  Rij = 1.0 - LAL_REAL4_EPS;
106 
107  gsl_matrix_set(R, i, j, Rij);
108  gsl_matrix_set(R, j, i, Rij); /* it is symmetric */
109  }
110 
111  /* perform Cholesky decomposition */
112  gsl_linalg_cholesky_decomp(R);
113 
114  /* generate numDetector random numbers (both re and im parts) and use
115  * lower-diagonal part of Cholesky decomposition to create correlations */
116  for (j = 0; j < numDetectors; ++j) {
117  double re = gsl_ran_gaussian_ziggurat(rng, sigma);
118  double im = gsl_ran_gaussian_ziggurat(rng, sigma);
119  for (i = j; i < numDetectors; ++i) {
120  htilde[i]->data->data[k] += gsl_matrix_get(R, i, j) * re;
121  htilde[i]->data->data[k] += I * gsl_matrix_get(R, i, j) * im;
122  }
123  }
124  }
125 
126  /* now go back to the time domain */
127  for (i = 0; i < numDetectors; ++i)
128  XLALREAL8FreqTimeFFT(h[i], htilde[i], plan);
129 
130  /* normal exit */
132 # undef CLEANUP_AND_RETURN
133 }
134 
135 /**
136  * @addtogroup LALSimSGWB_c
137  * @brief Routines to compute a stochastic gravitational-wave background
138  * power spectrum and to produce a continuous stream of simulated
139  * gravitational-wave detector strain.
140  * @{
141  */
142 
143 /**
144  * Creates a frequency series that contains a flat SGWB spectrum with the
145  * specified power Omega0 above some low frequency cutoff flow.
146  */
148  double Omega0, /**< [in] sgwb spectrum power (dimensionless) */
149  double flow, /**< [in] low frequncy cutoff of SGWB spectrum (Hz) */
150  double deltaF, /**< [in] frequency bin width (Hz) */
151  size_t length /**< [in] number of frequency bins */
152 )
153 {
154  REAL8FrequencySeries *OmegaGW;
155  LIGOTimeGPS epoch = {0, 0};
156  size_t klow = flow / deltaF;
157  size_t k;
158  OmegaGW = XLALCreateREAL8FrequencySeries("OmegaGW", &epoch, 0.0, deltaF, &lalDimensionlessUnit, length);
159  /* zero DC component */
160  OmegaGW->data->data[0] = 0.0;
161  /* zero up to low frequency cutoff */
162  for (k = 1; k < klow; ++k)
163  OmegaGW->data->data[k] = 0.0;
164  /* set remaining components */
165  for (; k < length - 1; ++k)
166  OmegaGW->data->data[k] = Omega0;
167  /* zero Nyquist component */
168  OmegaGW->data->data[length - 1] = 0.0;
169  return OmegaGW;
170 }
171 
172 
173 /**
174  * Creates a frequency series that contains a power law SGWB spectrum with the
175  * specified power Omegaref at reference frequency fref and specified power law
176  * power alpha above some low frequency cutoff flow.
177  */
179  double Omegaref, /**< [in] sgwb spectrum power at reference frequency (dimensionless) */
180  double alpha, /**< [in] sgwb spectrum power law power */
181  double fref, /**< [in] reference frequency (Hz) */
182  double flow, /**< [in] low frequncy cutoff of SGWB spectrum (Hz) */
183  double deltaF, /**< [in] frequency bin width (Hz) */
184  size_t length /**< [in] number of frequency bins */
185 )
186 {
187  REAL8FrequencySeries *OmegaGW;
188  LIGOTimeGPS epoch = {0, 0};
189  size_t klow = flow / deltaF;
190  size_t k;
191  OmegaGW = XLALCreateREAL8FrequencySeries("OmegaGW", &epoch, 0.0, deltaF, &lalDimensionlessUnit, length);
192  /* zero DC component */
193  OmegaGW->data->data[0] = 0.0;
194  /* zero up to low frequency cutoff */
195  for (k = 1; k < klow; ++k)
196  OmegaGW->data->data[k] = 0.0;
197  /* set remaining components */
198  for (; k < length - 1; ++k)
199  OmegaGW->data->data[k] = Omegaref * pow(k * deltaF / fref, alpha);
200  /* zero Nyquist component */
201  OmegaGW->data->data[length - 1] = 0.0;
202  return OmegaGW;
203 }
204 
205 
206 /**
207  * Routine that may be used to generate sequential segments of stochastic
208  * background gravitational wave signals for a network of detectors with a
209  * specified stride from one segment to the next.
210  *
211  * The spectrum is specified by the frequency series OmegaGW.
212  *
213  * Calling instructions: for the first call, set stride = 0; subsequent calls
214  * should pass the same time series and have non-zero stride. This routine
215  * will advance the time series by an amount given by the stride and will
216  * generate new data so that the data is continuous from one segment to the
217  * next. For example: the following routine will output a continuous stream of
218  * stochastic background signals with a "flat" (OmegaGW = const) spectrum for
219  * the HLV network.
220  *
221  * @code
222  * #include <stdio.h>
223  * #include <gsl/gsl_rng.h>
224  * #include <lal/LALStdlib.h>
225  * #include <lal/LALDetectors.h>
226  * #include <lal/FrequencySeries.h>
227  * #include <lal/TimeSeries.h>
228  * #include <lal/Units.h>
229  * #include <lal/LALSimSGWB.h>
230  * int mksgwbdata(void)
231  * {
232  * const double flow = 40.0; // 40 Hz low frequency cutoff
233  * const double duration = 16.0; // 16 second segments
234  * const double srate = 16384.0; // sampling rate in Hertz
235  * const double Omega0 = 1e-6; // fraction of critical energy in GWs
236  * const double H0 = 0.72 * LAL_H0FAC_SI; // Hubble's constant in seconds
237  * const LALDetector H1 = lalCachedDetectors[LAL_LHO_4K_DETECTOR]; // Hanford
238  * const LALDetector L1 = lalCachedDetectors[LAL_LLO_4K_DETECTOR]; // Livingston
239  * const LALDetector V1 = lalCachedDetectors[LAL_VIRGO_DETECTOR]; // Virgo
240  * LALDetector detectors[3] = {H1, L1, V1}; // the network of detectors
241  * size_t length = duration * srate; // segment length
242  * size_t stride = length / 2; // stride between segments
243  * LIGOTimeGPS epoch = { 0, 0 };
244  * REAL8FrequencySeries *OmegaGW; // the spectrum of the SGWB
245  * REAL8TimeSeries *h[3]; // the strain induced in the network of detectors
246  * gsl_rng *rng;
247  * gsl_rng_env_setup();
248  * rng = gsl_rng_alloc(gsl_rng_default);
249  * h[0] = XLALCreateREAL8TimeSeries("H1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
250  * h[1] = XLALCreateREAL8TimeSeries("L1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
251  * h[2] = XLALCreateREAL8TimeSeries("V1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
252  OmegaGW = XLALSimSGWBOmegaGWFlatSpectrum(Omega0, flow, deltaF, seglen/2 + 1);
253  * XLALSimSGWB(h, detectors, 3, 0, Omega0, flow, H0, rng); // first time to initialize
254  * while (1) { // infinite loop
255  * size_t j;
256  * for (j = 0; j < stride; ++j) // output first stride points
257  * printf("%.9f\t%e\t%e\t%e\n", XLALGPSGetREAL8(&h[0]->epoch) + j / srate, h[0]->data->data[j], h[1]->data->data[j], h[2]->data->data[j]);
258  * XLALSimSGWB(h, detectors, 3, stride, Omega0, flow, H0, rng); // make more data
259  * }
260  * @endcode
261  *
262  * If only one single segment of data is required, set stride to be the length
263  * of the timeseries data vector. This will make a single segment of data
264  * that is *not* periodic (also, in this case it will not advance the epoch of
265  * the timeseries).
266  *
267  * @note
268  * - If stride = 0, initialize h by generating one (periodic) realization of
269  * noise; subsequent calls should have non-zero stride.
270  * - If stride = h->data->length then generate one segment of non-periodic
271  * noise by generating two different realizations and feathering them
272  * together.
273  *
274  * @warning Only the first stride points are valid.
275  */
277  REAL8TimeSeries **h, /**< [in/out] array of sgwb timeseries for detector network */
278  const LALDetector *detectors, /**< [in] array of detectors in network */
279  size_t numDetectors, /**< [in] number of detectors in network */
280  size_t stride, /**< [in] stride (samples) */
281  const REAL8FrequencySeries *OmegaGW, /**< [in] sgwb spectrum frequeny series */
282  double H0, /**< [in] Hubble's constant (s) */
283  gsl_rng *rng /**< [in] GSL random number generator */
284 )
285 {
286 # define CLEANUP_AND_RETURN(errnum) do { \
287  if (overlap) for (i = 0; i < numDetectors; ++i) XLALDestroyREAL8Sequence(overlap[i]); \
288  XLALFree(overlap); \
289  if (errnum) XLAL_ERROR(errnum); else return 0; \
290  } while (0)
291  REAL8Vector **overlap = NULL;
293  size_t length;
294  double deltaT;
295  size_t i, j;
296 
297  length = h[0]->data->length;
298  deltaT = h[0]->deltaT;
299  epoch = h[0]->epoch;
300 
301  /* make sure all the lengths and other metadata are the same */
302  for (i = 1; i < numDetectors; ++i)
303  if (h[i]->data->length != length
304  || fabs(h[i]->deltaT - deltaT) > LAL_REAL8_EPS
305  || XLALGPSCmp(&epoch, &h[i]->epoch))
307 
308  /* make sure that the resolution of the frequency series is
309  * commensurate with the requested time series */
310  if (length/2 + 1 != OmegaGW->data->length
311  || (size_t)floor(0.5 + 1.0/(deltaT * OmegaGW->deltaF)) != length)
313 
314  /* stride cannot be longer than data length */
315  if (stride > length)
317 
318  if (stride == 0) { /* generate segment with no feathering */
319  XLALSimSGWBSegment(h, detectors, numDetectors, OmegaGW, H0, rng);
320  return 0;
321  } else if (stride == length) {
322  /* will generate two independent noise realizations
323  * and feather them together with full overlap */
324  XLALSimSGWBSegment(h, detectors, numDetectors, OmegaGW, H0, rng);
325  stride = 0;
326  }
327 
328  overlap = LALCalloc(numDetectors, sizeof(*overlap));
329  if (! overlap)
331  for (i = 0; i < numDetectors; ++i) {
332  overlap[i] = XLALCreateREAL8Sequence(length - stride);
333  if (! overlap[i])
335  /* copy overlap region between the old and the new data to temporary storage */
336  memcpy(overlap[i]->data, h[i]->data->data + stride, overlap[i]->length*sizeof(*overlap[i]->data));
337  }
338 
339  if (XLALSimSGWBSegment(h, detectors, numDetectors, OmegaGW, H0, rng))
341 
342  /* feather old data in overlap region with new data */
343  for (j = 0; j < length - stride; ++j) {
344  double x = cos(LAL_PI*j/(2.0 * (length - stride)));
345  double y = sin(LAL_PI*j/(2.0 * (length - stride)));
346  for (i = 0; i < numDetectors; ++i)
347  h[i]->data->data[j] = x*overlap[i]->data[j] + y*h[i]->data->data[j];
348  }
349 
350  /* advance time */
351  for (i = 0; i < numDetectors; ++i)
352  XLALGPSAdd(&h[i]->epoch, stride * deltaT);
353 
354  /* success */
356 # undef CLEANUP_AND_RETURN
357 }
358 
359 
360 /**
361  * Routine that may be used to generate sequential segments of stochastic
362  * background gravitational wave signals for a network of detectors with a
363  * specified stride from one segment to the next.
364  *
365  * The spectrum is flat for frequencies above flow with power given by Omega0,
366  * and zero for frequencies below the low frequency cutoff flow.
367  *
368  * Calling instructions: for the first call, set stride = 0; subsequent calls
369  * should pass the same time series and have non-zero stride. This routine
370  * will advance the time series by an amount given by the stride and will
371  * generate new data so that the data is continuous from one segment to the
372  * next. For example: the following routine will output a continuous stream of
373  * stochastic background signals with a "flat" (OmegaGW = const) spectrum for
374  * the HLV network.
375  *
376  * @code
377  * #include <stdio.h>
378  * #include <gsl/gsl_rng.h>
379  * #include <lal/LALStdlib.h>
380  * #include <lal/LALDetectors.h>
381  * #include <lal/FrequencySeries.h>
382  * #include <lal/TimeSeries.h>
383  * #include <lal/Units.h>
384  * #include <lal/LALSimSGWB.h>
385  * int mkgwbdata_flat(void)
386  * {
387  * const double flow = 40.0; // 40 Hz low frequency cutoff
388  * const double duration = 16.0; // 16 second segments
389  * const double srate = 16384.0; // sampling rate in Hertz
390  * const double Omega0 = 1e-6; // fraction of critical energy in GWs
391  * const double H0 = 0.72 * LAL_H0FAC_SI; // Hubble's constant in seconds
392  * const LALDetector H1 = lalCachedDetectors[LAL_LHO_4K_DETECTOR]; // Hanford
393  * const LALDetector L1 = lalCachedDetectors[LAL_LLO_4K_DETECTOR]; // Livingston
394  * const LALDetector V1 = lalCachedDetectors[LAL_VIRGO_DETECTOR]; // Virgo
395  * LALDetector detectors[3] = {H1, L1, V1}; // the network of detectors
396  * size_t length = duration * srate; // segment length
397  * size_t stride = length / 2; // stride between segments
398  * LIGOTimeGPS epoch = { 0, 0 };
399  * REAL8TimeSeries *h[3]; // the strain induced in the network of detectors
400  * gsl_rng *rng;
401  * gsl_rng_env_setup();
402  * rng = gsl_rng_alloc(gsl_rng_default);
403  * h[0] = XLALCreateREAL8TimeSeries("H1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
404  * h[1] = XLALCreateREAL8TimeSeries("L1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
405  * h[2] = XLALCreateREAL8TimeSeries("V1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
406  * XLALSimSGWBFlatSpectrum(h, detectors, 3, 0, Omega0, flow, H0, rng); // first time to initialize
407  * while (1) { // infinite loop
408  * size_t j;
409  * for (j = 0; j < stride; ++j) // output first stride points
410  * printf("%.9f\t%e\t%e\t%e\n", XLALGPSGetREAL8(&h[0]->epoch) + j / srate, h[0]->data->data[j], h[1]->data->data[j], h[2]->data->data[j]);
411  * XLALSimSGWBFlatSpectrum(h, detectors, 3, stride, Omega0, flow, H0, rng); // make more data
412  * }
413  * @endcode
414  *
415  * If only one single segment of data is required, set stride to be the length
416  * of the timeseries data vector. This will make a single segment of data
417  * that is *not* periodic (also, in this case it will not advance the epoch of
418  * the timeseries).
419  *
420  * @note
421  * - If stride = 0, initialize h by generating one (periodic) realization of
422  * noise; subsequent calls should have non-zero stride.
423  * - If stride = h->data->length then generate one segment of non-periodic
424  * noise by generating two different realizations and feathering them
425  * together.
426  *
427  * @warning Only the first stride points are valid.
428  */
430  REAL8TimeSeries **h, /**< [in/out] array of sgwb timeseries for detector network */
431  const LALDetector *detectors, /**< [in] array of detectors in network */
432  size_t numDetectors, /**< [in] number of detectors in network */
433  size_t stride, /**< [in] stride (samples) */
434  double Omega0, /**< [in] flat sgwb spectrum power (dimensionless) */
435  double flow, /**< [in] low frequency cutoff (Hz) */
436  double H0, /**< [in] Hubble's constant (s) */
437  gsl_rng *rng /**< [in] GSL random number generator */
438 )
439 {
440  REAL8FrequencySeries *OmegaGW;
441  size_t length;
442  double deltaF;
443  length = h[0]->data->length;
444  deltaF = 1.0/(length * h[0]->deltaT);
445  OmegaGW = XLALSimSGWBOmegaGWFlatSpectrum(Omega0, flow, deltaF, length/2 + 1);
446  if (! OmegaGW)
448  if (XLALSimSGWB(h, detectors, numDetectors, stride, OmegaGW, H0, rng)) {
451  }
453  return 0;
454 }
455 
456 
457 /**
458  * Routine that may be used to generate sequential segments of stochastic
459  * background gravitational wave signals for a network of detectors with a
460  * specified stride from one segment to the next.
461  *
462  * The spectrum is a power law with power alpha for frequencies above flow with
463  * power given by Omegaref at the reference frequency fref, and zero for
464  * frequencies below the low frequency cutoff flow.
465  *
466  * Calling instructions: for the first call, set stride = 0; subsequent calls
467  * should pass the same time series and have non-zero stride. This routine
468  * will advance the time series by an amount given by the stride and will
469  * generate new data so that the data is continuous from one segment to the
470  * next. For example: the following routine will output a continuous stream of
471  * stochastic background signals with a "flat" (OmegaGW = const) spectrum for
472  * the HLV network.
473  *
474  * @code
475  * #include <stdio.h>
476  * #include <gsl/gsl_rng.h>
477  * #include <lal/LALStdlib.h>
478  * #include <lal/LALDetectors.h>
479  * #include <lal/FrequencySeries.h>
480  * #include <lal/TimeSeries.h>
481  * #include <lal/Units.h>
482  * #include <lal/LALSimSGWB.h>
483  * int mksgwbdata_powerlaw(void)
484  * {
485  * const double flow = 40.0; // 40 Hz low frequency cutoff
486  * const double duration = 16.0; // 16 second segments
487  * const double srate = 16384.0; // sampling rate in Hertz
488  * const double Omegaref = 1e-6; // fraction of critical energy in GWs
489  * const double fref = 100; // reference frequency in Hertz
490  * const double alpha = 3.0; // sgwb spectrum power law power
491  * const double H0 = 0.72 * LAL_H0FAC_SI; // Hubble's constant in seconds
492  * const LALDetector H1 = lalCachedDetectors[LAL_LHO_4K_DETECTOR]; // Hanford
493  * const LALDetector L1 = lalCachedDetectors[LAL_LLO_4K_DETECTOR]; // Livingston
494  * const LALDetector V1 = lalCachedDetectors[LAL_VIRGO_DETECTOR]; // Virgo
495  * LALDetector detectors[3] = {H1, L1, V1}; // the network of detectors
496  * size_t length = duration * srate; // segment length
497  * size_t stride = length / 2; // stride between segments
498  * LIGOTimeGPS epoch = { 0, 0 };
499  * REAL8TimeSeries *h[3]; // the strain induced in the network of detectors
500  * gsl_rng *rng;
501  * gsl_rng_env_setup();
502  * rng = gsl_rng_alloc(gsl_rng_default);
503  * h[0] = XLALCreateREAL8TimeSeries("H1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
504  * h[1] = XLALCreateREAL8TimeSeries("L1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
505  * h[2] = XLALCreateREAL8TimeSeries("V1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
506  * XLALSimSGWBPowerLawSpectrum(h, detectors, 3, 0, Omegaref, alpha, fref, flow, H0, rng); // first time to initialize
507  * while (1) { // infinite loop
508  * size_t j;
509  * for (j = 0; j < stride; ++j) // output first stride points
510  * printf("%.9f\t%e\t%e\t%e\n", XLALGPSGetREAL8(&h[0]->epoch) + j / srate, h[0]->data->data[j], h[1]->data->data[j], h[2]->data->data[j]);
511  * XLALSimSGWBPowerLawSpectrum(h, detectors, 3, stride, Omegaref, alpha, fref, flow, H0, rng); // make more data
512  * }
513  * @endcode
514  *
515  * If only one single segment of data is required, set stride to be the length
516  * of the timeseries data vector. This will make a single segment of data
517  * that is *not* periodic (also, in this case it will not advance the epoch of
518  * the timeseries).
519  *
520  * @note
521  * - If stride = 0, initialize h by generating one (periodic) realization of
522  * noise; subsequent calls should have non-zero stride.
523  * - If stride = h->data->length then generate one segment of non-periodic
524  * noise by generating two different realizations and feathering them
525  * together.
526  *
527  * @warning Only the first stride points are valid.
528  */
530  REAL8TimeSeries **h, /**< [in/out] array of sgwb timeseries for detector network */
531  const LALDetector *detectors, /**< [in] array of detectors in network */
532  size_t numDetectors, /**< [in] number of detectors in network */
533  size_t stride, /**< [in] stride (samples) */
534  double Omegaref, /**< [in] sgwb spectrum power at reference frequency (dimensionless) */
535  double alpha, /**< [in] sgwb spectrum power power law */
536  double fref, /**< [in] sgwb spectrum reference frequency (Hz) */
537  double flow, /**< [in] low frequency cutoff (Hz) */
538  double H0, /**< [in] Hubble's constant (s) */
539  gsl_rng *rng /**< [in] GSL random number generator */
540 )
541 {
542  REAL8FrequencySeries *OmegaGW;
543  size_t length;
544  double deltaF;
545  length = h[0]->data->length;
546  deltaF = 1.0/(length * h[0]->deltaT);
547  OmegaGW = XLALSimSGWBOmegaGWPowerLawSpectrum(Omegaref, alpha, fref, flow, deltaF, length/2 + 1);
548  if (! OmegaGW)
550  if (! XLALSimSGWB(h, detectors, numDetectors, stride, OmegaGW, H0, rng)) {
553  }
555  return 0;
556 }
557 
559  const char *fname,
560  size_t length
561 )
562 {
563  double *f;
564  double *Omega; /**< [in] sgwb numerical power spectrum */
565  double flow;
566  double fk;
567  double Omegak;
568  double x;
569  double deltaF;
570  size_t N;
571  size_t klow;
572  size_t k;
573  size_t i;
574  REAL8FrequencySeries *OmegaGW;
575  LIGOTimeGPS epoch = {0, 0};
576  LALFILE *fp;
577 
578  /* read the file into Omega and f */
579  fp = XLALSimReadDataFileOpen(fname);
580  if (!fp)
582 
583  N = XLALSimReadDataFile2Col(&f, &Omega, fp);
584  XLALFileClose(fp);
585  if (N == (size_t)(-1))
587 
588  flow = f[0]; /**< [in] low frequncy cutoff of SGWB spectrum (Hz) */
589  deltaF = f[N-1]/(length - 2); /**< [in] frequency bin width (Hz) */
590 
591  for (i = 0; i < N; ++i)
592  Omega[i] = log(Omega[i]);
593 
594  klow = flow / deltaF;
595 
596  OmegaGW = XLALCreateREAL8FrequencySeries("OmegaGW", &epoch, 0.0, deltaF, &lalDimensionlessUnit, length);
597 
598  /* zero DC component */
599  OmegaGW->data->data[0] = 0.0;
600  /* zero up to low frequency cutoff */
601  for (k = 1; k < klow; ++k)
602  OmegaGW->data->data[k] = 0.0;
603 
604  i = 1;
605  for (; k < length - 1; ++k) {
606  fk = flow + k * deltaF;
607  while (f[i] < fk && i < N - 1)
608  ++i;
609  x = (f[i] - fk) / (f[i] - f[i-1]);
610  Omegak = x * Omega[i-1] + (1.0 - x) * Omega[i];
611  OmegaGW->data->data[k] = exp(Omegak);
612  }
613  /* zero Nyquist component */
614  OmegaGW->data->data[length - 1] = 0.0;
615 
616  return OmegaGW;
617 }
618 
619 
620 /** @} */
621 
622 /*
623  *
624  * TEST CODE
625  *
626  */
627 
628 #if 0
629 
630 #include <stdio.h>
631 #include <lal/TimeSeries.h>
632 
633 /* Example routine listed in documentation. */
634 int mksgwbdata(void)
635 {
636  const double flow = 40.0; // 40 Hz low frequency cutoff
637  const double duration = 16.0; // 16 second segments
638  const double srate = 16384.0; // sampling rate in Hertz
639  const double Omega0 = 1e-6; // fraction of critical energy in GWs
640  const double H0 = 0.72 * LAL_H0FAC_SI; // Hubble's constant in seconds
644  LALDetector detectors[3] = {H, L, V};
645  size_t length = duration * srate; // segment length
646  size_t stride = length / 2; // stride between segments
647  LIGOTimeGPS epoch = { 0, 0 };
648  REAL8TimeSeries *seg[3];
649  gsl_rng *rng;
650  gsl_rng_env_setup();
651  rng = gsl_rng_alloc(gsl_rng_default);
652  seg[0] = XLALCreateREAL8TimeSeries("H1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
653  seg[1] = XLALCreateREAL8TimeSeries("L1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
654  seg[2] = XLALCreateREAL8TimeSeries("V1:STRAIN", &epoch, 0.0, 1.0/srate, &lalStrainUnit, length);
655  XLALSimSGWBFlatSpectrum(seg, detectors, 3, 0, Omega0, flow, H0, rng); // first time to initialize
656  while (1) { // infinite loop
657  size_t j;
658  for (j = 0; j < stride; ++j) // output first stride points
659  printf("%.9f\t%e\t%e\t%e\n", XLALGPSGetREAL8(&seg[0]->epoch) + j / srate, seg[0]->data->data[j], seg[1]->data->data[j], seg[2]->data->data[j]);
660  XLALSimSGWBFlatSpectrum(seg, detectors, 3, stride, Omega0, flow, H0, rng); // make more data
661  }
662 }
663 
664 /*
665  * Test routine that generates 1024 seconds of data in 8 second segments with
666  * 50% overlap. Also produced are the PSD for the spectra as well as the
667  * normalized cross-spectral densities (to check the overlap reduction
668  * function).
669  */
670 int test_sgwb(void)
671 {
672  const double recdur = 1024.0; // duration of the entire data record in seconds
673  const double segdur = 8.0; // duration of a segment in seconds
674  const double srate = 4096.0; // sample rate in Hertz
675  const double flow = 1.0; // low frequency cutoff in Hertz
676  const double Omega0 = 1e-6;
677  const double H0 = 0.72 * LAL_H0FAC_SI;
678  const LIGOTimeGPS epoch = {0, 0};
679  enum { numDetectors = 4 };
688  REAL8FrequencySeries *OmegaGW;
689  COMPLEX16FrequencySeries *htilde1;
690  COMPLEX16FrequencySeries *htilde2;
691  REAL8Window *window;
692  REAL8FFTPlan *plan;
693  gsl_rng *rng;
694  double deltaT = 1.0 / srate;
695  double deltaF = 1.0 / segdur;
696  size_t reclen = recdur * srate;
697  size_t seglen = segdur * srate;
698  size_t stride = seglen / 2;
699  size_t numseg = 1 + (reclen - seglen)/stride;
700  size_t klow = flow / deltaF;
701  size_t i, j, k, l;
702  FILE *fp;
703 
704  if (reclen != (numseg - 1)*stride + seglen) {
705  fprintf(stderr, "warning: numseg, seglen, reclen, and stride not commensurate\n");
706  reclen = (numseg - 1)*stride + seglen;
707  }
708 
709  rng = gsl_rng_alloc(gsl_rng_default);
710  for (i = 0; i < numDetectors; ++i) {
711  const char *prefix = detectors[i].frDetector.prefix;
712  seg[i] = XLALCreateREAL8TimeSeries("STRAIN", &epoch, 0.0, deltaT, &lalStrainUnit, seglen);
713  rec[i] = XLALCreateREAL8TimeSeries("STRAIN", &epoch, 0.0, deltaT, &lalStrainUnit, reclen);
714  psd[i] = XLALCreateREAL8FrequencySeries("PSD", &epoch, 0.0, deltaF, &lalSecondUnit, seglen/2 + 1);
715  snprintf(seg[i]->name, sizeof(seg[i]->name), "%2s:%s", prefix, seg[i]->name);
716  }
717 
718  OmegaGW = XLALSimSGWBOmegaGWFlatSpectrum(Omega0, flow, deltaF, seglen/2 + 1);
719 
720  for (l = 0; l < numseg; ++l) {
721  /* first time, initialize; subsequent times, stride */
722  XLALSimSGWB(seg, detectors, numDetectors, l ? stride : 0, OmegaGW, H0, rng);
723  /* copy stride amount of data or seglen on last time */
724  for (i = 0; i < numDetectors; ++i)
725  memcpy(rec[i]->data->data + l*stride, seg[i]->data->data, (l == numseg - 1 ? seglen : stride) * sizeof(*rec[i]->data->data));
726  }
727 
728  fp = fopen("sgwb.dat", "w");
729  for (j = 0; j < reclen; ++j) {
730  fprintf(fp, "%.9f", j * deltaT);
731  for (i = 0; i < numDetectors; ++i)
732  fprintf(fp, "\t%e", rec[i]->data->data[j]);
733  fprintf(fp, "\n");
734  }
735  fclose(fp);
736 
737 
738  plan = XLALCreateForwardREAL8FFTPlan(seglen, 0);
739  window = XLALCreateHannREAL8Window(seglen);
740  for (i = 0; i < numDetectors; ++i)
741  XLALREAL8AverageSpectrumWelch(psd[i], rec[i], seglen, stride, window, plan);
742 
743  /* compute PSDs for each detector */
744  fp = fopen("sgwb-psd.dat", "w");
745  for (k = klow; k < seglen/2; ++k) {
746  double f = k * deltaF;
747  double actual = 5.6e-22 * (H0 / LAL_H0FAC_SI) * sqrt(Omega0) * pow(100.0/f, 1.5);
748  fprintf(fp, "%.9f\t%e", f, actual);
749  for (i = 0; i < numDetectors; ++i)
750  fprintf(fp, "\t%e", sqrt(psd[i]->data->data[k]));
751  fprintf(fp, "\n");
752  }
753  fclose(fp);
754 
755  /* compute cross spectral densities */
756  htilde1 = XLALCreateCOMPLEX16FrequencySeries("htilde1", &epoch, 0.0, deltaF, &lalSecondUnit, seglen/2 + 1);
757  htilde2 = XLALCreateCOMPLEX16FrequencySeries("htilde2", &epoch, 0.0, deltaF, &lalSecondUnit, seglen/2 + 1);
758  memset(psd[0]->data->data, 0, psd[0]->data->length * sizeof(*psd[0]->data->data));
759  for (l = 0; l < numseg; ++l) {
760  double fac = seglen / (window->sumofsquares * numseg);
761  /* window data */
762  for (j = 0; j < seglen; ++j) {
763  seg[0]->data->data[j] = window->data->data[j] * rec[0]->data->data[j + l*stride];
764  seg[1]->data->data[j] = window->data->data[j] * rec[1]->data->data[j + l*stride];
765  }
766 
767  XLALREAL8TimeFreqFFT(htilde1, seg[0], plan);
768  XLALREAL8TimeFreqFFT(htilde2, seg[1], plan);
769  for (k = klow; k < seglen/2; ++k) {
770  double re = creal(htilde1->data->data[k]);
771  double im = cimag(htilde1->data->data[k]);
772  psd[0]->data->data[k] += fac * (re * re + im * im);
773  }
774  }
775  fp = fopen("sgwb-orf.dat", "w");
776  for (k = klow; k < seglen/2; ++k) {
777  double f = k * deltaF;
778  double csdfac = 0.15 * pow(H0 / LAL_PI, 2.0) * pow(f, -3.0) * Omega0 * segdur;
779  fprintf(fp, "%e\t%e\t%e\n", f, XLALSimSGWBOverlapReductionFunction(f, &detectors[0], &detectors[1]), psd[0]->data->data[k] / csdfac);
780  }
781  fclose(fp);
782 
785 
786  XLALDestroyREAL8Window(window);
789  for (i = 0; i < numDetectors; ++i) {
793  }
794  gsl_rng_free(rng);
795 
796  return 0;
797 }
798 
799 int main(void)
800 {
802  gsl_rng_env_setup();
803  mksgwbdata();
804  test_sgwb();
805  return 0;
806 }
807 
808 #endif
#define LALCalloc(m, n)
static double sigma(const double a, const double b, const sysq *system)
Internal function that computes the spin-spin couplings.
const char * name
size_t XLALSimReadDataFile2Col(double **xdat, double **ydat, LALFILE *fp)
Read a two-column data file.
LALFILE * XLALSimReadDataFileOpen(const char *fname)
Opens a specified data file, searching default path if necessary.
static int XLALSimSGWBSegment(REAL8TimeSeries **h, const LALDetector *detectors, size_t numDetectors, const REAL8FrequencySeries *OmegaGW, double H0, gsl_rng *rng)
Definition: LALSimSGWB.c:43
#define CLEANUP_AND_RETURN(errnum)
#define fprintf
int main(int argc, char *argv[])
Definition: bh_qnmode.c:226
int l
Definition: bh_qnmode.c:135
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
double segdur
double srate
double duration
double flow
const double H
sigmaKerr data[0]
const LALDetector lalCachedDetectors[LAL_NUM_DETECTORS]
int XLALFileClose(LALFILE *file)
REAL8FrequencySeries * XLALCreateREAL8FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALDestroyREAL8FrequencySeries(REAL8FrequencySeries *series)
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
#define LAL_H0FAC_SI
#define LAL_REAL8_EPS
#define LAL_PI
#define LAL_REAL4_EPS
LAL_TAMA_300_DETECTOR
LAL_LLO_4K_DETECTOR
LAL_VIRGO_DETECTOR
LAL_LHO_4K_DETECTOR
int XLALSimSGWBPowerLawSpectrum(REAL8TimeSeries **h, const LALDetector *detectors, size_t numDetectors, size_t stride, double Omegaref, double alpha, double fref, double flow, double H0, gsl_rng *rng)
Routine that may be used to generate sequential segments of stochastic background gravitational wave ...
Definition: LALSimSGWB.c:529
int XLALSimSGWBFlatSpectrum(REAL8TimeSeries **h, const LALDetector *detectors, size_t numDetectors, size_t stride, double Omega0, double flow, double H0, gsl_rng *rng)
Routine that may be used to generate sequential segments of stochastic background gravitational wave ...
Definition: LALSimSGWB.c:429
REAL8FrequencySeries * XLALSimSGWBOmegaGWPowerLawSpectrum(double Omegaref, double alpha, double fref, double flow, double deltaF, size_t length)
Creates a frequency series that contains a power law SGWB spectrum with the specified power Omegaref ...
Definition: LALSimSGWB.c:178
int XLALSimSGWB(REAL8TimeSeries **h, const LALDetector *detectors, size_t numDetectors, size_t stride, const REAL8FrequencySeries *OmegaGW, double H0, gsl_rng *rng)
Routine that may be used to generate sequential segments of stochastic background gravitational wave ...
Definition: LALSimSGWB.c:276
REAL8FrequencySeries * XLALSimSGWBOmegaGWFlatSpectrum(double Omega0, double flow, double deltaF, size_t length)
Creates a frequency series that contains a flat SGWB spectrum with the specified power Omega0 above s...
Definition: LALSimSGWB.c:147
REAL8FrequencySeries * XLALSimSGWBOmegaGWNumericalSpectrumFromFile(const char *fname, size_t length)
Definition: LALSimSGWB.c:558
double XLALSimSGWBOverlapReductionFunction(double f, const LALDetector *detector1, const LALDetector *detector2)
Computes the overlap reduction function between two detectors at a specified frequency.
Definition: LALSimSGWBORF.c:40
void XLALDestroyREAL8FFTPlan(REAL8FFTPlan *plan)
REAL8FFTPlan * XLALCreateReverseREAL8FFTPlan(UINT4 size, int measurelvl)
REAL8FFTPlan * XLALCreateForwardREAL8FFTPlan(UINT4 size, int measurelvl)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
int XLALREAL8AverageSpectrumWelch(REAL8FrequencySeries *spectrum, const REAL8TimeSeries *tseries, UINT4 seglen, UINT4 stride, const REAL8Window *window, const REAL8FFTPlan *plan)
int XLALREAL8TimeFreqFFT(COMPLEX16FrequencySeries *freq, const REAL8TimeSeries *tser, const REAL8FFTPlan *plan)
int XLALREAL8FreqTimeFFT(REAL8TimeSeries *tser, const COMPLEX16FrequencySeries *freq, const REAL8FFTPlan *plan)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
const LALUnit lalStrainUnit
const LALUnit lalSecondUnit
const LALUnit lalDimensionlessUnit
LALUnit * XLALUnitMultiply(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
void XLALDestroyREAL8Window(REAL8Window *window)
REAL8Window * XLALCreateHannREAL8Window(UINT4 length)
#define XLAL_ERROR_NULL(...)
void XLALAbortErrorHandler(const char *func, const char *file, int line, int errnum)
#define XLAL_ERROR(...)
XLALErrorHandlerType * XLALSetErrorHandler(XLALErrorHandlerType *newHandler)
XLAL_ENOMEM
XLAL_EFUNC
XLAL_EINVAL
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
int XLALGPSCmp(const LIGOTimeGPS *t0, const LIGOTimeGPS *t1)
REAL8 XLALGPSGetREAL8(const LIGOTimeGPS *epoch)
list x
list y
int N
LALDetector detectors[LAL_NUM_DETECTORS]
Definition: sgwb.c:112
double alpha
Definition: sgwb.c:106
double fref
Definition: sgwb.c:107
double Omega0
Definition: sgwb.c:105
size_t numDetectors
Definition: sgwb.c:113
COMPLEX16Sequence * data
COMPLEX16 * data
LALFrDetector frDetector
CHAR prefix[3]
REAL8Sequence * data
REAL8Sequence * data
LIGOTimeGPS epoch
REAL8 * data
REAL8Sequence * data
REAL8 sumofsquares
FILE * fp
LIGOTimeGPS epoch
Definition: unicorn.c:20
double V
Definition: unicorn.c:25
double deltaT
Definition: unicorn.c:24