LALPulsar  6.1.0.1-89842e6
MetricUtils.c
Go to the documentation of this file.
1 //
2 // Copyright (C) 2011--2015 Reinhard Prix, Karl Wette
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with with program; see the file COPYING. If not, write to the
16 // Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 // MA 02110-1301 USA
18 //
19 
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <math.h>
23 
24 #include <gsl/gsl_math.h>
25 #include <gsl/gsl_blas.h>
26 #include <gsl/gsl_linalg.h>
27 
28 #include <lal/MetricUtils.h>
29 #include <lal/XLALError.h>
30 #include <lal/LogPrintf.h>
31 #include <lal/Factorial.h>
32 
33 #include <lal/GSLHelpers.h>
34 
35 // Shortcuts for integer powers
36 #define POW2(a) ( (a) * (a) )
37 #define POW3(a) ( (a) * (a) * (a) )
38 #define POW4(a) ( (a) * (a) * (a) * (a) )
39 #define POW5(a) ( (a) * (a) * (a) * (a) * (a) )
40 
41 /// \addtogroup MetricUtils_h
42 /// @{
43 
44 ///
45 /// Flexible comparison function between two metrics \f$ g^1_{ij} \f$ and \f$ g^2_{ij} \f$ .
46 ///
47 /// Returns maximal relative deviation \f$ \max_{i,j} \delta g_{ij} \f$ , measured in terms of
48 /// diagonal entries, i.e. \f$ \delta g_{ij} = ( g^1_{ij} - g^2_{ij} ) / \sqrt{ g^1_{ii} g^1_{jj} } \f$ .
49 /// This should always be well-defined, as we deal with positive-definite square matrices.
50 ///
51 REAL8
53  const gsl_matrix *g1_ij, ///< [in] Metric to compare \f$ g^1_{ij} \f$ .
54  const gsl_matrix *g2_ij ///< [in] Metric to compare \f$ g^2_{ij} \f$ .
55 )
56 {
57 
58  // Check input
59  XLAL_CHECK_REAL8( g1_ij != NULL, XLAL_EFAULT );
60  XLAL_CHECK_REAL8( g2_ij != NULL, XLAL_EFAULT );
61  XLAL_CHECK_REAL8( g1_ij->size1 == g1_ij->size2, XLAL_ESIZE );
62  XLAL_CHECK_REAL8( g2_ij->size1 == g2_ij->size2, XLAL_ESIZE );
63  XLAL_CHECK_REAL8( g1_ij->size1 == g2_ij->size1, XLAL_ESIZE );
64 
65  if ( lalDebugLevel & LALINFOBIT ) {
66  fprintf( stderr, "%s(): comparing this metric:", __func__ );
67  XLALfprintfGSLmatrix( stderr, "%.15e", g1_ij );
68  fprintf( stderr, "%s(): to this metric:", __func__ );
69  XLALfprintfGSLmatrix( stderr, "%.15e", g2_ij );
70  }
71 
72  REAL8 errmax = 0;
73  UINT4 dim = g1_ij->size1;
74  for ( UINT4 i = 0; i < dim; i ++ ) {
75  for ( UINT4 j = 0; j < dim; j ++ ) {
76  REAL8 norm = sqrt( gsl_matrix_get( g1_ij, i, i ) * gsl_matrix_get( g1_ij, j, j ) );
77  REAL8 e1 = gsl_matrix_get( g1_ij, i, j ) / norm;
78  REAL8 e2 = gsl_matrix_get( g2_ij, i, j ) / norm;
79  REAL8 base;
80  if ( e2 == 0 ) {
81  base = 1;
82  } else {
83  base = e2;
84  }
85  REAL8 reldiff = fabs( e1 - e2 ) / base;
86 
87  errmax = fmax( errmax, reldiff );
88  } // for j < dim
89  } // for i < dim
90 
91  return errmax;
92 
93 } // XLALCompareMetrics()
94 
95 ///
96 /// Compute the determinant of a metric \f$ g_{ij} \f$ .
97 ///
99  const gsl_matrix *g_ij ///< [in] Parameter-space metric \f$ g_{ij} \f$
100 )
101 {
102 
103  // Check input
104  XLAL_CHECK_REAL8( g_ij != NULL, XLAL_EFAULT );
105 
106  const size_t n = g_ij->size1;
107 
108  // Allocate memory
109  gsl_matrix *GAMAT_REAL8( LU_decomp, n, n );
110  gsl_permutation *GAPERM_REAL8( LU_perm, n );
111 
112  // Compute LU decomposition
113  gsl_matrix_memcpy( LU_decomp, g_ij );
114  int LU_sign = 0;
115  XLAL_CHECK_REAL8( gsl_linalg_LU_decomp( LU_decomp, LU_perm, &LU_sign ) == 0, XLAL_EFAILED, "'g_ij' cannot be LU-decomposed" );
116 
117  // Compute determinant
118  const double determinant = gsl_linalg_LU_det( LU_decomp, LU_sign );
119 
120  // Cleanup
121  GFMAT( LU_decomp );
122  GFPERM( LU_perm );
123 
124  return determinant;
125 
126 }
127 
128 ///
129 /// Compute the extent of the bounding box of the mismatch ellipse of a metric \f$ g_{ij} \f$ .
130 ///
132  const gsl_matrix *g_ij, ///< [in] Parameter-space metric \f$ g_{ij} \f$
133  const double max_mismatch ///< [in] Maximum prescribed mismatch
134 )
135 {
136 
137  // Check input
138  XLAL_CHECK_NULL( g_ij != NULL, XLAL_EFAULT );
139  XLAL_CHECK_NULL( g_ij->size1 == g_ij->size2, XLAL_ESIZE );
140 
141  const size_t n = g_ij->size1;
142 
143  // Allocate memory
144  gsl_matrix *GAMAT_NULL( LU_decomp, n, n );
145  gsl_permutation *GAPERM_NULL( LU_perm, n );
146  gsl_matrix *GAMAT_NULL( diag_norm, n, n );
147  gsl_matrix *GAMAT_NULL( inverse, n, n );
148  gsl_vector *GAVEC_NULL( bounding_box, n );
149 
150  // Diagonally normalize metric
151  XLAL_CHECK_NULL( XLALDiagNormalizeMetric( &LU_decomp, &diag_norm, g_ij ) == XLAL_SUCCESS, XLAL_EFUNC );
152 
153  // Compute metric inverse
154  int LU_sign = 0;
155  XLAL_CHECK_NULL( gsl_linalg_LU_decomp( LU_decomp, LU_perm, &LU_sign ) == 0, XLAL_EFAILED, "'g_ij' cannot be LU-decomposed" );
156  XLAL_CHECK_NULL( gsl_linalg_LU_invert( LU_decomp, LU_perm, inverse ) == 0, XLAL_EFAILED, "'g_ij' cannot be inverted" );
157 
158  // Compute bounding box, and invert diagonal normalization
159  for ( size_t i = 0; i < n; ++i ) {
160  const double diag_norm_i = gsl_matrix_get( diag_norm, i, i );
161  const double bounding_box_i = 2.0 * sqrt( max_mismatch * gsl_matrix_get( inverse, i, i ) ) * diag_norm_i;
162  gsl_vector_set( bounding_box, i, bounding_box_i );
163  }
164 
165  // Cleanup
166  GFMAT( LU_decomp, diag_norm, inverse );
167  GFPERM( LU_perm );
168 
169  return bounding_box;
170 
171 } // XLALMetricEllipseBoundingBox()
172 
173 ///
174 /// Calculate the projected metric onto the subspace orthogonal to coordinate-axis \c c, namely
175 /// \f$ g^{\prime}_{ij} = g_{ij} - ( g_{ic} g_{jc} / g_{cc} ) \f$ , where \f$ c \f$ is the index of
176 /// the projected coordinate.
177 ///
178 /// \note \c *p_gpr_ij will be allocated if \c NULL. \c *p_gpr_ij and \c g_ij may point to the same
179 /// matrix.
180 ///
181 int
183  gsl_matrix **p_gpr_ij, ///< [in,out] Pointer to projected matrix \f$ g^{\prime}_{ij} \f$
184  const gsl_matrix *g_ij, ///< [in] Matrix to project \f$ g_{ij} \f$
185  const UINT4 c ///< [in] Index of projected coordinate
186 )
187 {
188 
189  // Check input
190  XLAL_CHECK( g_ij != NULL, XLAL_EFAULT );
191  XLAL_CHECK( g_ij->size1 == g_ij->size2, XLAL_ESIZE );
192  XLAL_CHECK( p_gpr_ij != NULL, XLAL_EFAULT );
193  if ( *p_gpr_ij != NULL ) {
194  XLAL_CHECK( ( *p_gpr_ij )->size1 == ( *p_gpr_ij )->size2, XLAL_ESIZE );
195  XLAL_CHECK( ( *p_gpr_ij )->size1 == g_ij->size1, XLAL_ESIZE );
196  } else {
197  GAMAT( *p_gpr_ij, g_ij->size1, g_ij->size2 );
198  }
199  XLAL_CHECK( c < g_ij->size1, XLAL_EINVAL );
200 
201  // Allocate temporary matrix
202  gsl_matrix *GAMAT( ret_ij, g_ij->size1, g_ij->size2 );
203 
204  // Compute projected matrix
205  for ( UINT4 i = 0; i < g_ij->size1; i++ ) {
206  for ( UINT4 j = 0; j < g_ij->size2; j++ ) {
207  if ( i == c || j == c ) {
208  gsl_matrix_set( ret_ij, i, j, 0.0 );
209  } else {
210  double proj = gsl_matrix_get( g_ij, i, j ) - ( gsl_matrix_get( g_ij, i, c ) * gsl_matrix_get( g_ij, j, c ) / gsl_matrix_get( g_ij, c, c ) );
211  gsl_matrix_set( ret_ij, i, j, proj );
212  }
213  } /* for j < dim2 */
214 
215  } /* for i < dim1 */
216  gsl_matrix_memcpy( *p_gpr_ij, ret_ij );
217 
218  // Cleanup
219  GFMAT( ret_ij );
220 
221  return XLAL_SUCCESS;
222 
223 } // XLALProjectMetric()
224 
225 ///
226 /// Decompose a metric \f$ \mathbf{G} \f$ as \f$ \mathbf{G} = \mathbf{L} \mathbf{D}
227 /// \mathbf{L}^{\mathrm{T}} \f$ , where \f$ \mathbf{L} \f$ is a lower-triangular matrix
228 /// with unit diagonal, and \f$ \mathbf{D} \f$ is a diagonal matrix. This decomposition
229 /// may be useful if the metric cannot yet be guaranteed to be positive definite.
230 ///
232  gsl_matrix **p_cholesky, ///< [in,out] Pointer to decomposition; stores \f$ \mathbf{L} \f$ in lower triangular part \f$ \mathbf{D} \f$ on diagonal
233  const gsl_matrix *g_ij ///< [in] Matrix to decompose \f$ \mathbf{G} \f$
234 )
235 {
236 
237 
238  // Check input
239  XLAL_CHECK( g_ij != NULL, XLAL_EFAULT );
240  XLAL_CHECK( g_ij->size1 == g_ij->size2, XLAL_ESIZE );
241  XLAL_CHECK( p_cholesky != NULL, XLAL_EFAULT );
242  if ( *p_cholesky != NULL ) {
243  XLAL_CHECK( ( *p_cholesky )->size1 == g_ij->size1, XLAL_ESIZE );
244  XLAL_CHECK( ( *p_cholesky )->size2 == g_ij->size2, XLAL_ESIZE );
245  } else {
246  GAMAT( *p_cholesky, g_ij->size1, g_ij->size2 );
247  }
248 
249  // Straightforward implementation of Cholesky–Banachiewicz algorithm
250  gsl_matrix_set_zero( *p_cholesky );
251 #define A(i,j) *gsl_matrix_const_ptr( g_ij, i, j )
252 #define D(j) *gsl_matrix_ptr( *p_cholesky, j, j )
253 #define L(i,j) *gsl_matrix_ptr( *p_cholesky, i, j )
254  for ( size_t i = 0; i < g_ij->size1; ++i ) {
255  for ( size_t j = 0; j <= i; ++j ) {
256  if ( i == j ) {
257  D( j ) = A( j, j );
258  for ( size_t k = 0; k < j; ++k ) {
259  D( j ) -= L( j, k ) * L( j, k ) * D( k );
260  }
261  } else {
262  L( i, j ) = A( i, j );
263  for ( size_t k = 0; k < j; ++k ) {
264  L( i, j ) -= L( i, k ) * L( j, k ) * D( k );
265  }
266  L( i, j ) /= D( j );
267  }
268  }
269  }
270 #undef A
271 #undef D
272 #undef L
273 
274  return XLAL_SUCCESS;
275 
276 }
277 
278 ///
279 /// Apply a transform \f$ \mathbf{A} = (a_{ij}) \f$ to a metric \f$ \mathbf{G} = (g_{ij}) \f$ such that
280 /// \f$ \mathbf{G} \rightarrow \mathbf{G}^{\prime} = \mathbf{A}^{\mathrm{T}} \mathbf{G} \mathbf{A}
281 /// \f$ , or equivalently \f$ g_{ij} \rightarrow g^{\prime}_{kl} = g_{ij} a_{ik} a_{jl} \f$ .
282 ///
283 /// \note \c *p_gpr_ij will be allocated if \c NULL. \c *p_gpr_ij and \c g_ij may point to the same
284 /// matrix.
285 ///
287  gsl_matrix **p_gpr_ij, ///< [in,out] Pointer to transformed matrix \f$ \mathbf{G}^{\prime} \f$
288  const gsl_matrix *transform, ///< [in] Transform to apply \f$ \mathbf{A} \f$
289  const gsl_matrix *g_ij ///< [in] Matrix to transform \f$ \mathbf{G} \f$
290 )
291 {
292 
293  // Check input
294  XLAL_CHECK( g_ij != NULL, XLAL_EFAULT );
295  XLAL_CHECK( transform != NULL, XLAL_EFAULT );
296  XLAL_CHECK( g_ij->size1 == g_ij->size2, XLAL_ESIZE );
297  XLAL_CHECK( g_ij->size2 == transform->size1, XLAL_ESIZE );
298  XLAL_CHECK( p_gpr_ij != NULL, XLAL_EFAULT );
299  if ( *p_gpr_ij != NULL ) {
300  XLAL_CHECK( ( *p_gpr_ij )->size1 == transform->size2, XLAL_ESIZE );
301  XLAL_CHECK( ( *p_gpr_ij )->size2 == transform->size2, XLAL_ESIZE );
302  } else {
303  GAMAT( *p_gpr_ij, transform->size2, transform->size2 );
304  }
305 
306  // Allocate temporary matrix
307  gsl_matrix *tmp = gsl_matrix_alloc( g_ij->size1, transform->size2 );
308  XLAL_CHECK( tmp != NULL, XLAL_ENOMEM );
309 
310  // Perform transform
311  gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1.0, g_ij, transform, 0.0, tmp );
312  gsl_blas_dgemm( CblasTrans, CblasNoTrans, 1.0, transform, tmp, 0.0, *p_gpr_ij );
313 
314  // Ensure transformed g_ij is exactly symmetric
315  for ( size_t i = 0; i < ( *p_gpr_ij )->size1; ++i ) {
316  for ( size_t j = i + 1; j < ( *p_gpr_ij )->size2; ++j ) {
317  const double gij = gsl_matrix_get( *p_gpr_ij, i, j );
318  const double gji = gsl_matrix_get( *p_gpr_ij, j, i );
319  const double g = 0.5 * ( gij + gji );
320  gsl_matrix_set( *p_gpr_ij, i, j, g );
321  gsl_matrix_set( *p_gpr_ij, j, i, g );
322  }
323  }
324 
325  // Cleanup
326  gsl_matrix_free( tmp );
327 
328  return XLAL_SUCCESS;
329 
330 } // XLALTransformMetric()
331 
332 ///
333 /// Apply the inverse of a transform \f$ \mathbf{A}^{-1} = \mathbf{B} = (b_{ij}) \f$ to a metric
334 /// \f$ \mathbf{G} = (g_{ij}) \f$ such that \f$ \mathbf{G} \rightarrow \mathbf{G}^{\prime} =
335 /// \mathbf{B}^{\mathrm{T}} \mathbf{G} \mathbf{B} \f$ , or equivalently \f$ g_{ij} \rightarrow
336 /// g^{\prime}_{kl} = g_{ij} b_{ik} b_{jl} \f$ .
337 ///
338 /// \note \c *p_gpr_ij will be allocated if \c NULL. \c *p_gpr_ij and \c g_ij may point to the same
339 /// matrix.
340 ///
342  gsl_matrix **p_gpr_ij, ///< [in,out] Pointer to transformed matrix \f$ \mathbf{G}^{\prime} \f$
343  const gsl_matrix *transform, ///< [in] Transform \f$ \mathbf{A} \f$ , the inverse of which to apply
344  const gsl_matrix *g_ij ///< [in] Matrix to transform \f$ \mathbf{G} \f$
345 )
346 {
347 
348  // Check input
349  XLAL_CHECK( g_ij != NULL, XLAL_EFAULT );
350  XLAL_CHECK( transform != NULL, XLAL_EFAULT );
351  XLAL_CHECK( g_ij->size1 == g_ij->size2, XLAL_ESIZE );
352  XLAL_CHECK( g_ij->size2 == transform->size1, XLAL_ESIZE );
353  XLAL_CHECK( transform->size1 == transform->size2, XLAL_ESIZE );
354  XLAL_CHECK( p_gpr_ij != NULL, XLAL_EFAULT );
355 
356  // Allocate memory
357  gsl_matrix *GAMAT( LU_decomp, transform->size1, transform->size2 );
358  gsl_permutation *GAPERM( LU_perm, transform->size1 );
359  gsl_matrix *GAMAT( inverse, transform->size1, transform->size2 );
360 
361  // Compute inverse of transform
362  int LU_sign = 0;
363  gsl_matrix_memcpy( LU_decomp, transform );
364  XLAL_CHECK( gsl_linalg_LU_decomp( LU_decomp, LU_perm, &LU_sign ) == 0, XLAL_EFAILED, "'transform' cannot be LU-decomposed" );
365  XLAL_CHECK( gsl_linalg_LU_invert( LU_decomp, LU_perm, inverse ) == 0, XLAL_EFAILED, "'transform' cannot be inverted" );
366 
367  // Apply transform
368  XLAL_CHECK( XLALTransformMetric( p_gpr_ij, inverse, g_ij ) == XLAL_SUCCESS, XLAL_EFUNC );
369 
370  // Cleanup
371  GFMAT( LU_decomp, inverse );
372  GFPERM( LU_perm );
373 
374  return XLAL_SUCCESS;
375 
376 } // XLALInverseTransformMetric()
377 
378 ///
379 /// Diagonally-normalize a metric \f$ g_{ij} \f$ . <i>Diagonally-normalize</i> means normalize
380 /// metric by its diagonal, namely apply the transformation \f$ g_{ij} \rightarrow g^{\prime}_{ij} =
381 /// g_{ij} / \sqrt{g_{ii} g_{jj}} \f$ , resulting in a lower condition number and unit diagonal
382 /// elements.
383 ///
384 /// If \c p_transform is non-NULL, return the diagonal-normalization transform in \c *p_transform.
385 /// If \c p_gpr_ij is non-NULL, apply the transform to the metric \c *p_gpr_ij.
386 ///
387 /// \note \c *p_gpr_ij and \c *p_transform will be allocated if \c NULL. \c *p_gpr_ij and \c g_ij
388 /// may point to the same matrix.
389 ///
390 int
392  gsl_matrix **p_gpr_ij, ///< [in,out,optional] Pointer to transformed matrix \f$ g^{\prime}_{ij} \f$
393  gsl_matrix **p_transform, ///< [in,out,optional] Pointer to diagonal-normalization transform
394  const gsl_matrix *g_ij ///< [in] Matrix to transform \f$ g_{ij} \f$
395 )
396 {
397 
398  // Check input
399  XLAL_CHECK( g_ij != NULL, XLAL_EFAULT );
400  XLAL_CHECK( g_ij->size1 == g_ij->size2, XLAL_ESIZE );
401  if ( p_transform != NULL ) {
402  if ( *p_transform != NULL ) {
403  XLAL_CHECK( ( *p_transform )->size1 == g_ij->size1, XLAL_ESIZE );
404  XLAL_CHECK( ( *p_transform )->size2 == g_ij->size2, XLAL_ESIZE );
405  } else {
406  GAMAT( *p_transform, g_ij->size1, g_ij->size2 );
407  }
408  }
409 
410  // Create diagonal normalization transform
411  gsl_matrix *GAMAT( transform, g_ij->size1, g_ij->size2 );
412  gsl_matrix_set_zero( transform );
413  for ( size_t i = 0; i < g_ij->size1; ++i ) {
414  const double gii = gsl_matrix_get( g_ij, i, i );
415  XLAL_CHECK( gii > 0, XLAL_EINVAL, "Diagonal normalization not defined for non-positive diagonal elements! g_ii(%zu,%zu) = %g\n", i, i, gii );
416  gsl_matrix_set( transform, i, i, 1.0 / sqrt( gii ) );
417  }
418 
419  // Apply transform
420  if ( p_gpr_ij != NULL ) {
421  XLAL_CHECK( XLALTransformMetric( p_gpr_ij, transform, g_ij ) == XLAL_SUCCESS, XLAL_EFUNC );
422  }
423 
424  // Return transform
425  if ( p_transform != NULL ) {
426  gsl_matrix_memcpy( *p_transform, transform );
427  }
428 
429  // Cleanup
430  GFMAT( transform );
431 
432  return XLAL_SUCCESS;
433 
434 } // XLALDiagNormalizeMetric()
435 
436 ///
437 /// Return a metric in <i>naturalized</i> coordinates.
438 /// Frequency coordinates of spindown order \f$ s \f$ are scaled by
439 /// \f[ \frac{2\pi}{(s+1)!} \left(\frac{\overline{\Delta T}}{2}\right)^{s+1} \f]
440 /// where \f$ \overline{\Delta T}\equiv\sum_{k}^{N} \Delta T_k \f$ is the average segment-length
441 /// over all \f$ N \f$ segments.
442 ///
443 /// Sky coordinates are scaled by Holgers' units, see Eq.(44) in PRD82,042002(2010),
444 /// without the equatorial rotation in alpha:
445 /// \f[ \frac{2\pi f R_{E} \cos(\delta_{D})}{c} \f]
446 /// where \f$ f \f$ is the frequency and
447 /// \f$ R_{E} \f$ the Earth radius, and \f$ \delta_{D} \f$ is the detectors latitude.
448 ///
449 /// If \c p_transform is non-NULL, return the naturalization transform in \c *p_transform.
450 /// If \c p_gpr_ij is non-NULL, apply the transform to the metric \c *p_gpr_ij.
451 ///
452 /// \note \c *p_gpr_ij and \c *p_transform will be allocated if \c NULL. \c *p_gpr_ij and \c g_ij
453 /// may point to the same matrix.
454 ///
455 int
457  gsl_matrix **p_gpr_ij, ///< [in,out,optional] Pointer to transformed matrix \f$ g^{\prime}_{ij} \f$
458  gsl_matrix **p_transform, ///< [in,out,optional] Pointer to naturalization transform
459  const gsl_matrix *g_ij, ///< [in] Matrix to transform \f$ g_{ij} \f$
460  const DopplerMetricParams *metricParams ///< [in] Input parameters used to calculate naturalization transform
461 )
462 {
463 
464  // Check input
465  XLAL_CHECK( g_ij != NULL, XLAL_EFAULT );
466  XLAL_CHECK( g_ij->size1 == g_ij->size2, XLAL_ESIZE );
467  if ( p_transform != NULL ) {
468  if ( *p_transform != NULL ) {
469  XLAL_CHECK( ( *p_transform )->size1 == g_ij->size1, XLAL_ESIZE );
470  XLAL_CHECK( ( *p_transform )->size2 == g_ij->size2, XLAL_ESIZE );
471  } else {
472  GAMAT( *p_transform, g_ij->size1, g_ij->size2 );
473  }
474  }
475  XLAL_CHECK( metricParams != NULL, XLAL_EFAULT );
476 
477  // Compute average segment length
478  XLAL_CHECK( XLALSegListIsInitialized( &( metricParams->segmentList ) ), XLAL_EINVAL, "Passed un-initialzied segment list 'metricParams->segmentList'\n" );
479  UINT4 Nseg = metricParams->segmentList.length;
480  REAL8 sumTseg = 0;
481  for ( UINT4 k = 0; k < Nseg; k ++ ) {
482  LIGOTimeGPS *startTime_k = &( metricParams->segmentList.segs[k].start );
483  LIGOTimeGPS *endTime_k = &( metricParams->segmentList.segs[k].end );
484  sumTseg += XLALGPSDiff( endTime_k, startTime_k );
485  }
486  REAL8 avgTseg = sumTseg / Nseg;
487 
488  // Compute naturalization transform
489  gsl_matrix *GAMAT( transform, g_ij->size1, g_ij->size2 );
490  gsl_matrix_set_zero( transform );
491  for ( size_t i = 0; i < g_ij->size1; ++i ) {
492  const DopplerCoordinateID coordID = metricParams->coordSys.coordIDs[i];
493  const double Freq = metricParams->signalParams.Doppler.fkdot[0];
494  const double T = avgTseg;
495  double scale = 0;
496  switch ( coordID ) {
497  case DOPPLERCOORD_NONE:
498  scale = 1;
499  break;
500 
501  case DOPPLERCOORD_FREQ:
502  case DOPPLERCOORD_GC_NU0:
503  scale = LAL_TWOPI * LAL_FACT_INV[1] * ( 0.5 * T );
504  break;
505 
506  case DOPPLERCOORD_F1DOT:
507  case DOPPLERCOORD_GC_NU1:
508  scale = LAL_TWOPI * LAL_FACT_INV[2] * POW2( 0.5 * T );
509  break;
510 
511  case DOPPLERCOORD_F2DOT:
512  case DOPPLERCOORD_GC_NU2:
513  scale = LAL_TWOPI * LAL_FACT_INV[3] * POW3( 0.5 * T );
514  break;
515 
516  case DOPPLERCOORD_F3DOT:
517  case DOPPLERCOORD_GC_NU3:
518  scale = LAL_TWOPI * LAL_FACT_INV[4] * POW4( 0.5 * T );
519  break;
520 
521  case DOPPLERCOORD_ALPHA:
522  case DOPPLERCOORD_DELTA:
536  case DOPPLERCOORD_N3OY_ECL: {
537  const REAL8 rEarth_c = ( LAL_REARTH_SI / LAL_C_SI );
538  REAL8 cosdD = cos( metricParams->multiIFO.sites[0].frDetector.vertexLatitudeRadians );
539  scale = LAL_TWOPI * Freq * rEarth_c * cosdD;
540  }
541  break;
542 
543  default:
544  scale = 1;
545  break;
546  } // switch(coordID)
547  gsl_matrix_set( transform, i, i, 1.0 / scale );
548  }
549 
550  // Apply transform
551  if ( p_gpr_ij != NULL ) {
552  XLAL_CHECK( XLALTransformMetric( p_gpr_ij, transform, g_ij ) == XLAL_SUCCESS, XLAL_EFUNC );
553  }
554 
555  // Return transform
556  if ( p_transform != NULL ) {
557  gsl_matrix_memcpy( *p_transform, transform );
558  }
559 
560  // Cleanup
561  GFMAT( transform );
562 
563  return XLAL_SUCCESS;
564 
565 } // XLALNaturalizeMetric()
566 
567 ///
568 /// Compute the transform which changes the metric reference time \f$ \tau_0 \rightarrow \tau_1 =
569 /// \tau_0 + \Delta\tau \f$ .
570 ///
571 /// If \c p_transform is non-NULL, return the reference-time transform in \c *p_transform.
572 /// If \c p_gpr_ij is non-NULL, apply the transform to the metric \c *p_gpr_ij.
573 ///
574 /// \note \c *p_gpr_ij and \c *p_transform will be allocated if \c NULL. \c *p_gpr_ij and \c g_ij
575 /// may point to the same matrix.
576 ///
577 int
579  gsl_matrix **p_gpr_ij, ///< [in,out,optional] Pointer to transformed matrix \f$ g^{\prime}_{ij} \f$
580  gsl_matrix **p_transform, ///< [in,out,optional] Pointer to reference time transform
581  const gsl_matrix *g_ij, ///< [in] Matrix to transform \f$ g_{ij} \f$
582  const DopplerCoordinateSystem *coordSys, ///< [in] Coordinate system of metric
583  const double Dtau ///< [in] Difference between new and old reference times \f$ \Delta\tau \f$
584 )
585 {
586 
587  // Check input
588  XLAL_CHECK( g_ij != NULL, XLAL_EFAULT );
589  XLAL_CHECK( g_ij->size1 == g_ij->size2, XLAL_ESIZE );
590  if ( p_transform != NULL ) {
591  if ( *p_transform != NULL ) {
592  XLAL_CHECK( ( *p_transform )->size1 == g_ij->size1, XLAL_ESIZE );
593  XLAL_CHECK( ( *p_transform )->size2 == g_ij->size2, XLAL_ESIZE );
594  } else {
595  GAMAT( *p_transform, g_ij->size1, g_ij->size2 );
596  }
597  }
598  XLAL_CHECK( coordSys != NULL, XLAL_EFAULT );
599  XLAL_CHECK( coordSys->dim == g_ij->size1, XLAL_ESIZE );
600  for ( size_t i = 0; i < coordSys->dim; ++i ) {
601  switch ( coordSys->coordIDs[i] ) {
602  case DOPPLERCOORD_GC_NU0:
603  case DOPPLERCOORD_GC_NU1:
604  case DOPPLERCOORD_GC_NU2:
605  case DOPPLERCOORD_GC_NU3:
606  XLAL_ERROR( XLAL_EINVAL, "GCT coordinates are not supported" );
607  default:
608  break;
609  }
610  }
611 
612  // Compute transformation matrix for frequency-spindown coordinates
613  // from Prix: "Frequency metric for CW searches" (2014-08-17), p. 4
614  gsl_matrix *GAMAT( transform, g_ij->size1, g_ij->size2 );
615  gsl_matrix_set_identity( transform );
616  if ( Dtau != 0.0 ) {
617  for ( size_t i = 0; i < coordSys->dim; ++i ) {
618  const DopplerCoordinateID icoord = coordSys->coordIDs[i];
619  if ( DOPPLERCOORD_FREQ <= icoord && icoord <= DOPPLERCOORD_LASTFDOT ) {
620  const size_t ispinorder = icoord - DOPPLERCOORD_FREQ;
621 
622  for ( size_t j = i + 1; j < coordSys->dim; ++j ) {
623  const DopplerCoordinateID jcoord = coordSys->coordIDs[j];
624  if ( DOPPLERCOORD_FREQ <= jcoord && jcoord <= DOPPLERCOORD_LASTFDOT ) {
625  const size_t jspinorder = jcoord - DOPPLERCOORD_FREQ;
626 
627  if ( jspinorder > ispinorder ) {
628  const double tij = LAL_FACT_INV[jspinorder - ispinorder] * pow( -1 * Dtau, jspinorder - ispinorder );
629  gsl_matrix_set( transform, i, j, tij );
630  }
631 
632  }
633  }
634 
635  }
636  }
637  }
638 
639  // Apply transform
640  if ( p_gpr_ij != NULL ) {
641  XLAL_CHECK( XLALTransformMetric( p_gpr_ij, transform, g_ij ) == XLAL_SUCCESS, XLAL_EFUNC );
642  }
643 
644  // Return transform
645  if ( p_transform != NULL ) {
646  gsl_matrix_memcpy( *p_transform, transform );
647  }
648 
649  // Cleanup
650  GFMAT( transform );
651 
652  return XLAL_SUCCESS;
653 
654 } // XLALPhaseMetricRefTimeTransform()
655 
656 /// @}
#define __func__
log an I/O error, i.e.
int j
int k
#define c
#define POW4(a)
Definition: MetricUtils.c:38
#define D(j)
#define L(i, j)
#define POW3(a)
Definition: MetricUtils.c:37
#define A(i, j)
#define POW2(a)
Definition: MetricUtils.c:36
#define fprintf
const double scale
multiplicative scaling factor of the coordinate
#define rEarth_c
static const REAL8 LAL_FACT_INV[]
#define LAL_TWOPI
double REAL8
uint32_t UINT4
#define lalDebugLevel
LALINFOBIT
void void int XLALfprintfGSLmatrix(FILE *fp, const char *fmt, const gsl_matrix *gij) _LAL_GCC_VPRINTF_FORMAT_(2)
int XLALChangeMetricReferenceTime(gsl_matrix **p_gpr_ij, gsl_matrix **p_transform, const gsl_matrix *g_ij, const DopplerCoordinateSystem *coordSys, const double Dtau)
Compute the transform which changes the metric reference time .
Definition: MetricUtils.c:578
int XLALProjectMetric(gsl_matrix **p_gpr_ij, const gsl_matrix *g_ij, const UINT4 c)
Calculate the projected metric onto the subspace orthogonal to coordinate-axis c, namely ,...
Definition: MetricUtils.c:182
REAL8 XLALCompareMetrics(const gsl_matrix *g1_ij, const gsl_matrix *g2_ij)
Flexible comparison function between two metrics and .
Definition: MetricUtils.c:52
int XLALCholeskyLDLTDecompMetric(gsl_matrix **p_cholesky, const gsl_matrix *g_ij)
Decompose a metric as , where is a lower-triangular matrix with unit diagonal, and is a diagonal ...
Definition: MetricUtils.c:231
int XLALInverseTransformMetric(gsl_matrix **p_gpr_ij, const gsl_matrix *transform, const gsl_matrix *g_ij)
Apply the inverse of a transform to a metric such that , or equivalently .
Definition: MetricUtils.c:341
int XLALDiagNormalizeMetric(gsl_matrix **p_gpr_ij, gsl_matrix **p_transform, const gsl_matrix *g_ij)
Diagonally-normalize a metric .
Definition: MetricUtils.c:391
double XLALMetricDeterminant(const gsl_matrix *g_ij)
Compute the determinant of a metric .
Definition: MetricUtils.c:98
int XLALTransformMetric(gsl_matrix **p_gpr_ij, const gsl_matrix *transform, const gsl_matrix *g_ij)
Apply a transform to a metric such that , or equivalently .
Definition: MetricUtils.c:286
gsl_vector * XLALMetricEllipseBoundingBox(const gsl_matrix *g_ij, const double max_mismatch)
Compute the extent of the bounding box of the mismatch ellipse of a metric .
Definition: MetricUtils.c:131
int XLALNaturalizeMetric(gsl_matrix **p_gpr_ij, gsl_matrix **p_transform, const gsl_matrix *g_ij, const DopplerMetricParams *metricParams)
Return a metric in naturalized coordinates.
Definition: MetricUtils.c:456
int XLALSegListIsInitialized(const LALSegList *seglist)
DopplerCoordinateID
enum listing symbolic 'names' for all Doppler Coordinates supported by the metric codes in FstatMetri...
@ DOPPLERCOORD_N3SX_EQU
X spin-component of unconstrained super-sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_N2X_ECL
X component of contrained sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_N3X_ECL
X component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_N3X_EQU
X component of unconstrained super-sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_N3OY_ECL
Y orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_N3Z_EQU
Z component of unconstrained super-sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_LASTFDOT
@ DOPPLERCOORD_F2DOT
Second spindown [Units: Hz/s^2].
@ DOPPLERCOORD_N2X_EQU
X component of contrained sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_DELTA
Declination [Units: radians].
@ DOPPLERCOORD_N2Y_ECL
Y component of contrained sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_GC_NU1
Global correlation first spindown [Units: Hz/s].
@ DOPPLERCOORD_GC_NU0
Global correlation frequency [Units: Hz].
@ DOPPLERCOORD_N3Y_EQU
Y component of unconstrained super-sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_F1DOT
First spindown [Units: Hz/s].
@ DOPPLERCOORD_N2Y_EQU
Y component of contrained sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_GC_NU2
Global correlation second spindown [Units: Hz/s^2].
@ DOPPLERCOORD_GC_NU3
Global correlation third spindown [Units: Hz/s^3].
@ DOPPLERCOORD_ALPHA
Right ascension [Units: radians].
@ DOPPLERCOORD_NONE
No Doppler component.
@ DOPPLERCOORD_N3Y_ECL
Y component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_F3DOT
Third spindown [Units: Hz/s^3].
@ DOPPLERCOORD_N3OX_ECL
X orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_N3Z_ECL
Z component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_N3SY_EQU
Y spin-component of unconstrained super-sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_FREQ
Frequency [Units: Hz].
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
#define XLAL_CHECK_REAL8(assertion,...)
#define XLAL_CHECK_NULL(assertion,...)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_ESIZE
XLAL_EINVAL
XLAL_EFAILED
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
T
n
type describing a Doppler coordinate system: lists the number of dimensions and the symbolic names of...
DopplerCoordinateID coordIDs[DOPPLERMETRIC_MAX_DIM]
coordinate 'names'
UINT4 dim
number of dimensions covered
meta-info specifying a Doppler-metric
MultiLALDetector multiIFO
detectors to compute metric for
PulsarParams signalParams
parameter-space point to compute metric for (doppler + amplitudes)
LALSegList segmentList
segment list: Nseg segments of the form (startGPS endGPS numSFTs)
DopplerCoordinateSystem coordSys
number of dimensions and coordinate-IDs of Doppler-metric
LALFrDetector frDetector
REAL8 vertexLatitudeRadians
LIGOTimeGPS end
LIGOTimeGPS start
UINT4 length
LALSeg * segs
LALDetector sites[PULSAR_MAX_DETECTORS]
array of site information
PulsarSpins fkdot
Intrinsic spins: [Freq, f1dot, f2dot, ...
PulsarDopplerParams Doppler
'Phase-evolution parameters': {skypos, fkdot, orbital params }