LALPulsar 7.1.2.1-bf6a62b
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
42 { MATH_OP_ARITHMETIC_SUM, "arithsum" },
43 { MATH_OP_ARITHMETIC_MEAN, "arithmean" },
44 { MATH_OP_ARITHMETIC_MEDIAN, "arithmedian" },
45 { MATH_OP_HARMONIC_SUM, "harmsum" },
46 { MATH_OP_HARMONIC_MEAN, "harmmean" },
47 { MATH_OP_POWERMINUS2_SUM, "powerminus2sum" },
48 { MATH_OP_POWERMINUS2_MEAN, "powerminus2mean" },
49 { MATH_OP_MINIMUM, "min" },
50 { MATH_OP_MAXIMUM, "max" },
51 /*
52 * NOTE: these options used to also be available as numerical values:
53 * - MATH_OP_ARITHMETIC_SUM: 0
54 * - MATH_OP_ARITHMETIC_MEAN: 1
55 * - MATH_OP_ARITHMETIC_MEDIAN: 2
56 * - MATH_OP_HARMONIC_SUM: 3
57 * - MATH_OP_HARMONIC_MEAN: 4
58 * - MATH_OP_POWERMINUS2_SUM: 5
59 * - MATH_OP_POWERMINUS2_MEAN: 6
60 * - MATH_OP_MINIMUM: 7
61 * - MATH_OP_MAXIMUM: 8
62 */
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 * See also XLALMathOpOverREAL8Vector().
491 *
492 */
493REAL8 XLALMathOpOverArray( const REAL8 *data, /**< input data array */
494 const size_t length, /**< length of the input data array */
495 const MathOpType optype /**< type of operation */
496 )
497{
498
499 UINT4 i;
500 REAL8 res = 0.0;
501
502 switch ( optype ) {
503
504 case MATH_OP_ARITHMETIC_SUM: /* sum(data) */
505
506 for ( i = 0; i < length; ++i ) {
507 res += *( data++ );
508 }
509
510 break;
511
512 case MATH_OP_ARITHMETIC_MEAN: /* sum(data) / length */
513
514 for ( i = 0; i < length; ++i ) {
515 res += *( data++ );
516 }
517 res /= ( REAL8 )length;
518
519 break;
520
521 case MATH_OP_HARMONIC_SUM: /* 1 / sum(1 / data) */
522
523 for ( i = 0; i < length; ++i ) {
524 res += 1.0 / *( data++ );
525 }
526 res = 1.0 / res;
527
528 break;
529
530 case MATH_OP_HARMONIC_MEAN: /* length / sum(1 / data) */
531
532 for ( i = 0; i < length; ++i ) {
533 res += 1.0 / *( data++ );
534 }
535 res = ( REAL8 )length / res;
536
537 break;
538
539 case MATH_OP_POWERMINUS2_SUM: /* sqrt(1 / sum(1 / (data * data))) */
540
541 for ( i = 0; i < length; ++i ) {
542 res += 1.0 / ( data[i] * data[i] );
543 }
544 res = 1.0 / sqrt( res );
545
546 break;
547
548 case MATH_OP_POWERMINUS2_MEAN: /* sqrt(length / sum(1 / (data * data))) */
549
550 for ( i = 0; i < length; ++i ) {
551 res += 1.0 / ( data[i] * data[i] );
552 }
553 res = 1.0 / sqrt( res / ( REAL8 )length );
554
555 break;
556
557 /* these cases require sorted data;
558 * we use gsl_sort_index() to avoid in-place modification of the input data
559 * by gsl_sort()
560 */
562 case MATH_OP_MINIMUM:
563 case MATH_OP_MAXIMUM:
564 ; /* empty statement because declaration cannot be first line in a switch case */
565 size_t *sortidx;
566 if ( ( sortidx = XLALMalloc( length * sizeof( size_t ) ) ) == NULL ) {
567 XLALPrintError( "XLALMalloc(%ld) failed.\n", length );
569 }
570 gsl_sort_index( sortidx, data, 1, length );
571
572 switch ( optype ) {
573
574 case MATH_OP_ARITHMETIC_MEDIAN: /* middle element of sorted data */
575
576 if ( length / 2 == ( length + 1 ) / 2 ) { /* length is even */
577 res = ( data[sortidx[length / 2 - 1]] + data[sortidx[length / 2]] ) / 2;
578 } else { /* length is odd */
579 res = data[sortidx[length / 2]];
580 }
581
582 break;
583
584 case MATH_OP_MINIMUM: /* first element of sorted data */
585
586 res = data[sortidx[0]];
587 break;
588
589 case MATH_OP_MAXIMUM: /* last element of sorted data */
590
591 res = data[sortidx[length - 1]];
592 break;
593
594 default:
595
596 XLAL_ERROR_REAL8( XLAL_EINVAL, "For optype=%i this block should not have been reached.", optype );
597
598 }
599
600 XLALFree( sortidx );
601 break;
602
603 default:
604
605 XLAL_ERROR_REAL8( XLAL_EINVAL, "'%i' is not an implemented math. operation", optype );
606
607 } /* switch (optype) */
608
609 return res;
610
611} /* XLALMathOpOverArray() */
612
613
614/**
615 * Compute various types of "math operations" over the entries of a REAL8Vector.
616 *
617 * The supported optypes (e.g. sums and averages) are defined in \c MathOpType.
618 *
619 * See also XLALMathOpOverArray().
620 */
622 const REAL8Vector *data, /**< input data array */
623 const MathOpType optype /**< type of operation */
624)
625{
626
628
629 // Call XLALMathOpOverArray()
630 REAL8 res = XLALMathOpOverArray( data->data, data->length, optype );
632
633 return res;
634
635}
636
637
638/**
639 * Compute an additional normalization factor from the total number of SFTs to be applied after a loop of mathops over SFTs and IFOs.
640 *
641 * Currently only implemented for:
642 * MATH_OP_HARMONIC_SUM (to emulate MATH_OP_HARMONIC_MEAN over the combined array)
643 * MATH_OP_POWERMINUS2_SUM (to emulate MATH_OP_POWERMINUS2_MEAN over the combined array)
644 *
645 * This function exists only as a simple workaround for when we want to compute
646 * some types of mathops, e.g. the harmonic or power2 mean,
647 * over all SFTs from all detectors together:
648 * then call XLALComputePSDandNormSFTPower with PSDmthopSFTs=PSDmthopIFOs=MATH_OP_[HARMONIC/POWERMINUS2]_SUM
649 * and then this factor is applied at the end,
650 * which gives equivalent results to if we had rewritten the loop order in XLALComputePSDandNormSFTPower
651 * to allow for calling MATH_OP_[HARMONIC/POWERMINUS2]_MEAN over the combined array.
652 */
654 const UINT4 totalNumSFTs, /**< total number of SFTs from all IFOs */
655 const MathOpType optypeSFTs /**< type of operations that was done over SFTs */
656)
657{
658
659 REAL8 factor;
660
661 switch ( optypeSFTs ) {
662
663 case MATH_OP_ARITHMETIC_SUM: /* sum(data) */
664 case MATH_OP_ARITHMETIC_MEAN: /* sum(data) / length */
665 case MATH_OP_HARMONIC_MEAN: /* length / sum(1 / data) */
666 case MATH_OP_ARITHMETIC_MEDIAN: /* middle element of sorted data */
667 case MATH_OP_MINIMUM: /* first element of sorted data */
668 case MATH_OP_MAXIMUM: /* last element of sorted data */
669 case MATH_OP_POWERMINUS2_MEAN: /* sqrt(length / sum(1 / (data * data))) */
670 XLALPrintWarning( "%s: no additional normalisation factor defined for math operation '%i'", __func__, optypeSFTs );
671 factor = 1;
672 break;
673
674 case MATH_OP_HARMONIC_SUM: /* 1 / sum(1 / data) */
675 factor = totalNumSFTs;
676 break;
677
678 case MATH_OP_POWERMINUS2_SUM: /* sqrt(1 / sum(1 / (data * data))) */
679 factor = sqrt( totalNumSFTs );
680 break;
681
682 default:
683 XLAL_ERROR_REAL8( XLAL_EINVAL, "'%i' is not an implemented math. operation", optypeSFTs );
684
685 } /* switch (optypeSFTs) */
686
687 return factor;
688
689} /* XLALGetMathOpNormalizationFactorFromTotalNumberOfSFTs() */
690
691
692/**
693 * Compute the PSD (power spectral density) and the "normalized power" \f$ P_\mathrm{SFT} \f$ over a MultiSFTVector.
694 *
695 * \return: a REAL8Vector of the final normalized and averaged/summed PSD;
696 * plus the full multiPSDVector array,
697 * and optionally a REAL8Vector of normalized SFT power (only if not passed as NULL)
698 *
699 * If normalizeSFTsInPlace is TRUE, then the inputSFTs will be modified by XLALNormalizeMultiSFTVect().
700 * If it is FALSE, an internal copy will be used and the inputSFTs will be returned unmodified.
701 */
702int
704 REAL8Vector **finalPSD, /* [out] final PSD averaged over all IFOs and timestamps */
705 MultiPSDVector **multiPSDVector, /* [out] complete MultiPSDVector over IFOs, timestamps and freq-bins */
706 REAL8Vector **normSFT, /* [out] normalised SFT power (optional) */
707 MultiSFTVector *inputSFTs, /* [in] the multi-IFO SFT data */
708 const BOOLEAN returnMultiPSDVector, /* [in] whether to return multiPSDVector */
709 const BOOLEAN returnNormSFT, /* [in] whether to return normSFT vector */
710 const UINT4 blocksRngMed, /* [in] running Median window size */
711 const MathOpType PSDmthopSFTs, /* [in] math operation over SFTs for each IFO (PSD) */
712 const MathOpType PSDmthopIFOs, /* [in] math operation over IFOs (PSD) */
713 const MathOpType nSFTmthopSFTs, /* [in] math operation over SFTs for each IFO (normSFT) */
714 const MathOpType nSFTmthopIFOs, /* [in] math operation over IFOs (normSFT) */
715 const BOOLEAN PSDnormByTotalNumSFTs, /* [in] for PSD, whether to include a final normalization factor derived from the total number of SFTs (over all IFOs); only useful for some mthops */
716 const REAL8 FreqMin, /* [in] starting frequency -> first output bin (if -1: use full SFT data including rngmed bins, else must be >=0) */
717 const REAL8 FreqBand, /* [in] frequency band to cover with output bins (must be >=0) */
718 const BOOLEAN normalizeSFTsInPlace /* [in] if FALSE, a copy of inputSFTs will be used internally and the original will not be modified */
719)
720{
721
722 /* sanity checks */
723 XLAL_CHECK( finalPSD, XLAL_EINVAL );
724 XLAL_CHECK( multiPSDVector, XLAL_EINVAL );
725 XLAL_CHECK( normSFT, XLAL_EINVAL );
726 XLAL_CHECK( inputSFTs && inputSFTs->data && inputSFTs->length > 0, XLAL_EINVAL, "inputSFTs must be pre-allocated." );
727 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." );
728 XLAL_CHECK( !PSDnormByTotalNumSFTs || PSDmthopSFTs == PSDmthopIFOs, XLAL_EINVAL, "when PSDnormByTotalNumSFTs=true, PSDmthopSFTs(=%i) must equal PSDmthopIFOs(=%i)", PSDmthopSFTs, PSDmthopIFOs );
729
730 /* get power running-median rngmed[ |data|^2 ] from SFTs *
731 * as the output of XLALNormalizeMultiSFTVect(),
732 * multiPSD will be a rng-median smoothed periodogram over the normalized SFTs.
733 * The inputSFTs themselves will also be normalized in this call
734 * unless overruled by normalizeSFTsInPlace option.
735 */
736 UINT4Vector *numSFTsVec = NULL;
737 MultiSFTVector *SFTs = NULL;
738 UINT4 numIFOs = inputSFTs->length;
739 if ( normalizeSFTsInPlace ) {
740 SFTs = inputSFTs;
741 } else {
742 numSFTsVec = XLALCreateUINT4Vector( numIFOs );
743 for ( UINT4 X = 0; X < numIFOs; ++X ) {
744 numSFTsVec->data[X] = inputSFTs->data[X]->length;
745 }
746 SFTs = XLALCreateEmptyMultiSFTVector( numSFTsVec );
747 for ( UINT4 X = 0; X < numIFOs; ++X ) {
748 for ( UINT4 k = 0; k < numSFTsVec->data[X]; ++k ) {
749 XLAL_CHECK( XLALCopySFT( &( SFTs->data[X]->data[k] ), &( inputSFTs->data[X]->data[k] ) ) == XLAL_SUCCESS, XLAL_EFUNC );
750 }
751 }
752 }
753 XLAL_CHECK( ( *multiPSDVector = XLALNormalizeMultiSFTVect( SFTs, blocksRngMed, NULL ) ) != NULL, XLAL_EFUNC );
754 UINT4 numBinsSFTs = SFTs->data[0]->data[0].data->length;
755 REAL8 dFreq = ( *multiPSDVector )->data[0]->data[0].deltaF;
756
757 /* restrict to just the "physical" band if requested */
758 /* first, figure out the effective PSD bin-boundaries for user */
759 UINT4 firstBin, lastBin;
760 if ( FreqMin > 0 ) { /* user requested a constrained band */
761 REAL8 fminSFTs = SFTs->data[0]->data[0].f0;
762 REAL8 fmaxSFTs = fminSFTs + numBinsSFTs * dFreq;
763 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 );
764 /* check band wide enough for wings */
765 UINT4 rngmedSideBandBins = blocksRngMed / 2 + 1; /* truncates down plus add one bin extra safety! */
766 REAL8 rngmedSideBand = rngmedSideBandBins * dFreq;
767 /* set the actual output range in bins */
768 UINT4 minBinPSD = XLALRoundFrequencyDownToSFTBin( FreqMin, dFreq );
769 UINT4 maxBinPSD = XLALRoundFrequencyUpToSFTBin( FreqMin + FreqBand, dFreq ) - 1;
770 UINT4 minBinSFTs = XLALRoundFrequencyDownToSFTBin( fminSFTs, dFreq );
771 UINT4 maxBinSFTs = XLALRoundFrequencyUpToSFTBin( fmaxSFTs, dFreq ) - 1;
772 if ( ( minBinPSD < minBinSFTs ) || ( maxBinPSD > maxBinSFTs ) ) {
773 XLAL_ERROR( XLAL_ERANGE, "Requested frequency range [%f,%f)Hz means rngmedSideBand=%fHz (%d bins) would spill over available SFT band [%f,%f)Hz",
774 FreqMin, FreqMin + FreqBand, rngmedSideBand, rngmedSideBandBins, fminSFTs, fmaxSFTs );
775 }
776 UINT4 binsBand = maxBinPSD - minBinPSD + 1;
777 firstBin = minBinPSD - minBinSFTs;
778 lastBin = firstBin + binsBand - 1;
779 } else { /* output all bins loaded from SFTs (includes rngmed-sidebands) */
780 firstBin = 0;
781 lastBin = numBinsSFTs - 1;
782 }
783 XLAL_PRINT_INFO( "loaded SFTs have %d bins, effective PSD output band is [%d, %d]", numBinsSFTs, firstBin, lastBin );
784 /* now truncate */
785 XLAL_CHECK( XLALCropMultiPSDandSFTVectors( *multiPSDVector, SFTs, firstBin, lastBin ) == XLAL_SUCCESS, XLAL_EFUNC, "Failed call to XLALCropMultiPSDandSFTVectors (multiPSDVector, SFTs, firstBin=%d, lastBin=%d)", firstBin, lastBin );
786
787 /* number of raw bins in final PSD */
788 UINT4 numBins = ( *multiPSDVector )->data[0]->data[0].data->length;
789
790 /* allocate main output struct, using cropped length */
791 XLAL_CHECK( ( *finalPSD = XLALCreateREAL8Vector( numBins ) ) != NULL, XLAL_ENOMEM, "Failed to create REAL8Vector for finalPSD." );
792
793 /* number of IFOs */
794 REAL8Vector *overIFOs = NULL; /* one frequency bin over IFOs */
795 XLAL_CHECK( ( overIFOs = XLALCreateREAL8Vector( numIFOs ) ) != NULL, XLAL_ENOMEM, "Failed to create REAL8Vector for overIFOs array." );
796
797 /* maximum number of SFTs */
798 UINT4 maxNumSFTs = 0;
799 for ( UINT4 X = 0; X < numIFOs; ++X ) {
800 maxNumSFTs = GSL_MAX( maxNumSFTs, ( *multiPSDVector )->data[X]->length );
801 }
802
803 REAL8Vector *overSFTs = NULL; /* one frequency bin over SFTs */
804 XLAL_CHECK( ( overSFTs = XLALCreateREAL8Vector( maxNumSFTs ) ) != NULL, XLAL_ENOMEM, "Failed to create REAL8Vector for overSFTs array." );
805
806 /* normalize rngmd(power) to get proper *single-sided* PSD: Sn = (2/Tsft) rngmed[|data|^2]] */
807 REAL8 normPSD = 2.0 * dFreq;
808
809 /* 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:
810 * call with PSDmthopSFTs=PSDmthopIFOs=MATH_OP_[HARMONIC/POWERMINUS2]_SUM
811 * and then this factor is applied at the end,
812 * which gives equivalent results to if we had rewritten the loop order
813 * to allow for calling MATH_OP_[HARMONIC/POWERMINUS2]_MEAN over the combined array.
814 */
815 REAL8 PSDtotalNumSFTsNormalizingFactor = 1.;
816 if ( PSDnormByTotalNumSFTs ) {
817 UINT4 totalNumSFTs = 0;
818 for ( UINT4 X = 0; X < numIFOs; ++X ) {
819 totalNumSFTs += ( *multiPSDVector )->data[X]->length;
820 }
821 PSDtotalNumSFTsNormalizingFactor = XLALGetMathOpNormalizationFactorFromTotalNumberOfSFTs( totalNumSFTs, PSDmthopSFTs );
822 }
823
824 XLAL_PRINT_INFO( "Computing spectrogram and PSD ..." );
825 /* loop over frequency bins in final PSD */
826 for ( UINT4 k = 0; k < numBins; ++k ) {
827
828 /* loop over IFOs */
829 for ( UINT4 X = 0; X < numIFOs; ++X ) {
830
831 /* number of SFTs for this IFO */
832 UINT4 numSFTs = ( *multiPSDVector )->data[X]->length;
833
834 /* copy PSD frequency bins and normalise multiPSDVector for later use */
835 for ( UINT4 alpha = 0; alpha < numSFTs; ++alpha ) {
836 ( *multiPSDVector )->data[X]->data[alpha].data->data[k] *= normPSD;
837 overSFTs->data[alpha] = ( *multiPSDVector )->data[X]->data[alpha].data->data[k];
838 }
839
840 /* compute math. operation over SFTs for this IFO */
841 overIFOs->data[X] = XLALMathOpOverArray( overSFTs->data, numSFTs, PSDmthopSFTs );
842 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( overIFOs->data[X] ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for overIFOs->data[X=%d]", X );
843
844 } /* for IFOs X */
845
846 /* compute math. operation over IFOs for this frequency */
847 ( *finalPSD )->data[k] = XLALMathOpOverArray( overIFOs->data, numIFOs, PSDmthopIFOs );
848 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( ( *finalPSD )->data[k] ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for finalPSD->data[k=%d]", k );
849
850 if ( PSDnormByTotalNumSFTs ) {
851 ( *finalPSD )->data[k] *= PSDtotalNumSFTsNormalizingFactor;
852 }
853
854 } /* for freq bins k */
855 XLAL_PRINT_INFO( "done." );
856
857 /* compute normalised SFT power */
858 if ( returnNormSFT ) {
859 XLAL_PRINT_INFO( "Computing normalised SFT power ..." );
860 XLAL_CHECK( ( *normSFT = XLALCreateREAL8Vector( numBins ) ) != NULL, XLAL_ENOMEM, "Failed to create REAL8Vector for normSFT." );
861
862 /* loop over frequency bins in SFTs */
863 for ( UINT4 k = 0; k < numBins; ++k ) {
864
865 /* loop over IFOs */
866 for ( UINT4 X = 0; X < numIFOs; ++X ) {
867
868 /* number of SFTs for this IFO */
869 UINT4 numSFTs = SFTs->data[X]->length;
870
871 /* compute SFT power */
872 for ( UINT4 alpha = 0; alpha < numSFTs; ++alpha ) {
873 COMPLEX8 bin = SFTs->data[X]->data[alpha].data->data[k];
874 overSFTs->data[alpha] = crealf( bin ) * crealf( bin ) + cimagf( bin ) * cimagf( bin );
875 }
876
877 /* compute math. operation over SFTs for this IFO */
878 overIFOs->data[X] = XLALMathOpOverArray( overSFTs->data, numSFTs, nSFTmthopSFTs );
879 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( overIFOs->data[X] ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for overIFOs->data[X=%d]", X );
880
881 } /* over IFOs */
882
883 /* compute math. operation over IFOs for this frequency */
884 ( *normSFT )->data[k] = XLALMathOpOverArray( overIFOs->data, numIFOs, nSFTmthopIFOs );
885 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( ( *normSFT )->data[k] ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for normSFT->data[k=%d]", k );
886
887 } /* over freq bins */
888 XLAL_PRINT_INFO( "done." );
889 } /* if returnNormSFT */
890
891 XLALDestroyREAL8Vector( overSFTs );
892 XLALDestroyREAL8Vector( overIFOs );
893
894 if ( !returnMultiPSDVector ) {
895 XLALDestroyMultiPSDVector( *multiPSDVector );
896 *multiPSDVector = NULL;
897 }
898
899 if ( !normalizeSFTsInPlace ) {
900 XLALDestroyUINT4Vector( numSFTsVec );
902 }
903
904 return XLAL_SUCCESS;
905
906} /* XLALComputePSDandNormSFTPower() */
907
908
909/**
910 * Compute the PSD (power spectral density) over a MultiSFTVector.
911 * This is just a convenience wrapper around XLALComputePSDandNormSFTPower()
912 * without the extra options for normSFTpower computation.
913 *
914 * \return a REAL8Vector of the final normalized and averaged/summed PSD.
915 *
916 * NOTE: contrary to earlier versions of this function, it no longer modifies
917 * the inputSFTs in-place, so now it can safely be called multiple times.
918 *
919 */
920int
922 REAL8Vector **finalPSD, /* [out] final PSD averaged over all IFOs and timestamps */
923 MultiSFTVector *inputSFTs, /* [in] the multi-IFO SFT data */
924 const UINT4 blocksRngMed, /* [in] running Median window size */
925 const MathOpType PSDmthopSFTs, /* [in] math operation over SFTs for each IFO (PSD) */
926 const MathOpType PSDmthopIFOs, /* [in] math operation over IFOs (PSD) */
927 const BOOLEAN PSDnormByTotalNumSFTs, /* [in] for PSD, whether to include a final normalization factor derived from the total number of SFTs (over all IFOs); only useful for some mthops */
928 const REAL8 FreqMin, /* [in] starting frequency -> first output bin (if -1: use full SFT data including rngmed bins, else must be >=0) */
929 const REAL8 FreqBand /* [in] frequency band to cover with output bins (must be >=0) */
930)
931{
932
933 MultiPSDVector *multiPSDVector = NULL;
934 REAL8Vector *normSFT = NULL;
936 &multiPSDVector,
937 &normSFT,
938 inputSFTs,
939 0, // returnMultiPSDVector
940 0, // returnNormSFT
941 blocksRngMed,
942 PSDmthopSFTs,
943 PSDmthopIFOs,
944 0, // nSFTmthopSFTs
945 0, // nSFTmthopIFOs
946 PSDnormByTotalNumSFTs,
947 FreqMin,
948 FreqBand,
949 FALSE // normalizeSFTsInPlace
950 ) == XLAL_SUCCESS, XLAL_EFUNC );
951
952 return XLAL_SUCCESS;
953
954} /* XLALComputePSDfromSFTs() */
955
956
957/**
958 * Dump complete multi-PSDVector over IFOs, timestamps and frequency-bins into
959 * per-IFO ASCII output-files 'outbname-IFO'
960 *
961 */
962int
963XLALDumpMultiPSDVector( const CHAR *outbname, /**< output basename 'outbname' */
964 const MultiPSDVector *multiPSDVect /**< multi-psd vector to output */
965 )
966{
967 /* check input consistency */
968 if ( outbname == NULL ) {
969 XLALPrintError( "%s: NULL input 'outbname'\n", __func__ );
971 }
972 if ( multiPSDVect == NULL ) {
973 XLALPrintError( "%s: NULL input 'multiPSDVect'\n", __func__ );
975 }
976 if ( multiPSDVect->length == 0 || multiPSDVect->data == 0 ) {
977 XLALPrintError( "%s: invalid multiPSDVect input (length=0 or data=NULL)\n", __func__ );
979 }
980
981 CHAR *fname;
982 FILE *fp;
983
984 UINT4 len = strlen( outbname ) + 4;
985 if ( ( fname = XLALMalloc( len * sizeof( CHAR ) ) ) == NULL ) {
986 XLALPrintError( "%s: XLALMalloc(%d) failed.\n", __func__, len );
988 }
989
990 UINT4 numIFOs = multiPSDVect->length;
991 UINT4 X;
992 for ( X = 0; X < numIFOs; X ++ ) {
993 PSDVector *thisPSDVect = multiPSDVect->data[X];
994 char buf[100];
995
996 sprintf( fname, "%s-%c%c", outbname, thisPSDVect->data[0].name[0], thisPSDVect->data[0].name[1] );
997
998 if ( ( fp = fopen( fname, "wb" ) ) == NULL ) {
999 XLALPrintError( "%s: Failed to open PSDperSFT file '%s' for writing!\n", __func__, fname );
1000 XLALFree( fname );
1002 }
1003
1004 REAL8 f0 = thisPSDVect->data[0].f0;
1005 REAL8 dFreq = thisPSDVect->data[0].deltaF;
1006 UINT4 numFreqs = thisPSDVect->data[0].data->length;
1007 UINT4 iFreq;
1008
1009 /* write comment header line into this output file */
1010 /* FIXME: output code-version/cmdline/history info */
1011 fprintf( fp, "%%%% first line holds frequencies [Hz] of PSD-Columns\n" );
1012 /* loop over frequency and output comnment-header markers */
1013 fprintf( fp, "%%%%%-17s", "dummy" );
1014 for ( iFreq = 0; iFreq < numFreqs; iFreq ++ ) {
1015 sprintf( buf, "f%d [Hz]", iFreq + 1 );
1016 fprintf( fp, " %-21s", buf );
1017 }
1018 fprintf( fp, "\n" );
1019
1020 /* write parseable header-line giving bin frequencies for PSDs */
1021 fprintf( fp, "%-19d", -1 );
1022 for ( iFreq = 0; iFreq < numFreqs; iFreq++ ) {
1023 fprintf( fp, " %-21.16g", f0 + iFreq * dFreq );
1024 }
1025 fprintf( fp, "\n\n\n" );
1026
1027 /* output another header line describing the following format "ti[GPS] PSD(f1) ... " */
1028 fprintf( fp, "%%%%%-17s", "ti[GPS]" );
1029 for ( iFreq = 0; iFreq < numFreqs; iFreq ++ ) {
1030 sprintf( buf, "PSD(f%d)", iFreq + 1 );
1031 fprintf( fp, " %-21s", buf );
1032 }
1033 fprintf( fp, "\n" );
1034
1035 /* loop over timestamps: dump all PSDs over frequencies into one line */
1036 UINT4 numTS = thisPSDVect->length;
1037 UINT4 iTS;
1038 for ( iTS = 0; iTS < numTS; iTS++ ) {
1039 REAL8FrequencySeries *thisPSD = &thisPSDVect->data[iTS];
1040
1041 /* first output timestamp GPS time for this line */
1042 REAL8 tGPS = XLALGPSGetREAL8( &thisPSD->epoch );
1043 fprintf( fp, "%-19.18g", tGPS );
1044
1045 /* some internal consistency/paranoia checks */
1046 if ( ( f0 != thisPSD->f0 ) || ( dFreq != thisPSD->deltaF ) || ( numFreqs != thisPSD->data->length ) ) {
1047 XLALPrintError( "%s: %d-th timestamp %f: inconsistent PSDVector: f0 = %g : %g, dFreq = %g : %g, numFreqs = %d : %d \n",
1048 __func__, iTS, tGPS, f0, thisPSD->f0, dFreq, thisPSD->deltaF, numFreqs, thisPSD->data->length );
1049 XLALFree( fname );
1050 fclose( fp );
1052 }
1053
1054 /* loop over all frequencies and dump PSD-value */
1055 for ( iFreq = 0; iFreq < numFreqs; iFreq ++ ) {
1056 fprintf( fp, " %-21.16g", thisPSD->data->data[iFreq] );
1057 }
1058
1059 fprintf( fp, "\n" );
1060
1061 } /* for iTS < numTS */
1062
1063 fclose( fp );
1064
1065 } /* for X < numIFOs */
1066
1067 XLALFree( fname );
1068
1069 return XLAL_SUCCESS;
1070
1071} /* XLALDumpMultiPSDVector() */
1072
1073
1074/**
1075 * Write a PSD vector as an ASCII table to an open output file pointer.
1076 *
1077 * Adds a column header string, but version or commandline info
1078 * needs to be printed by the caller.
1079 *
1080 * Standard and (optional) columns: FreqBinStart (FreqBinEnd) PSD (normSFTpower)
1081 */
1082int
1084 FILE *fpOut, /**< output file pointer */
1085 REAL8Vector *PSDVect, /**< required: PSD vector */
1086 REAL8Vector *normSFTVect, /**< optional: normSFT vector (can be NULL if outputNormSFT==False) */
1087 BOOLEAN outputNormSFT, /**< output a column for normSFTVect? */
1088 BOOLEAN outFreqBinEnd, /**< output a column for the end frequency of each bin? */
1089 INT4 PSDmthopBins, /**< math operation for binning of the PSD */
1090 INT4 nSFTmthopBins, /**< math operation for binning of the normSFT */
1091 INT4 binSize, /**< output bin size (in number of input bins, 1 to keep input binning) */
1092 INT4 binStep, /**< output bin step (in number of input bins, 1 to keep input binning) */
1093 REAL8 Freq0, /**< starting frequency of inputs */
1094 REAL8 dFreq /**< frequency step of inputs */
1095)
1096{
1097
1098 /* input sanity checks */
1099 XLAL_CHECK( ( PSDVect != NULL ) && ( PSDVect->data != NULL ) && ( PSDVect->length > 0 ), XLAL_EINVAL, "PSDVect must be allocated and not zero length." );
1100 if ( outputNormSFT ) {
1101 XLAL_CHECK( ( normSFTVect != NULL ) && ( normSFTVect->data != NULL ), XLAL_EINVAL, "If requesting outputNormSFT, normSFTVect must be allocated" );
1102 XLAL_CHECK( normSFTVect->length == PSDVect->length, XLAL_EINVAL, "Lengths of PSDVect and normSFTVect do not match (%d!=%d)", PSDVect->length, normSFTVect->length );
1103 }
1104 XLAL_CHECK( binSize > 0, XLAL_EDOM, "output bin size must be >0" );
1105 XLAL_CHECK( binStep > 0, XLAL_EDOM, "output bin step must be >0" );
1106 XLAL_CHECK( Freq0 >= 0., XLAL_EDOM, "starting frequency must be nonnegative" );
1107 XLAL_CHECK( dFreq >= 0., XLAL_EDOM, "frequency step must be nonnegative" );
1108
1109 /* work out total number of bins */
1110 UINT4 numBins = ( UINT4 )floor( ( PSDVect->length - binSize ) / binStep ) + 1;
1111
1112 /* write column headings */
1113 fprintf( fpOut, "%%%% columns:\n%%%% FreqBinStart" );
1114 if ( outFreqBinEnd ) {
1115 fprintf( fpOut, " FreqBinEnd" );
1116 }
1117 fprintf( fpOut, " PSD" );
1118 if ( outputNormSFT ) {
1119 fprintf( fpOut, " normSFTpower" );
1120 }
1121 fprintf( fpOut, "\n" );
1122
1123 for ( UINT4 k = 0; k < numBins; ++k ) {
1124 UINT4 b = k * binStep;
1125
1126 REAL8 f0 = Freq0 + b * dFreq;
1127 REAL8 f1 = f0 + binStep * dFreq;
1128 fprintf( fpOut, "%f", f0 );
1129 if ( outFreqBinEnd ) {
1130 fprintf( fpOut, " %f", f1 );
1131 }
1132
1133 REAL8 psd = XLALMathOpOverArray( &( PSDVect->data[b] ), binSize, PSDmthopBins );
1134 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( psd ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for psd[k=%d]", k );
1135
1136 fprintf( fpOut, " %e", psd );
1137
1138 if ( outputNormSFT ) {
1139 REAL8 nsft = XLALMathOpOverArray( &( normSFTVect->data[b] ), binSize, nSFTmthopBins );
1140 XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( nsft ), XLAL_EFUNC, "XLALMathOpOverArray() returned NAN for nsft[k=%d]", k );
1141
1142 fprintf( fpOut, " %f", nsft );
1143 }
1144
1145 fprintf( fpOut, "\n" );
1146 } // k < numBins
1147
1148 return XLAL_SUCCESS;
1149
1150} /* XLALWritePSDtoFile() */
1151
1152/// @}
#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:963
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:1083
int XLALComputePSDfromSFTs(REAL8Vector **finalPSD, MultiSFTVector *inputSFTs, const UINT4 blocksRngMed, const MathOpType PSDmthopSFTs, const MathOpType PSDmthopIFOs, const BOOLEAN PSDnormByTotalNumSFTs, const REAL8 FreqMin, const REAL8 FreqBand)
Compute the PSD (power spectral density) over a MultiSFTVector.
Definition: PSDutils.c:921
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:41
REAL8 XLALMathOpOverREAL8Vector(const REAL8Vector *data, const MathOpType optype)
Compute various types of "math operations" over the entries of a REAL8Vector.
Definition: PSDutils.c:621
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
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:493
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 PSDnormByTotalNumSFTs, 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:703
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:653
@ 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
int int XLALPrintWarning(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_CHECK_REAL8(assertion,...)
#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