Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALPulsar 7.1.1.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
PSDutils.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2013, 2020 David Keitel
3 * Copyright (C) 2010, 2013, 2014, 2016, 2017, 2022 Karl Wette
4 * Copyright (C) 2010 Chris Messenger
5 * Copyright (C) 2007, 2013 John T. Whelan
6 * Copyright (C) 2006 Badri Krishnan
7 * Copyright (C) 2004--2006, 2008--2013, 2016 Reinhard Prix
8 * Copyright (C) 2004, 2005 Bernd Machenschalk
9 * Copyright (C) 2004, 2005 Alicia Sintes
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with with program; see the file COPYING. If not, write to the
23 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 * MA 02110-1301 USA
25 */
26
27/*---------- includes ----------*/
28
29#include <gsl/gsl_math.h>
30#include <gsl/gsl_sort_double.h>
31
32#include <lal/Units.h>
33#include <lal/FrequencySeries.h>
34#include <lal/NormalizeSFTRngMed.h>
35
36#include <lal/PSDutils.h>
37#include "SFTinternal.h"
38
39/*---------- global variables ----------*/
40
41// for backwards compatibility, we currently allow calling the same internal
42// enum entries by either human-friendly names or by the old-style numerical
43// arguments. The latter may get deprecated at some point in the future.
45 { MATH_OP_ARITHMETIC_SUM, "arithsum" },
46 { MATH_OP_ARITHMETIC_MEAN, "arithmean" },
47 { MATH_OP_ARITHMETIC_MEDIAN, "arithmedian" },
48 { MATH_OP_HARMONIC_SUM, "harmsum" },
49 { MATH_OP_HARMONIC_MEAN, "harmmean" },
50 { MATH_OP_POWERMINUS2_SUM, "powerminus2sum" },
51 { MATH_OP_POWERMINUS2_MEAN, "powerminus2mean" },
52 { MATH_OP_MINIMUM, "min" },
53 { MATH_OP_MAXIMUM, "max" },
57 { MATH_OP_HARMONIC_SUM, "3" },
58 { MATH_OP_HARMONIC_MEAN, "4" },
61 { MATH_OP_MINIMUM, "7" },
62 { MATH_OP_MAXIMUM, "8" },
63};
64
65/*========== function definitions ==========*/
66
67/// \addtogroup PSDutils_h
68/// @{
69
70/**
71 * Destroy a PSD-vector
72 */
73void
74XLALDestroyPSDVector( PSDVector *vect ) /**< the PSD-vector to free */
75{
76 if ( vect == NULL ) { /* nothing to be done */
77 return;
78 }
79
80 for ( UINT4 i = 0; i < vect->length; i++ ) {
81 REAL8FrequencySeries *psd = &( vect->data[i] );
82 if ( psd->data ) {
83 if ( psd->data->data ) {
84 XLALFree( psd->data->data );
85 }
86 XLALFree( psd->data );
87 }
88 } // for i < numPSDs
89
90 XLALFree( vect->data );
91 XLALFree( vect );
92
93 return;
94
95} /* XLALDestroyPSDVector() */
96
97
98/**
99 * Destroy a multi PSD-vector
100 */
101void
102XLALDestroyMultiPSDVector( MultiPSDVector *multvect ) /**< the SFT-vector to free */
103{
104 if ( multvect == NULL ) {
105 return;
106 }
107
108 for ( UINT4 i = 0; i < multvect->length; i++ ) {
109 XLALDestroyPSDVector( multvect->data[i] );
110 }
111
112 XLALFree( multvect->data );
113 XLALFree( multvect );
114
115 return;
116
117} /* XLALDestroyMultiPSDVector() */
118
119///
120/// Create a \c MultiNoiseWeights of the given length.
121///
122/// NOTE: this does not allocate the individual data entries.
123///
125XLALCreateMultiNoiseWeights( const UINT4 length ) ///< [in] Length of the \c MultiNoiseWeights.
126{
127
128 MultiNoiseWeights *multiWeights = NULL;
129 XLAL_CHECK_NULL( ( multiWeights = XLALCalloc( 1, sizeof( *multiWeights ) ) ) != NULL, XLAL_ENOMEM );
130 multiWeights->length = length;
131 if ( length > 0 ) {
132 XLAL_CHECK_NULL( ( multiWeights->data = XLALCalloc( length, sizeof( *multiWeights->data ) ) ) != NULL, XLAL_ENOMEM );
133 }
134
135 return multiWeights;
136
137} // XLALCreateMultiNoiseWeights()
138
139///
140/// Create a full copy of a \c MultiNoiseWeights object.
141///
143XLALCopyMultiNoiseWeights( const MultiNoiseWeights *multiWeights ) ///< [in] Length of the \c MultiNoiseWeights.
144{
145
146 // check input sanity
147 XLAL_CHECK_NULL( multiWeights != NULL, XLAL_EINVAL );
148 XLAL_CHECK_NULL( multiWeights->data != NULL, XLAL_EINVAL );
149 XLAL_CHECK_NULL( multiWeights->length > 0, XLAL_EINVAL );
150
151 MultiNoiseWeights *copiedWeights = NULL;
152 XLAL_CHECK_NULL( ( copiedWeights = XLALCreateMultiNoiseWeights( multiWeights->length ) ) != NULL, XLAL_EFUNC );
153 copiedWeights->length = multiWeights->length;
154 copiedWeights->Sinv_Tsft = multiWeights->Sinv_Tsft;
155 copiedWeights->isNotNormalized = multiWeights->isNotNormalized;
156
157 for ( UINT4 X = 0; X < multiWeights->length; X++ ) {
158 /* create k^th weights vector */
159 XLAL_CHECK_NULL( ( copiedWeights->data[X] = XLALCreateREAL8Vector( multiWeights->data[X]->length ) ) != NULL, XLAL_EFUNC );
160 for ( UINT4 alpha = 0; alpha < multiWeights->data[X]->length; alpha++ ) {
161 copiedWeights->data[X]->data[alpha] = multiWeights->data[X]->data[alpha];
162 }
163 }
164
165 return copiedWeights;
166
167} // XLALCopyMultiNoiseWeights()
168
169
170/** Destroy a MultiNoiseWeights object */
171void
173{
174 if ( weights == NULL ) {
175 return;
176 }
177
178 for ( UINT4 k = 0; k < weights->length; k++ ) {
180 }
181
182 XLALFree( weights->data );
183 XLALFree( weights );
184
185 return;
186
187} /* XLALDestroyMultiNoiseWeights() */
188
189
190/**
191 * Function that *truncates the PSD in place* to the requested frequency-bin interval [firstBin, lastBin] for the given multiPSDVector.
192 * Now also truncates the original SFT vector, as necessary for correct computation of normalized SFT power.
193 */
194int
196 MultiSFTVector *multiSFTVect,
197 UINT4 firstBin,
198 UINT4 lastBin
199 )
200{
201 /* check user input */
202 if ( !multiPSDVect ) {
203 XLALPrintError( "%s: invalid NULL input 'multiPSDVect'\n", __func__ );
205 }
206 if ( !multiSFTVect ) {
207 XLALPrintError( "%s: invalid NULL input 'multiSFTVect'\n", __func__ );
209 }
210 if ( lastBin < firstBin ) {
211 XLALPrintError( "%s: empty bin interval requested [%d, %d]\n", __func__, firstBin, lastBin );
213 }
214
215 UINT4 numIFOs = multiPSDVect->length;
216 UINT4 numBins = multiPSDVect->data[0]->data[0].data->length;
217
218 if ( numIFOs != multiSFTVect->length ) {
219 XLALPrintError( "%s: inconsistent number of IFOs between PSD (%d) and SFT (%d) vectors.\n", __func__, numIFOs, multiSFTVect->length );
221 }
222
223 if ( numBins != multiSFTVect->data[0]->data[0].data->length ) {
224 XLALPrintError( "%s: inconsistent number of bins between PSD (%d bins) and SFT (%d bins) vectors.\n", __func__, numBins, multiSFTVect->data[0]->data[0].data->length );
226 }
227
228 if ( ( firstBin >= numBins ) || ( lastBin >= numBins ) ) {
229 XLALPrintError( "%s: requested bin-interval [%d, %d] outside of PSD bins [0, %d]\n", __func__, firstBin, lastBin, numBins - 1 );
231 }
232
233 /* ----- check if there's anything to do at all? ----- */
234 if ( ( firstBin == 0 ) && ( lastBin == numBins - 1 ) ) {
235 return XLAL_SUCCESS;
236 }
237
238 /* ----- loop over detectors, timestamps, then crop each PSD ----- */
239 UINT4 X;
240 for ( X = 0; X < numIFOs; X ++ ) {
241 PSDVector *thisPSDVect = multiPSDVect->data[X];
242 SFTVector *thisSFTVect = multiSFTVect->data[X];
243 UINT4 numTS = thisPSDVect->length;
244
245 UINT4 iTS;
246 for ( iTS = 0; iTS < numTS; iTS ++ ) {
247 REAL8FrequencySeries *thisPSD = &thisPSDVect->data[iTS];
248 COMPLEX8FrequencySeries *thisSFT = &thisSFTVect->data[iTS];
249
250 if ( numBins != thisPSD->data->length ) {
251 XLALPrintError( "%s: inconsistent number of frequency-bins across multiPSDVector: X=%d, iTS=%d: numBins = %d != %d\n",
252 __func__, X, iTS, numBins, thisPSD->data->length );
254 }
255
256 if ( numBins != thisSFT->data->length ) {
257 XLALPrintError( "%s: inconsistent number of frequency-bins across multiSFTVector: X=%d, iTS=%d: numBins = %d != %d\n",
258 __func__, X, iTS, numBins, thisSFT->data->length );
260 }
261
262 UINT4 numNewBins = lastBin - firstBin + 1;
263
264 /* crop PSD */
265 XLAL_CHECK( XLALResizeREAL8FrequencySeries( thisPSD, firstBin, numNewBins ) != NULL, XLAL_EFUNC );
266
267 /* crop SFT */
268 XLAL_CHECK( XLALResizeCOMPLEX8FrequencySeries( thisSFT, firstBin, numNewBins ) != NULL, XLAL_EFUNC );
269
270 } /* for iTS < numTS */
271
272 } /* for X < numIFOs */
273
274 /* that should be all ... */
275 return XLAL_SUCCESS;
276
277} /* XLALCropMultiPSDandSFTVectors() */
278
279
280/**
281 * Computes weight factors arising from MultiSFTs with different noise
282 * floors
283 */
286 UINT4 blocksRngMed,
287 UINT4 excludePercentile )
288{
289 XLAL_CHECK_NULL( rngmed != NULL, XLAL_EINVAL );
290 XLAL_CHECK_NULL( rngmed->data != NULL, XLAL_EINVAL );
291 XLAL_CHECK_NULL( rngmed->length != 0, XLAL_EINVAL );
292
293 UINT4 numIFOs = rngmed->length;
294 REAL8 Tsft = TSFTfromDFreq( rngmed->data[0]->data[0].deltaF );
295
296 /* create multi noise weights for output */
297 MultiNoiseWeights *multiWeights = NULL;
298 XLAL_CHECK_NULL( ( multiWeights = XLALCreateMultiNoiseWeights( numIFOs ) ) != NULL, XLAL_EFUNC );
299
300 UINT4 numSFTsTot = 0;
301 REAL8 sumWeights = 0;
302
303 for ( UINT4 X = 0; X < numIFOs; X++ ) {
304 UINT4 numSFTs = rngmed->data[X]->length;
305 numSFTsTot += numSFTs;
306
307 /* create k^th weights vector */
308 if ( ( multiWeights->data[X] = XLALCreateREAL8Vector( numSFTs ) ) == NULL ) {
309 /* free weights vectors created previously in loop */
310 XLALDestroyMultiNoiseWeights( multiWeights );
311 XLAL_ERROR_NULL( XLAL_EFUNC, "Failed to allocate noiseweights for IFO X = %d\n", X );
312 } /* if XLALCreateREAL8Vector() failed */
313
314 /* loop over rngmeds and calculate weights -- one for each sft */
315 for ( UINT4 alpha = 0; alpha < numSFTs; alpha++ ) {
316 UINT4 halfBlock = blocksRngMed / 2;
317
318 REAL8FrequencySeries *thisrm = &( rngmed->data[X]->data[alpha] );
319
320 UINT4 lengthsft = thisrm->data->length;
321
322 XLAL_CHECK_NULL( lengthsft >= blocksRngMed, XLAL_EINVAL );
323
324 UINT4 length = lengthsft - blocksRngMed + 1;
325 UINT4 halfLength = length / 2;
326
327 /* calculate index in power medians vector from which to calculate mean */
328 UINT4 excludeIndex = excludePercentile * halfLength ; /* integer arithmetic */
329 excludeIndex /= 100; /* integer arithmetic */
330
331 REAL8 Tsft_avgS2 = 0.0; // 'S2' refers to double-sided PSD
332 for ( UINT4 k = halfBlock + excludeIndex; k < lengthsft - halfBlock - excludeIndex; k++ ) {
333 Tsft_avgS2 += thisrm->data->data[k];
334 }
335 Tsft_avgS2 /= lengthsft - 2 * halfBlock - 2 * excludeIndex;
336
337 REAL8 wXa = 1.0 / Tsft_avgS2; // unnormalized weight
338 multiWeights->data[X]->data[alpha] = wXa;
339
340 sumWeights += wXa; // sum the weights to normalize this at the end
341 } /* end loop over sfts for each ifo */
342
343 } /* end loop over ifos */
344
345 /* overall noise-normalization factor Sinv = 1/Nsft sum_Xa Sinv_Xa,
346 * see Eq.(60) in CFSv2 notes:
347 * https://dcc.ligo.org/cgi-bin/private/DocDB/ShowDocument?docid=1665&version=3
348 */
349 REAL8 TsftS2_inv = sumWeights / numSFTsTot; // this is double-sided PSD 'S2'
350
351 /* make weights of order unity by normalizing with TsftS2_inv, see Eq.(58) in CFSv2 notes (v3) */
352 for ( UINT4 X = 0; X < numIFOs; X ++ ) {
353 UINT4 numSFTs = multiWeights->data[X]->length;
354 for ( UINT4 alpha = 0; alpha < numSFTs; alpha ++ ) {
355 multiWeights->data[X]->data[alpha] /= TsftS2_inv;
356 }
357 }
358
359 multiWeights->Sinv_Tsft = 0.5 * Tsft * Tsft * TsftS2_inv; /* 'Sinv * Tsft' refers to single-sided PSD!! Eq.(60) in CFSv2 notes (v3)*/
360
361 return multiWeights;
362
363} /* XLALComputeMultiNoiseWeights() */
364
365
366/**
367 * Compute the "data-quality factor" \f$ \mathcal{Q}(f) = \sum_X \frac{\epsilon_X}{\mathcal{S}_X(f)} \f$ over the given SFTs.
368 * The input \a multiPSD is a pre-computed PSD map \f$ \mathcal{S}_{X,i}(f) \f$ , over IFOs \f$ X \f$ , SFTs \f$ i \f$
369 * and frequency \f$ f \f$ .
370 *
371 * \return the output is a vector \f$ \mathcal{Q}(f) \f$ .
372 *
373 */
375XLALComputeSegmentDataQ( const MultiPSDVector *multiPSDVect, /**< input PSD map over IFOs, SFTs, and frequencies */
376 LALSeg segment /**< segment to compute Q for */
377 )
378{
379 /* check input consistency */
380 if ( multiPSDVect == NULL ) {
381 XLALPrintError( "%s: NULL input 'multiPSDVect'\n", __func__ );
383 }
384 if ( multiPSDVect->length == 0 || multiPSDVect->data == 0 ) {
385 XLALPrintError( "%s: invalid multiPSDVect input (length=0 or data=NULL)\n", __func__ );
387 }
388
389 REAL8 Tseg = XLALGPSDiff( &segment.end, &segment.start );
390 if ( Tseg <= 0 ) {
391 XLALPrintError( "%s: negative segment-duration '%g'\n", __func__, Tseg );
393 }
394
395 REAL8 Tsft = 1.0 / multiPSDVect->data[0]->data[0].deltaF;
396 REAL8 f0 = multiPSDVect->data[0]->data[0].f0;
397 REAL8 dFreq = multiPSDVect->data[0]->data[0].deltaF;
398 UINT4 numFreqs = multiPSDVect->data[0]->data[0].data->length;
399
400 REAL8FrequencySeries *Q, *SXinv;
401 if ( ( Q = XLALCreateREAL8FrequencySeries( "Qfactor", &segment.start, f0, dFreq, &lalHertzUnit, numFreqs ) ) == NULL ) {
402 XLALPrintError( "%s: Q = XLALCreateREAL8FrequencySeries(numFreqs=%d) failed with xlalErrno = %d\n", __func__, numFreqs, xlalErrno );
404 }
405 if ( ( SXinv = XLALCreateREAL8FrequencySeries( "SXinv", &segment.start, f0, dFreq, &lalHertzUnit, numFreqs ) ) == NULL ) {
406 XLALPrintError( "%s: SXinv = XLALCreateREAL8FrequencySeries(numFreqs=%d) failed with xlalErrno = %d\n", __func__, numFreqs, xlalErrno );
408 }
409 /* initialize Q-array to zero, as we'll keep adding to it */
410 memset( Q->data->data, 0, Q->data->length * sizeof( Q->data->data[0] ) );
411
412 /* ----- loop over IFOs ----- */
413 UINT4 numIFOs = multiPSDVect->length;
414 UINT4 X;
415 for ( X = 0; X < numIFOs; X ++ ) {
416 PSDVector *thisPSDVect = multiPSDVect->data[X];
417
418 /* initialize SXinv-array to zero, as we'll keep adding to it */
419 memset( SXinv->data->data, 0, SXinv->data->length * sizeof( SXinv->data->data[0] ) );
420 UINT4 numSFTsInSeg = 0; /* reset counter of SFTs within this segment */
421
422 /* ----- loop over all timestamps ----- */
423 /* find SFTs inside segment, count them and combine their PSDs */
424 UINT4 numTS = thisPSDVect->length;
425 UINT4 iTS;
426 for ( iTS = 0; iTS < numTS; iTS++ ) {
427 REAL8FrequencySeries *thisPSD = &thisPSDVect->data[iTS];
428
429 /* some internal consistency/paranoia checks */
430 if ( ( f0 != thisPSD->f0 ) || ( dFreq != thisPSD->deltaF ) || ( numFreqs != thisPSD->data->length ) ) {
431 XLALPrintError( "%s: %d-th timestamp %f: inconsistent PSDVector: f0 = %g : %g, dFreq = %g : %g, numFreqs = %d : %d \n",
432 __func__, iTS, XLALGPSGetREAL8( &thisPSD->epoch ), f0, thisPSD->f0, dFreq, thisPSD->deltaF, numFreqs, thisPSD->data->length );
434 }
435
436 int cmp = XLALCWGPSinRange( thisPSD->epoch, &segment.start, &segment.end );
437
438 if ( cmp < 0 ) { /* SFT-end before segment => advance to the next one */
439 continue;
440 }
441 if ( cmp > 0 ) { /* SFT-start past end of segment: ==> terminate loop */
442 break;
443 }
444
445 if ( cmp == 0 ) { /* this SFT is inside segment */
446 numSFTsInSeg ++;
447 /* add SXinv(f) += 1/SX_i(f) over all frequencies */
448 UINT4 iFreq;
449 for ( iFreq = 0; iFreq < numFreqs; iFreq++ ) {
450 SXinv->data->data[iFreq] += 1.0 / thisPSD->data->data[iFreq] ;
451 }
452
453 } /* if SFT inside segment */
454
455 } /* for iTS < numTS */
456
457 /* compute duty-cycle eps_X = nX * Tsft / Tseg for this IFO */
458 REAL8 duty_X = numSFTsInSeg * Tsft / Tseg;
459 /* sanity check: eps in [0, 1]*/
460 if ( ( duty_X < 0 ) || ( duty_X > 1 ) ) {
461 XLALPrintError( "%s: something is WRONG: duty-cyle = %g not within [0,1]!\n", __func__, duty_X );
463 }
464
465 /* add duty_X-weighted SXinv to Q */
466 UINT4 iFreq;
467 for ( iFreq = 0; iFreq < numFreqs; iFreq ++ ) {
468 Q->data->data[iFreq] += duty_X * SXinv->data->data[iFreq] / numSFTsInSeg;
469 }
470
471 } /* for X < numIFOs */
472
473 /* clean up, free memory */
475
476
477 return Q;
478
479} /* XLALComputeSegmentDataQ() */
480
481
482/**
483 * Compute various types of "math operations" over the entries of an array.
484 *
485 * The supported optypes (e.g. sums and averages) are defined in \c MathOpType.
486 *
487 * This can be used e.g. for the different established conventions of combining
488 * SFTs for a PSD estimate.
489 *
490 */
491REAL8 XLALMathOpOverArray( const REAL8 *data, /**< input data array */
492 const size_t length, /**< length of the input data array */
493 const MathOpType optype /**< type of operation */
494 )
495{
496
497 UINT4 i;
498 REAL8 res = 0.0;
499
500 switch ( optype ) {
501
502 case MATH_OP_ARITHMETIC_SUM: /* sum(data) */
503
504 for ( i = 0; i < length; ++i ) {
505 res += *( data++ );
506 }
507
508 break;
509
510 case MATH_OP_ARITHMETIC_MEAN: /* sum(data)/length */
511
512 for ( i = 0; i < length; ++i ) {
513 res += *( data++ );
514 }
515 res /= ( REAL8 )length;
516
517 break;
518
519 case MATH_OP_HARMONIC_SUM: /* 1 / sum(1 / data) */
520
521 for ( i = 0; i < length; ++i ) {
522 res += 1.0 / *( data++ );
523 }
524 res = 1.0 / res;
525
526 break;
527
528 case MATH_OP_HARMONIC_MEAN: /* length / sum(1 / data) */
529
530 for ( i = 0; i < length; ++i ) {
531 res += 1.0 / *( data++ );
532 }
533 res = ( REAL8 )length / res;
534
535 break;
536
537 case MATH_OP_POWERMINUS2_SUM: /* 1 / sqrt ( sum(1 / data/data) )*/
538
539 for ( i = 0; i < length; ++i ) {
540 res += 1.0 / ( data[i] * data[i] );
541 }
542 res = 1.0 / sqrt( res );
543
544 break;
545
546 case MATH_OP_POWERMINUS2_MEAN: /* 1 / sqrt ( sum(1/data/data) / length )*/
547
548 for ( i = 0; i < length; ++i ) {
549 res += 1.0 / ( data[i] * data[i] );
550 }
551 res = 1.0 / sqrt( res / ( REAL8 )length );
552
553 break;
554
555 /* these cases require sorted data;
556 * we use gsl_sort_index() to avoid in-place modification of the input data
557 * by gsl_sort()
558 */
560 case MATH_OP_MINIMUM:
561 case MATH_OP_MAXIMUM:
562 ; /* empty statement because declaration cannot be first line in a switch case */
563 size_t *sortidx;
564 if ( ( sortidx = XLALMalloc( length * sizeof( size_t ) ) ) == NULL ) {
565 XLALPrintError( "XLALMalloc(%ld) failed.\n", length );
567 }
568 gsl_sort_index( sortidx, data, 1, length );
569
570 switch ( optype ) {
571
572 case MATH_OP_ARITHMETIC_MEDIAN: /* middle element of sorted data */
573
574 if ( length / 2 == ( length + 1 ) / 2 ) { /* length is even */
575 res = ( data[sortidx[length / 2 - 1]] + data[sortidx[length / 2]] ) / 2;
576 } else { /* length is odd */
577 res = data[sortidx[length / 2]];
578 }
579
580 break;
581
582 case MATH_OP_MINIMUM: /* first element of sorted data */
583
584 res = data[sortidx[0]];
585 break;
586
587 case MATH_OP_MAXIMUM: /* last element of sorted data */
588
589 res = data[sortidx[length - 1]];
590 break;
591
592 default:
593
594 XLAL_ERROR_REAL8( XLAL_EINVAL, "For optype=%i this block should not have been reached.", optype );
595
596 }
597
598 XLALFree( sortidx );
599 break;
600
601 default:
602
603 XLAL_ERROR_REAL8( XLAL_EINVAL, "'%i' is not an implemented math. operation", optype );
604
605 } /* switch (optype) */
606
607 return res;
608
609} /* XLALMathOpOverArray() */
610
611
612/**
613 * Compute an additional normalization factor from the total number of SFTs to be applied after a loop of mathops over SFTs and IFOs.
614 *
615 * Currently only implemented for:
616 * MATH_OP_HARMONIC_SUM (to emulate MATH_OP_HARMONIC_MEAN over the combined array)
617 * MATH_OP_POWERMINUS2_SUM (to emulate MATH_OP_POWERMINUS2_MEAN over the combined array)
618 *
619 * This function exists only as a simple workaround for when we want to compute
620 * some types of mathops, e.g. the harmonic or power2 mean,
621 * over all SFTs from all detectors together:
622 * then call XLALComputePSDandNormSFTPower with PSDmthopSFTs=PSDmthopIFOs=MATH_OP_[HARMONIC/POWERMINUS2]_SUM
623 * and then this factor is applied at the end,
624 * which gives equivalent results to if we had rewritten the loop order in XLALComputePSDandNormSFTPower
625 * to allow for calling MATH_OP_[HARMONIC/POWERMINUS2]_MEAN over the combined array.
626 */
628 const UINT4 totalNumSFTs, /**< total number of SFTs from all IFOs */
629 const MathOpType optypeSFTs /**< type of operations that was done over SFTs */
630)
631{
632
633 REAL8 factor;
634
635 switch ( optypeSFTs ) {
636
637 case MATH_OP_ARITHMETIC_SUM: /* sum(data) */
638 case MATH_OP_ARITHMETIC_MEAN: /* sum(data)/length */
641 case MATH_OP_MINIMUM:
642 case MATH_OP_MAXIMUM:
644 XLAL_ERROR_REAL8( XLAL_EINVAL, "For math operation '%i' it makes no sense to call this function!", optypeSFTs );
645 break;
646
647 case MATH_OP_HARMONIC_SUM: /* length / sum(1 / data) */
648 factor = totalNumSFTs;
649 break;
650
651 case MATH_OP_POWERMINUS2_SUM: /* 1 / sqrt ( sum(1 / data/data) )*/
652 factor = sqrt( totalNumSFTs );
653 break;
654
655 default:
656
657 XLAL_ERROR_REAL8( XLAL_EINVAL, "'%i' is not an implemented math. operation", optypeSFTs );
658
659 } /* switch (optypeSFTs) */
660
661 return factor;
662
663} /* XLALGetMathOpNormalizationFactorFromTotalNumberOfSFTs() */
664
665
666/**
667 * Compute the PSD (power spectral density) and the "normalized power" \f$ P_\mathrm{SFT} \f$ over a MultiSFTVector.
668 *
669 * \return: a REAL8Vector of the final normalized and averaged/summed PSD;
670 * plus the full multiPSDVector array,
671 * and optionally a REAL8Vector of normalized SFT power (only if not passed as NULL)
672 *
673 * If normalizeSFTsInPlace is TRUE, then the inputSFTs will be modified by XLALNormalizeMultiSFTVect().
674 * If it is FALSE, an internal copy will be used and the inputSFTs will be returned unmodified.
675 */
676int
678 REAL8Vector **finalPSD, /* [out] final PSD averaged over all IFOs and timestamps */
679 MultiPSDVector **multiPSDVector, /* [out] complete MultiPSDVector over IFOs, timestamps and freq-bins */
680 REAL8Vector **normSFT, /* [out] normalised SFT power (optional) */
681 MultiSFTVector *inputSFTs, /* [in] the multi-IFO SFT data */
682 const BOOLEAN returnMultiPSDVector, /* [in] whether to return multiPSDVector */
683 const BOOLEAN returnNormSFT, /* [in] whether to return normSFT vector */
684 const UINT4 blocksRngMed, /* [in] running Median window size */
685 const MathOpType PSDmthopSFTs, /* [in] math operation over SFTs for each IFO (PSD) */
686 const MathOpType PSDmthopIFOs, /* [in] math operation over IFOs (PSD) */
687 const MathOpType nSFTmthopSFTs, /* [in] math operation over SFTs for each IFO (normSFT) */
688 const MathOpType nSFTmthopIFOs, /* [in] math operation over IFOs (normSFT) */
689 const BOOLEAN normalizeByTotalNumSFTs, /* [in] whether to include a final normalization factor derived from the total number of SFTs (over all IFOs); only useful for some mthops */
690 const REAL8 FreqMin, /* [in] starting frequency -> first output bin (if -1: use full SFT data including rngmed bins, else must be >=0) */
691 const REAL8 FreqBand, /* [in] frequency band to cover with output bins (must be >=0) */
692 const BOOLEAN normalizeSFTsInPlace /* [in] if FALSE, a copy of inputSFTs will be used internally and the original will not be modified */
693)
694{
695
696 /* sanity checks */
697 XLAL_CHECK( finalPSD, XLAL_EINVAL );
698 XLAL_CHECK( multiPSDVector, XLAL_EINVAL );
699 XLAL_CHECK( normSFT, XLAL_EINVAL );
700 XLAL_CHECK( inputSFTs && inputSFTs->data && inputSFTs->length > 0, XLAL_EINVAL, "inputSFTs must be pre-allocated." );
701 XLAL_CHECK( ( FreqMin >= 0 && FreqBand >= 0 ) || ( FreqMin == -1 && FreqBand == 0 ), XLAL_EINVAL, "Need either both Freqmin>=0 && FreqBand>=0 (truncate PSD band to this range) or FreqMin=-1 && FreqBand=0 (use full band as loaded from SFTs, including rngmed-sidebands." );
702
703 /* get power running-median rngmed[ |data|^2 ] from SFTs *
704 * as the output of XLALNormalizeMultiSFTVect(),
705 * multiPSD will be a rng-median smoothed periodogram over the normalized SFTs.
706 * The inputSFTs themselves will also be normalized in this call
707 * unless overruled by normalizeSFTsInPlace option.
708 */
709 UINT4Vector *numSFTsVec = NULL;
710 MultiSFTVector *SFTs = NULL;
711 UINT4 numIFOs = inputSFTs->length;
712 if ( normalizeSFTsInPlace ) {
713 SFTs = inputSFTs;
714 } else {
715 numSFTsVec = XLALCreateUINT4Vector( numIFOs );
716 for ( UINT4 X = 0; X < numIFOs; ++X ) {
717 numSFTsVec->data[X] = inputSFTs->data[X]->length;
718 }
719 SFTs = XLALCreateEmptyMultiSFTVector( numSFTsVec );
720 for ( UINT4 X = 0; X < numIFOs; ++X ) {
721 for ( UINT4 k = 0; k < numSFTsVec->data[X]; ++k ) {
722 XLAL_CHECK( XLALCopySFT( &( SFTs->data[X]->data[k] ), &( inputSFTs->data[X]->data[k] ) ) == XLAL_SUCCESS, XLAL_EFUNC );
723 }
724 }
725 }
726 XLAL_CHECK( ( *multiPSDVector = XLALNormalizeMultiSFTVect( SFTs, blocksRngMed, NULL ) ) != NULL, XLAL_EFUNC );
727 UINT4 numBinsSFTs = SFTs->data[0]->data[0].data->length;
728 REAL8 dFreq = ( *multiPSDVector )->data[0]->data[0].deltaF;
729
730 /* restrict to just the "physical" band if requested */
731 /* first, figure out the effective PSD bin-boundaries for user */
732 UINT4 firstBin, lastBin;
733 if ( FreqMin > 0 ) { /* user requested a constrained band */
734 REAL8 fminSFTs = SFTs->data[0]->data[0].f0;
735 REAL8 fmaxSFTs = fminSFTs + numBinsSFTs * dFreq;
736 XLAL_CHECK( ( FreqMin >= fminSFTs ) && ( FreqMin + FreqBand <= fmaxSFTs ), XLAL_EDOM, "Requested frequency range [%f,%f]Hz not covered by available SFT band [%f,%f]Hz", FreqMin, FreqMin + FreqBand, fminSFTs, fmaxSFTs );
737 /* check band wide enough for wings */
738 UINT4 rngmedSideBandBins = blocksRngMed / 2 + 1; /* truncates down plus add one bin extra safety! */
739 REAL8 rngmedSideBand = rngmedSideBandBins * dFreq;
740 /* set the actual output range in bins */
741 UINT4 minBinPSD = XLALRoundFrequencyDownToSFTBin( FreqMin, dFreq );
742 UINT4 maxBinPSD = XLALRoundFrequencyUpToSFTBin( FreqMin + FreqBand, dFreq ) - 1;
743 UINT4 minBinSFTs = XLALRoundFrequencyDownToSFTBin( fminSFTs, dFreq );
744 UINT4 maxBinSFTs = XLALRoundFrequencyUpToSFTBin( fmaxSFTs, dFreq ) - 1;
745 if ( ( minBinPSD < minBinSFTs ) || ( maxBinPSD > maxBinSFTs ) ) {
746 XLAL_ERROR( XLAL_ERANGE, "Requested frequency range [%f,%f)Hz means rngmedSideBand=%fHz (%d bins) would spill over available SFT band [%f,%f)Hz",
747 FreqMin, FreqMin + FreqBand, rngmedSideBand, rngmedSideBandBins, fminSFTs, fmaxSFTs );
748 }
749 UINT4 binsBand = maxBinPSD - minBinPSD + 1;
750 firstBin = minBinPSD - minBinSFTs;
751 lastBin = firstBin + binsBand - 1;
752 } else { /* output all bins loaded from SFTs (includes rngmed-sidebands) */
753 firstBin = 0;
754 lastBin = numBinsSFTs - 1;
755 }
756 XLAL_PRINT_INFO( "loaded SFTs have %d bins, effective PSD output band is [%d, %d]", numBinsSFTs, firstBin, lastBin );
757 /* now truncate */
758 XLAL_CHECK( XLALCropMultiPSDandSFTVectors( *multiPSDVector, SFTs, firstBin, lastBin ) == XLAL_SUCCESS, XLAL_EFUNC, "Failed call to XLALCropMultiPSDandSFTVectors (multiPSDVector, SFTs, firstBin=%d, lastBin=%d)", firstBin, lastBin );
759
760 /* number of raw bins in final PSD */
761 UINT4 numBins = ( *multiPSDVector )->data[0]->data[0].data->length;
762
763 /* allocate main output struct, using cropped length */
764 XLAL_CHECK( ( *finalPSD = XLALCreateREAL8Vector( numBins ) ) != NULL, XLAL_ENOMEM, "Failed to create REAL8Vector for finalPSD." );
765
766 /* number of IFOs */
767 REAL8Vector *overIFOs = NULL; /* one frequency bin over IFOs */
768 XLAL_CHECK( ( overIFOs = XLALCreateREAL8Vector( numIFOs ) ) != NULL, XLAL_ENOMEM, "Failed to create REAL8Vector for overIFOs array." );
769
770 /* maximum number of SFTs */
771 UINT4 maxNumSFTs = 0;
772 for ( UINT4 X = 0; X < numIFOs; ++X ) {
773 maxNumSFTs = GSL_MAX( maxNumSFTs, ( *multiPSDVector )->data[X]->length );
774 }
775
776 REAL8Vector *overSFTs = NULL; /* one frequency bin over SFTs */
777 XLAL_CHECK( ( overSFTs = XLALCreateREAL8Vector( maxNumSFTs ) ) != NULL, XLAL_ENOMEM, "Failed to create REAL8Vector for overSFTs array." );
778
779 /* normalize rngmd(power) to get proper *single-sided* PSD: Sn = (2/Tsft) rngmed[|data|^2]] */
780 REAL8 normPSD = 2.0 * dFreq;
781
782 /* optionally include a normalizing factor for when we want to compute e.g. the harmonic or power2 mean over all SFTs from all detectors together:
783 * call with PSDmthopSFTs=PSDmthopIFOs=MATH_OP_[HARMONIC/POWERMINUS2]_SUM
784 * and then this factor is applied at the end,
785 * which gives equivalent results to if we had rewritten the loop order
786 * to allow for calling MATH_OP_[HARMONIC/POWERMINUS2]_MEAN over the combined array.
787 */
788 REAL8 totalNumSFTsNormalizingFactor = 1.;
789 if ( normalizeByTotalNumSFTs ) {
790 UINT4 totalNumSFTs = 0;
791 for ( UINT4 X = 0; X < numIFOs; ++X ) {
792 totalNumSFTs += ( *multiPSDVector )->data[X]->length;
793 }
794 totalNumSFTsNormalizingFactor = XLALGetMathOpNormalizationFactorFromTotalNumberOfSFTs( totalNumSFTs, PSDmthopSFTs );
795 }
796
797 XLAL_PRINT_INFO( "Computing spectrogram and PSD ..." );
798 /* loop over frequency bins in final PSD */
799 for ( UINT4 k = 0; k < numBins; ++k ) {
800
801 /* loop over IFOs */
802 for ( UINT4 X = 0; X < numIFOs; ++X ) {
803
804 /* number of SFTs for this IFO */
805 UINT4 numSFTs = ( *multiPSDVector )->data[X]->length;
806
807 /* copy PSD frequency bins and normalise multiPSDVector for later use */
808 for ( UINT4 alpha = 0; alpha < numSFTs; ++alpha ) {
809 ( *multiPSDVector )->data[X]->data[alpha].data->data[k] *= normPSD;
810 overSFTs->data[alpha] = ( *multiPSDVector )->data[X]->data[alpha].data->data[k];
811 }
812
813 /* compute math. operation over SFTs for this IFO */
814 overIFOs->data[X] = XLALMathOpOverArray( overSFTs->data, numSFTs, PSDmthopSFTs );
815 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( overIFOs->data[X] ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for overIFOs->data[X=%d]", X );
816
817 } /* for IFOs X */
818
819 /* compute math. operation over IFOs for this frequency */
820 ( *finalPSD )->data[k] = XLALMathOpOverArray( overIFOs->data, numIFOs, PSDmthopIFOs );
821 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( ( *finalPSD )->data[k] ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for finalPSD->data[k=%d]", k );
822
823 if ( normalizeByTotalNumSFTs ) {
824 ( *finalPSD )->data[k] *= totalNumSFTsNormalizingFactor;
825 }
826
827 } /* for freq bins k */
828 XLAL_PRINT_INFO( "done." );
829
830 /* compute normalised SFT power */
831 if ( returnNormSFT ) {
832 XLAL_PRINT_INFO( "Computing normalised SFT power ..." );
833 XLAL_CHECK( ( *normSFT = XLALCreateREAL8Vector( numBins ) ) != NULL, XLAL_ENOMEM, "Failed to create REAL8Vector for normSFT." );
834
835 /* loop over frequency bins in SFTs */
836 for ( UINT4 k = 0; k < numBins; ++k ) {
837
838 /* loop over IFOs */
839 for ( UINT4 X = 0; X < numIFOs; ++X ) {
840
841 /* number of SFTs for this IFO */
842 UINT4 numSFTs = SFTs->data[X]->length;
843
844 /* compute SFT power */
845 for ( UINT4 alpha = 0; alpha < numSFTs; ++alpha ) {
846 COMPLEX8 bin = SFTs->data[X]->data[alpha].data->data[k];
847 overSFTs->data[alpha] = crealf( bin ) * crealf( bin ) + cimagf( bin ) * cimagf( bin );
848 }
849
850 /* compute math. operation over SFTs for this IFO */
851 overIFOs->data[X] = XLALMathOpOverArray( overSFTs->data, numSFTs, nSFTmthopSFTs );
852 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( overIFOs->data[X] ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for overIFOs->data[X=%d]", X );
853
854 } /* over IFOs */
855
856 /* compute math. operation over IFOs for this frequency */
857 ( *normSFT )->data[k] = XLALMathOpOverArray( overIFOs->data, numIFOs, nSFTmthopIFOs );
858 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( ( *normSFT )->data[k] ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for normSFT->data[k=%d]", k );
859
860 } /* over freq bins */
861 XLAL_PRINT_INFO( "done." );
862 } /* if returnNormSFT */
863
864 XLALDestroyREAL8Vector( overSFTs );
865 XLALDestroyREAL8Vector( overIFOs );
866
867 if ( !returnMultiPSDVector ) {
868 XLALDestroyMultiPSDVector( *multiPSDVector );
869 *multiPSDVector = NULL;
870 }
871
872 if ( !normalizeSFTsInPlace ) {
873 XLALDestroyUINT4Vector( numSFTsVec );
875 }
876
877 return XLAL_SUCCESS;
878
879} /* XLALComputePSDandNormSFTPower() */
880
881
882/**
883 * Compute the PSD (power spectral density) over a MultiSFTVector.
884 * This is just a convenience wrapper around XLALComputePSDandNormSFTPower()
885 * without the extra options for normSFTpower computation.
886 *
887 * \return a REAL8Vector of the final normalized and averaged/summed PSD.
888 *
889 * NOTE: contrary to earlier versions of this function, it no longer modifies
890 * the inputSFTs in-place, so now it can safely be called multiple times.
891 *
892 */
893int
895 REAL8Vector **finalPSD, /* [out] final PSD averaged over all IFOs and timestamps */
896 MultiSFTVector *inputSFTs, /* [in] the multi-IFO SFT data */
897 const UINT4 blocksRngMed, /* [in] running Median window size */
898 const MathOpType PSDmthopSFTs, /* [in] math operation over SFTs for each IFO (PSD) */
899 const MathOpType PSDmthopIFOs, /* [in] math operation over IFOs (PSD) */
900 const BOOLEAN normalizeByTotalNumSFTs, /* [in] whether to include a final normalization factor derived from the total number of SFTs (over all IFOs); only useful for some mthops */
901 const REAL8 FreqMin, /* [in] starting frequency -> first output bin (if -1: use full SFT data including rngmed bins, else must be >=0) */
902 const REAL8 FreqBand /* [in] frequency band to cover with output bins (must be >=0) */
903)
904{
905
906 MultiPSDVector *multiPSDVector = NULL;
907 REAL8Vector *normSFT = NULL;
909 &multiPSDVector,
910 &normSFT,
911 inputSFTs,
912 0, // returnMultiPSDVector
913 0, // returnNormSFT
914 blocksRngMed,
915 PSDmthopSFTs,
916 PSDmthopIFOs,
917 0, // nSFTmthopSFTs
918 0, // nSFTmthopIFOs
919 normalizeByTotalNumSFTs,
920 FreqMin,
921 FreqBand,
922 FALSE // normalizeSFTsInPlace
923 ) == XLAL_SUCCESS, XLAL_EFUNC );
924
925 return XLAL_SUCCESS;
926
927} /* XLALComputePSDfromSFTs() */
928
929
930/**
931 * Dump complete multi-PSDVector over IFOs, timestamps and frequency-bins into
932 * per-IFO ASCII output-files 'outbname-IFO'
933 *
934 */
935int
936XLALDumpMultiPSDVector( const CHAR *outbname, /**< output basename 'outbname' */
937 const MultiPSDVector *multiPSDVect /**< multi-psd vector to output */
938 )
939{
940 /* check input consistency */
941 if ( outbname == NULL ) {
942 XLALPrintError( "%s: NULL input 'outbname'\n", __func__ );
944 }
945 if ( multiPSDVect == NULL ) {
946 XLALPrintError( "%s: NULL input 'multiPSDVect'\n", __func__ );
948 }
949 if ( multiPSDVect->length == 0 || multiPSDVect->data == 0 ) {
950 XLALPrintError( "%s: invalid multiPSDVect input (length=0 or data=NULL)\n", __func__ );
952 }
953
954 CHAR *fname;
955 FILE *fp;
956
957 UINT4 len = strlen( outbname ) + 4;
958 if ( ( fname = XLALMalloc( len * sizeof( CHAR ) ) ) == NULL ) {
959 XLALPrintError( "%s: XLALMalloc(%d) failed.\n", __func__, len );
961 }
962
963 UINT4 numIFOs = multiPSDVect->length;
964 UINT4 X;
965 for ( X = 0; X < numIFOs; X ++ ) {
966 PSDVector *thisPSDVect = multiPSDVect->data[X];
967 char buf[100];
968
969 sprintf( fname, "%s-%c%c", outbname, thisPSDVect->data[0].name[0], thisPSDVect->data[0].name[1] );
970
971 if ( ( fp = fopen( fname, "wb" ) ) == NULL ) {
972 XLALPrintError( "%s: Failed to open PSDperSFT file '%s' for writing!\n", __func__, fname );
973 XLALFree( fname );
975 }
976
977 REAL8 f0 = thisPSDVect->data[0].f0;
978 REAL8 dFreq = thisPSDVect->data[0].deltaF;
979 UINT4 numFreqs = thisPSDVect->data[0].data->length;
980 UINT4 iFreq;
981
982 /* write comment header line into this output file */
983 /* FIXME: output code-version/cmdline/history info */
984 fprintf( fp, "%%%% first line holds frequencies [Hz] of PSD-Columns\n" );
985 /* loop over frequency and output comnment-header markers */
986 fprintf( fp, "%%%%%-17s", "dummy" );
987 for ( iFreq = 0; iFreq < numFreqs; iFreq ++ ) {
988 sprintf( buf, "f%d [Hz]", iFreq + 1 );
989 fprintf( fp, " %-21s", buf );
990 }
991 fprintf( fp, "\n" );
992
993 /* write parseable header-line giving bin frequencies for PSDs */
994 fprintf( fp, "%-19d", -1 );
995 for ( iFreq = 0; iFreq < numFreqs; iFreq++ ) {
996 fprintf( fp, " %-21.16g", f0 + iFreq * dFreq );
997 }
998 fprintf( fp, "\n\n\n" );
999
1000 /* output another header line describing the following format "ti[GPS] PSD(f1) ... " */
1001 fprintf( fp, "%%%%%-17s", "ti[GPS]" );
1002 for ( iFreq = 0; iFreq < numFreqs; iFreq ++ ) {
1003 sprintf( buf, "PSD(f%d)", iFreq + 1 );
1004 fprintf( fp, " %-21s", buf );
1005 }
1006 fprintf( fp, "\n" );
1007
1008 /* loop over timestamps: dump all PSDs over frequencies into one line */
1009 UINT4 numTS = thisPSDVect->length;
1010 UINT4 iTS;
1011 for ( iTS = 0; iTS < numTS; iTS++ ) {
1012 REAL8FrequencySeries *thisPSD = &thisPSDVect->data[iTS];
1013
1014 /* first output timestamp GPS time for this line */
1015 REAL8 tGPS = XLALGPSGetREAL8( &thisPSD->epoch );
1016 fprintf( fp, "%-19.18g", tGPS );
1017
1018 /* some internal consistency/paranoia checks */
1019 if ( ( f0 != thisPSD->f0 ) || ( dFreq != thisPSD->deltaF ) || ( numFreqs != thisPSD->data->length ) ) {
1020 XLALPrintError( "%s: %d-th timestamp %f: inconsistent PSDVector: f0 = %g : %g, dFreq = %g : %g, numFreqs = %d : %d \n",
1021 __func__, iTS, tGPS, f0, thisPSD->f0, dFreq, thisPSD->deltaF, numFreqs, thisPSD->data->length );
1022 XLALFree( fname );
1023 fclose( fp );
1025 }
1026
1027 /* loop over all frequencies and dump PSD-value */
1028 for ( iFreq = 0; iFreq < numFreqs; iFreq ++ ) {
1029 fprintf( fp, " %-21.16g", thisPSD->data->data[iFreq] );
1030 }
1031
1032 fprintf( fp, "\n" );
1033
1034 } /* for iTS < numTS */
1035
1036 fclose( fp );
1037
1038 } /* for X < numIFOs */
1039
1040 XLALFree( fname );
1041
1042 return XLAL_SUCCESS;
1043
1044} /* XLALDumpMultiPSDVector() */
1045
1046
1047/**
1048 * Write a PSD vector as an ASCII table to an open output file pointer.
1049 *
1050 * Adds a column header string, but version or commandline info
1051 * needs to be printed by the caller.
1052 *
1053 * Standard and (optional) columns: FreqBinStart (FreqBinEnd) PSD (normSFTpower)
1054 */
1055int
1057 FILE *fpOut, /**< output file pointer */
1058 REAL8Vector *PSDVect, /**< required: PSD vector */
1059 REAL8Vector *normSFTVect, /**< optional: normSFT vector (can be NULL if outputNormSFT==False) */
1060 BOOLEAN outputNormSFT, /**< output a column for normSFTVect? */
1061 BOOLEAN outFreqBinEnd, /**< output a column for the end frequency of each bin? */
1062 INT4 PSDmthopBins, /**< math operation for binning of the PSD */
1063 INT4 nSFTmthopBins, /**< math operation for binning of the normSFT */
1064 INT4 binSize, /**< output bin size (in number of input bins, 1 to keep input binning) */
1065 INT4 binStep, /**< output bin step (in number of input bins, 1 to keep input binning) */
1066 REAL8 Freq0, /**< starting frequency of inputs */
1067 REAL8 dFreq /**< frequency step of inputs */
1068)
1069{
1070
1071 /* input sanity checks */
1072 XLAL_CHECK( ( PSDVect != NULL ) && ( PSDVect->data != NULL ) && ( PSDVect->length > 0 ), XLAL_EINVAL, "PSDVect must be allocated and not zero length." );
1073 if ( outputNormSFT ) {
1074 XLAL_CHECK( ( normSFTVect != NULL ) && ( normSFTVect->data != NULL ), XLAL_EINVAL, "If requesting outputNormSFT, normSFTVect must be allocated" );
1075 XLAL_CHECK( normSFTVect->length == PSDVect->length, XLAL_EINVAL, "Lengths of PSDVect and normSFTVect do not match (%d!=%d)", PSDVect->length, normSFTVect->length );
1076 }
1077 XLAL_CHECK( binSize > 0, XLAL_EDOM, "output bin size must be >0" );
1078 XLAL_CHECK( binStep > 0, XLAL_EDOM, "output bin step must be >0" );
1079 XLAL_CHECK( Freq0 >= 0., XLAL_EDOM, "starting frequency must be nonnegative" );
1080 XLAL_CHECK( dFreq >= 0., XLAL_EDOM, "frequency step must be nonnegative" );
1081
1082 /* work out total number of bins */
1083 UINT4 numBins = ( UINT4 )floor( ( PSDVect->length - binSize ) / binStep ) + 1;
1084
1085 /* write column headings */
1086 fprintf( fpOut, "%%%% columns:\n%%%% FreqBinStart" );
1087 if ( outFreqBinEnd ) {
1088 fprintf( fpOut, " FreqBinEnd" );
1089 }
1090 fprintf( fpOut, " PSD" );
1091 if ( outputNormSFT ) {
1092 fprintf( fpOut, " normSFTpower" );
1093 }
1094 fprintf( fpOut, "\n" );
1095
1096 for ( UINT4 k = 0; k < numBins; ++k ) {
1097 UINT4 b = k * binStep;
1098
1099 REAL8 f0 = Freq0 + b * dFreq;
1100 REAL8 f1 = f0 + binStep * dFreq;
1101 fprintf( fpOut, "%f", f0 );
1102 if ( outFreqBinEnd ) {
1103 fprintf( fpOut, " %f", f1 );
1104 }
1105
1106 REAL8 psd = XLALMathOpOverArray( &( PSDVect->data[b] ), binSize, PSDmthopBins );
1107 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( psd ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for psd[k=%d]", k );
1108
1109 fprintf( fpOut, " %e", psd );
1110
1111 if ( outputNormSFT ) {
1112 REAL8 nsft = XLALMathOpOverArray( &( normSFTVect->data[b] ), binSize, nSFTmthopBins );
1113 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( nsft ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for nsft[k=%d]", k );
1114
1115 fprintf( fpOut, " %f", nsft );
1116 }
1117
1118 fprintf( fpOut, "\n" );
1119 } // k < numBins
1120
1121 return XLAL_SUCCESS;
1122
1123} /* XLALWritePSDtoFile() */
1124
1125/// @}
#define __func__
log an I/O error, i.e.
int k
Internal SFT types and functions.
#define fprintf
const double Q
REAL8FrequencySeries * XLALResizeREAL8FrequencySeries(REAL8FrequencySeries *series, int first, size_t length)
REAL8FrequencySeries * XLALCreateREAL8FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
COMPLEX8FrequencySeries * XLALResizeCOMPLEX8FrequencySeries(COMPLEX8FrequencySeries *series, int first, size_t length)
void XLALDestroyREAL8FrequencySeries(REAL8FrequencySeries *series)
unsigned char BOOLEAN
double REAL8
char CHAR
uint32_t UINT4
float complex COMPLEX8
int32_t INT4
void * XLALMalloc(size_t n)
void * XLALCalloc(size_t m, size_t n)
void XLALFree(void *p)
MultiPSDVector * XLALNormalizeMultiSFTVect(MultiSFTVector *multsft, UINT4 blockSize, const MultiNoiseFloor *assumeSqrtSX)
Function for normalizing a multi vector of SFTs in a multi IFO search and returns the running-median ...
int XLALDumpMultiPSDVector(const CHAR *outbname, const MultiPSDVector *multiPSDVect)
Dump complete multi-PSDVector over IFOs, timestamps and frequency-bins into per-IFO ASCII output-file...
Definition: PSDutils.c:936
int XLALWritePSDtoFilePointer(FILE *fpOut, REAL8Vector *PSDVect, REAL8Vector *normSFTVect, BOOLEAN outputNormSFT, BOOLEAN outFreqBinEnd, INT4 PSDmthopBins, INT4 nSFTmthopBins, INT4 binSize, INT4 binStep, REAL8 Freq0, REAL8 dFreq)
Write a PSD vector as an ASCII table to an open output file pointer.
Definition: PSDutils.c:1056
int XLALCropMultiPSDandSFTVectors(MultiPSDVector *multiPSDVect, MultiSFTVector *multiSFTVect, UINT4 firstBin, UINT4 lastBin)
Function that truncates the PSD in place to the requested frequency-bin interval [firstBin,...
Definition: PSDutils.c:195
MultiNoiseWeights * XLALCopyMultiNoiseWeights(const MultiNoiseWeights *multiWeights)
Create a full copy of a MultiNoiseWeights object.
Definition: PSDutils.c:143
const UserChoices MathOpTypeChoices
Definition: PSDutils.c:44
int XLALComputePSDfromSFTs(REAL8Vector **finalPSD, MultiSFTVector *inputSFTs, const UINT4 blocksRngMed, const MathOpType PSDmthopSFTs, const MathOpType PSDmthopIFOs, const BOOLEAN normalizeByTotalNumSFTs, const REAL8 FreqMin, const REAL8 FreqBand)
Compute the PSD (power spectral density) over a MultiSFTVector.
Definition: PSDutils.c:894
void XLALDestroyPSDVector(PSDVector *vect)
Destroy a PSD-vector.
Definition: PSDutils.c:74
void XLALDestroyMultiPSDVector(MultiPSDVector *multvect)
Destroy a multi PSD-vector.
Definition: PSDutils.c:102
int XLALComputePSDandNormSFTPower(REAL8Vector **finalPSD, MultiPSDVector **multiPSDVector, REAL8Vector **normSFT, MultiSFTVector *inputSFTs, const BOOLEAN returnMultiPSDVector, const BOOLEAN returnNormSFT, const UINT4 blocksRngMed, const MathOpType PSDmthopSFTs, const MathOpType PSDmthopIFOs, const MathOpType nSFTmthopSFTs, const MathOpType nSFTmthopIFOs, const BOOLEAN normalizeByTotalNumSFTs, const REAL8 FreqMin, const REAL8 FreqBand, const BOOLEAN normalizeSFTsInPlace)
Compute the PSD (power spectral density) and the "normalized power" over a MultiSFTVector.
Definition: PSDutils.c:677
MultiNoiseWeights * XLALComputeMultiNoiseWeights(const MultiPSDVector *rngmed, UINT4 blocksRngMed, UINT4 excludePercentile)
Computes weight factors arising from MultiSFTs with different noise floors.
Definition: PSDutils.c:285
void XLALDestroyMultiNoiseWeights(MultiNoiseWeights *weights)
Destroy a MultiNoiseWeights object.
Definition: PSDutils.c:172
MultiNoiseWeights * XLALCreateMultiNoiseWeights(const UINT4 length)
Create a MultiNoiseWeights of the given length.
Definition: PSDutils.c:125
MathOpType
common types of mathematical operations over an array
Definition: PSDutils.h:82
REAL8 XLALMathOpOverArray(const REAL8 *data, const size_t length, const MathOpType optype)
Compute various types of "math operations" over the entries of an array.
Definition: PSDutils.c:491
REAL8FrequencySeries * XLALComputeSegmentDataQ(const MultiPSDVector *multiPSDVect, LALSeg segment)
Compute the "data-quality factor" over the given SFTs.
Definition: PSDutils.c:375
REAL8 XLALGetMathOpNormalizationFactorFromTotalNumberOfSFTs(const UINT4 totalNumSFTs, const MathOpType optypeSFTs)
Compute an additional normalization factor from the total number of SFTs to be applied after a loop o...
Definition: PSDutils.c:627
@ MATH_OP_MINIMUM
Definition: PSDutils.h:90
@ MATH_OP_HARMONIC_SUM
Definition: PSDutils.h:86
@ MATH_OP_POWERMINUS2_SUM
Definition: PSDutils.h:88
@ MATH_OP_HARMONIC_MEAN
Definition: PSDutils.h:87
@ MATH_OP_MAXIMUM
Definition: PSDutils.h:91
@ MATH_OP_ARITHMETIC_SUM
Definition: PSDutils.h:83
@ MATH_OP_ARITHMETIC_MEDIAN
Definition: PSDutils.h:85
@ MATH_OP_POWERMINUS2_MEAN
Definition: PSDutils.h:89
@ MATH_OP_ARITHMETIC_MEAN
Definition: PSDutils.h:84
void XLALDestroyMultiSFTVector(MultiSFTVector *multvect)
Destroy a multi SFT-vector.
Definition: SFTtypes.c:424
REAL8 TSFTfromDFreq(REAL8 dFreq)
Definition: SFTtypes.c:1144
int XLALCopySFT(SFTtype *dest, const SFTtype *src)
Copy an entire SFT-type into another.
Definition: SFTtypes.c:202
UINT4 XLALRoundFrequencyUpToSFTBin(const REAL8 freq, const REAL8 df)
Round a REAL8 frequency up to the nearest integer SFT bin number.
Definition: SFTtypes.c:82
MultiSFTVector * XLALCreateEmptyMultiSFTVector(UINT4Vector *numsft)
Create an empty multi-IFO SFT vector with a given number of SFTs per IFO (which are not allocated).
Definition: SFTtypes.c:394
UINT4 XLALRoundFrequencyDownToSFTBin(const REAL8 freq, const REAL8 df)
Round a REAL8 frequency down to the nearest integer SFT bin number.
Definition: SFTtypes.c:70
int XLALCWGPSinRange(const LIGOTimeGPS gps, const LIGOTimeGPS *minGPS, const LIGOTimeGPS *maxGPS)
Defines the official CW convention for whether a GPS time is 'within' a given range,...
Definition: SFTtypes.c:52
const LALUnit lalHertzUnit
void XLALDestroyUINT4Vector(UINT4Vector *vector)
REAL8Vector * XLALCreateREAL8Vector(UINT4 length)
void XLALDestroyREAL8Vector(REAL8Vector *vector)
UINT4Vector * XLALCreateUINT4Vector(UINT4 length)
#define XLAL_ERROR_REAL8(...)
#define XLAL_PRINT_INFO(...)
#define XLAL_ERROR_NULL(...)
#define xlalErrno
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_CHECK_NULL(assertion,...)
#define XLAL_IS_REAL8_FAIL_NAN(val)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_ERANGE
XLAL_EFUNC
XLAL_EDOM
XLAL_ESYS
XLAL_EINVAL
XLAL_EFAILED
REAL8 XLALGPSGetREAL8(const LIGOTimeGPS *epoch)
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
float data[BLOCKSIZE]
Definition: hwinject.c:360
psd
list weights
#define FALSE
double alpha
COMPLEX8Sequence * data
A vector of COMPLEX8FrequencySeries.
COMPLEX8FrequencySeries * data
Pointer to the data array.
UINT4 length
Number of elements in array.
COMPLEX8 * data
LIGOTimeGPS end
LIGOTimeGPS start
One noise-weight (number) per SFT (therefore indexed over IFOs and SFTs.
Definition: PSDutils.h:71
REAL8 Sinv_Tsft
normalization factor used: (using single-sided PSD!)
Definition: PSDutils.h:77
UINT4 length
number of detectors
Definition: PSDutils.h:75
REAL8Vector ** data
weights-vector for each detector
Definition: PSDutils.h:76
BOOLEAN isNotNormalized
if true: weights are saved unnormalized (divide by Sinv_Tsft to get normalized version).
Definition: PSDutils.h:78
A collection of PSD vectors – one for each IFO in a multi-IFO search.
Definition: PSDutils.h:62
PSDVector ** data
sftvector for each ifo
Definition: PSDutils.h:67
UINT4 length
number of ifos
Definition: PSDutils.h:66
A collection of SFT vectors – one for each IFO in a multi-IFO search.
Definition: SFTfileIO.h:179
UINT4 length
number of ifos
Definition: SFTfileIO.h:183
SFTVector ** data
sftvector for each ifo
Definition: SFTfileIO.h:184
REAL8Sequence * data
CHAR name[LALNameLength]
A vector of REAL8FrequencySeries.
REAL8FrequencySeries * data
Pointer to the data array.
UINT4 length
Number of elements in array.
REAL8 * data
UINT4 * data