Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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 */
43static 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 */
580 if (!fp)
582
583 N = XLALSimReadDataFile2Col(&f, &Omega, 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. */
634int 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 */
670int 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;
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
789 for (i = 0; i < numDetectors; ++i) {
793 }
794 gsl_rng_free(rng);
795
796 return 0;
797}
798
799int 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
LALFILE * XLALSimReadDataFileOpen(const char *fname)
Opens a specified data file, searching default path if necessary.
size_t XLALSimReadDataFile2Col(double **xdat, double **ydat, LALFILE *fp)
Read a two-column data file.
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)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
REAL8FrequencySeries * XLALCreateREAL8FrequencySeries(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 * 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 * 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
REAL8FrequencySeries * XLALSimSGWBOmegaGWNumericalSpectrumFromFile(const char *fname, size_t length)
Definition: LALSimSGWB.c:558
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
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)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
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)
XLALErrorHandlerType * XLALSetErrorHandler(XLALErrorHandlerType *newHandler)
#define XLAL_ERROR(...)
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 y
int N
x
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