LALPulsar  6.1.0.1-fe68b98
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" },
54  { MATH_OP_ARITHMETIC_SUM, "0" },
55  { MATH_OP_ARITHMETIC_MEAN, "1" },
57  { MATH_OP_HARMONIC_SUM, "3" },
58  { MATH_OP_HARMONIC_MEAN, "4" },
59  { MATH_OP_POWERMINUS2_SUM, "5" },
60  { MATH_OP_POWERMINUS2_MEAN, "6" },
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  */
73 void
74 XLALDestroyPSDVector( 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  */
101 void
102 XLALDestroyMultiPSDVector( 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 ///
125 XLALCreateMultiNoiseWeights( 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 ///
143 XLALCopyMultiNoiseWeights( 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 */
171 void
173 {
174  if ( weights == NULL ) {
175  return;
176  }
177 
178  for ( UINT4 k = 0; k < weights->length; k++ ) {
179  XLALDestroyREAL8Vector( weights->data[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  */
194 int
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  */
375 XLALComputeSegmentDataQ( 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  */
491 REAL8 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  */
676 int
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  */
893 int
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  */
935 int
936 XLALDumpMultiPSDVector( 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 );
1024  XLAL_ERROR( XLAL_EDOM );
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  */
1055 int
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 * XLALCreateREAL8FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
REAL8FrequencySeries * XLALResizeREAL8FrequencySeries(REAL8FrequencySeries *series, int first, size_t length)
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 * XLALCalloc(size_t m, size_t n)
void * XLALMalloc(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)
UINT4Vector * XLALCreateUINT4Vector(UINT4 length)
REAL8Vector * XLALCreateREAL8Vector(UINT4 length)
void XLALDestroyREAL8Vector(REAL8Vector *vector)
#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)
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