LALPulsar  6.1.0.1-c9a8ef6
UniversalDopplerMetric.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2017 Arunava Mukherjee
3  * Copyright (C) 2012--2015 Karl Wette
4  * Copyright (C) 2008, 2009 Reinhard Prix
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with with program; see the file COPYING. If not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301 USA
20  */
21 
22 /*---------- INCLUDES ----------*/
23 #include <math.h>
24 #ifdef _OPENMP
25 #include <omp.h>
26 #endif
27 
28 /* gsl includes */
29 #include <gsl/gsl_block.h>
30 #include <gsl/gsl_vector.h>
31 #include <gsl/gsl_matrix.h>
32 #include <gsl/gsl_linalg.h>
33 #include <gsl/gsl_blas.h>
34 #include <gsl/gsl_integration.h>
35 #include <gsl/gsl_eigen.h>
36 
37 #include <lal/GetEarthTimes.h>
38 #include <lal/ComputeFstat.h>
39 #include <lal/XLALGSL.h>
40 #include <lal/Factorial.h>
41 #include <lal/LogPrintf.h>
42 #include <lal/MetricUtils.h>
43 
44 #include <lal/FstatisticTools.h>
45 #include <lal/UniversalDopplerMetric.h>
46 #include <lal/MetricUtils.h>
47 
48 #include <lal/GSLHelpers.h>
49 
50 /**
51  * \author Reinhard Prix, Karl Wette
52  * \ingroup UniversalDopplerMetric_h
53  * \brief Function to compute the full F-statistic metric, including
54  * antenna-pattern functions from multi-detector, as derived in \cite Prix07 .
55  *
56  */
57 
58 /*---------- SCALING FACTORS ----------*/
59 /** shortcuts for integer powers */
60 #define POW2(a) ( (a) * (a) )
61 #define POW3(a) ( (a) * (a) * (a) )
62 #define POW4(a) ( (a) * (a) * (a) * (a) )
63 #define POW5(a) ( (a) * (a) * (a) * (a) * (a) )
64 
65 /* These constants define an internal scale used within CW_Phi_i().
66  * Distances are normalised by SCALE_R, and times are normalised by SCALE_T.
67  */
68 #define SCALE_R (LAL_AU_SI)
69 #define SCALE_T (LAL_YRSID_SI)
70 
71 /*---------- LOOKUP ARRAYS ----------*/
72 
73 /**
74  * Array of symbolic 'names' for various detector-motions
75  */
76 static const struct {
78  const char *const name;
79 } DetectorMotionNames[] = {
80 
81 #define DETMOTION_NAMES(orbit_type, spin_orbit_name, nospin_orbit_name) \
82  {DETMOTION_SPIN | orbit_type, "spin" spin_orbit_name}, \
83  {DETMOTION_SPINZ | orbit_type, "spinz" spin_orbit_name}, \
84  {DETMOTION_SPINXY | orbit_type, "spinxy" spin_orbit_name}, \
85  {orbit_type, nospin_orbit_name} /* this must be the last entry in the macro */
86 
87  DETMOTION_NAMES( DETMOTION_ORBIT, "+orbit", "orbit" ),
88  DETMOTION_NAMES( DETMOTION_PTOLEORBIT, "+ptoleorbit", "ptoleorbit" ),
89  DETMOTION_NAMES( 0, "", NULL ) /* last entry provides marker for end of list */
90 
91 #undef DETMOTION_NAMES
92 };
93 
94 /**
95  * Array of descriptor structs for each Doppler coordinate name
96  */
97 static const struct {
98  const char *const name; /**< coordinate name */
99  const double scale; /**< multiplicative scaling factor of the coordinate */
100  const char *const help; /**< help string explaining the coordinate's meaning and units */
102 
103  [DOPPLERCOORD_FREQ] = {"freq", SCALE_T, "Frequency [Units: Hz]."},
104  [DOPPLERCOORD_F1DOT] = {"f1dot", POW2( SCALE_T ), "First spindown [Units: Hz/s]."},
105  [DOPPLERCOORD_F2DOT] = {"f2dot", POW3( SCALE_T ), "Second spindown [Units: Hz/s^2]."},
106  [DOPPLERCOORD_F3DOT] = {"f3dot", POW4( SCALE_T ), "Third spindown [Units: Hz/s^3]."},
107  [DOPPLERCOORD_F4DOT] = {"f4dot", POW5( SCALE_T ), "Fourth spindown [Units: Hz/s^4]."},
108 
109  [DOPPLERCOORD_GC_NU0] = {"gc_nu0", SCALE_T, "Global correlation frequency [Units: Hz]."},
110  [DOPPLERCOORD_GC_NU1] = {"gc_nu1", POW2( SCALE_T ), "Global correlation first spindown [Units: Hz/s]."},
111  [DOPPLERCOORD_GC_NU2] = {"gc_nu2", POW3( SCALE_T ), "Global correlation second spindown [Units: Hz/s^2]."},
112  [DOPPLERCOORD_GC_NU3] = {"gc_nu3", POW4( SCALE_T ), "Global correlation third spindown [Units: Hz/s^3]."},
113  [DOPPLERCOORD_GC_NU4] = {"gc_nu4", POW5( SCALE_T ), "Global correlation fourth spindown [Units: Hz/s^4]."},
114 
115  [DOPPLERCOORD_ALPHA] = {"alpha", SCALE_R / LAL_C_SI, "Right ascension [Units: radians]."},
116  [DOPPLERCOORD_DELTA] = {"delta", SCALE_R / LAL_C_SI, "Declination [Units: radians]."},
117 
118  [DOPPLERCOORD_N2X_EQU] = {"n2x_equ", SCALE_R / LAL_C_SI, "X component of constrained sky position in equatorial coordinates [Units: none]."},
119  [DOPPLERCOORD_N2Y_EQU] = {"n2y_equ", SCALE_R / LAL_C_SI, "Y component of constrained sky position in equatorial coordinates [Units: none]."},
120 
121  [DOPPLERCOORD_N2X_ECL] = {"n2x_ecl", SCALE_R / LAL_C_SI, "X component of constrained sky position in ecliptic coordinates [Units: none]."},
122  [DOPPLERCOORD_N2Y_ECL] = {"n2y_ecl", SCALE_R / LAL_C_SI, "Y component of constrained sky position in ecliptic coordinates [Units: none]."},
123 
124  [DOPPLERCOORD_N3X_EQU] = {"n3x_equ", SCALE_R / LAL_C_SI, "X component of unconstrained super-sky position in equatorial coordinates [Units: none]."},
125  [DOPPLERCOORD_N3Y_EQU] = {"n3y_equ", SCALE_R / LAL_C_SI, "Y component of unconstrained super-sky position in equatorial coordinates [Units: none]."},
126  [DOPPLERCOORD_N3Z_EQU] = {"n3z_equ", SCALE_R / LAL_C_SI, "Z component of unconstrained super-sky position in equatorial coordinates [Units: none]."},
127 
128  [DOPPLERCOORD_N3X_ECL] = {"n3x_ecl", SCALE_R / LAL_C_SI, "X component of unconstrained super-sky position in ecliptic coordinates [Units: none]."},
129  [DOPPLERCOORD_N3Y_ECL] = {"n3y_ecl", SCALE_R / LAL_C_SI, "Y component of unconstrained super-sky position in ecliptic coordinates [Units: none]."},
130  [DOPPLERCOORD_N3Z_ECL] = {"n3z_ecl", SCALE_R / LAL_C_SI, "Z component of unconstrained super-sky position in ecliptic coordinates [Units: none]."},
131 
132  [DOPPLERCOORD_N3SX_EQU] = {"n3sx_equ", SCALE_R / LAL_C_SI, "X spin-component of unconstrained super-sky position in equatorial coordinates [Units: none]."},
133  [DOPPLERCOORD_N3SY_EQU] = {"n3sy_equ", SCALE_R / LAL_C_SI, "Y spin-component of unconstrained super-sky position in equatorial coordinates [Units: none]."},
134 
135  [DOPPLERCOORD_N3OX_ECL] = {"n3ox_ecl", SCALE_R / LAL_C_SI, "X orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none]."},
136  [DOPPLERCOORD_N3OY_ECL] = {"n3oy_ecl", SCALE_R / LAL_C_SI, "Y orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none]."},
137  [DOPPLERCOORD_N3OZ_ECL] = {"n3oz_ecl", SCALE_R / LAL_C_SI, "Z orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none]."},
138 
139  [DOPPLERCOORD_ASINI] = {"asini", 1, "Projected semimajor axis of binary orbit in small-eccentricy limit (ELL1 model) [Units: light seconds]."},
140  [DOPPLERCOORD_TASC] = {"tasc", 1, "Time of ascension (neutron star crosses line of nodes moving away from observer) for binary orbit (ELL1 model) [Units: GPS seconds]."},
141  [DOPPLERCOORD_PORB] = {"porb", 1, "Period of binary orbit (ELL1 model) [Units: s]."},
142  [DOPPLERCOORD_KAPPA] = {"kappa", 1, "Lagrange parameter 'kappa = ecc * cos(argp)', ('ecc' = eccentricity, 'argp' = argument of periapse) of binary orbit (ELL1 model) [Units: none]."},
143  [DOPPLERCOORD_ETA] = {"eta", 1, "Lagrange parameter 'eta = ecc * sin(argp) of binary orbit (ELL1 model) [Units: none]."},
144 
145  [DOPPLERCOORD_DASC] = {"dasc", 1, "Distance traversed on the arc of binary orbit (ELL1 model) 'dasc = 2 * pi * (ap/porb) * tasc' [Units: light second]."},
146  [DOPPLERCOORD_VP] = {"vp", 1, "Rescaled (by asini) differential-coordinate 'dvp = asini * dOMEGA', ('OMEGA' = 2 * pi/'porb') of binary orbit (ELL1 model) [Units: (light second)/(GPS second)]."},
147  [DOPPLERCOORD_KAPPAP] = {"kappap", 1, "Rescaled (by asini) differential-coordinate 'dkappap = asini * dkappa' [Units: light seconds]."},
148  [DOPPLERCOORD_ETAP] = {"etap", 1, "Rescaled (by asini) differential-coordinate 'detap = asini * deta' [Units: light seconds]."}
149 
150 };
151 
152 /*---------- DEFINES ----------*/
153 #define TRUE (1==1)
154 #define FALSE (0==1)
155 
156 /* get the coordinate ID for a given coordinate index */
157 #define GET_COORD_ID(coordSys, coord) ( (coord) >= 0 ? (coordSys)->coordIDs[(coord)] : DOPPLERCOORD_NONE )
158 
159 /* get the coordinate name and scale associated with a given coordinate index */
160 #define GET_COORD_NAME(coordSys, coord) ( (coord) >= 0 ? DopplerCoordinates[GET_COORD_ID(coordSys, coord)].name : "(none)" )
161 #define GET_COORD_SCALE(coordSys, coord) ( (coord) >= 0 ? DopplerCoordinates[GET_COORD_ID(coordSys, coord)].scale : 1.0 )
162 
163 /** copy 3 components of Euklidean vector */
164 #define COPY_VECT(dst,src) do { (dst)[0] = (src)[0]; (dst)[1] = (src)[1]; (dst)[2] = (src)[2]; } while(0)
165 
166 /** Simple Euklidean scalar product for two 3-dim vectors in cartesian coords */
167 #define DOT_VECT(u,v) ((u)[0]*(v)[0] + (u)[1]*(v)[1] + (u)[2]*(v)[2])
168 
169 /** Vector product of two 3-dim vectors in cartesian coords */
170 #define CROSS_VECT_0(u,v) ((u)[1]*(v)[2] - (u)[2]*(v)[1])
171 #define CROSS_VECT_1(u,v) ((u)[2]*(v)[0] - (u)[0]*(v)[2])
172 #define CROSS_VECT_2(u,v) ((u)[0]*(v)[1] - (u)[1]*(v)[0])
173 #define CROSS_VECT(x,u,v) do { (x)[0] = CROSS_VECT_0(u,v); (x)[1] = CROSS_VECT_1(u,v); (x)[2] = CROSS_VECT_2(u,v); } while (0)
174 
175 /** Operations on 3-dim vectors */
176 #define ZERO_VECT(v) do{ (v)[0] = 0; (v)[1] = 0; (v)[2] = 0; } while(0)
177 #define NORMSQ_VECT(v) DOT_VECT(v,v)
178 #define NORM_VECT(v) sqrt(NORMSQ_VECT(v))
179 #define MULT_VECT(v,lam) do{ (v)[0] *= (lam); (v)[1] *= (lam); (v)[2] *= (lam); } while(0)
180 #define ADD_VECT(dst,src) do { (dst)[0] += (src)[0]; (dst)[1] += (src)[1]; (dst)[2] += (src)[2]; } while(0)
181 #define SUB_VECT(dst,src) do { (dst)[0] -= (src)[0]; (dst)[1] -= (src)[1]; (dst)[2] -= (src)[2]; } while(0)
182 
183 /** Convert 3-D vector from equatorial into ecliptic coordinates */
184 #define EQU_VECT_TO_ECL(dst,src) do { \
185  (dst)[0] = (src)[0]; \
186  (dst)[1] = (src)[1]*LAL_COSIEARTH + (src)[2]*LAL_SINIEARTH; \
187  (dst)[2] = (src)[2]*LAL_COSIEARTH - (src)[1]*LAL_SINIEARTH; \
188  } while(0)
189 
190 /** Convert 3-D vector from ecliptic into equatorial coordinates */
191 #define ECL_VECT_TO_EQU(dst,src) do { \
192  (dst)[0] = (src)[0]; \
193  (dst)[1] = (src)[1]*LAL_COSIEARTH - (src)[2]*LAL_SINIEARTH; \
194  (dst)[2] = (src)[2]*LAL_COSIEARTH + (src)[1]*LAL_SINIEARTH; \
195  } while(0)
196 
197 #define SQUARE(x) ((x) * (x))
198 
199 /** convert GPS-time to REAL8 */
200 #define GPS2REAL8(gps) (1.0 * (gps).gpsSeconds + 1.e-9 * (gps).gpsNanoSeconds )
201 
202 #define MYMAX(a,b) ( (a) > (b) ? (a) : (b) )
203 #define MYMIN(a,b) ( (a) < (b) ? (a) : (b) )
204 
205 #define RELERR(dx,x) ( (x) == 0 ? (dx) : ( (dx) / (x) ) )
206 
207 /* highest supported spindown-order */
208 #define MAX_SPDNORDER 4
209 
210 /** 5-point derivative formulas (eg see http://math.fullerton.edu/mathews/articles/2003NumericalDiffFormulae.pdf) */
211 #define DERIV5P_1(pm2,pm1,p0,pp1,pp2,h) ( ( (pm2) - 8.0 * (pm1) + 8.0 * (pp1) - (pp2)) / ( 12.0 * (h) ) )
212 #define DERIV5P_2(pm2,pm1,p0,pp1,pp2,h) ( (-(pm2) + 16.0 * (pm1) - 30.0 * (p0) + 16.0 * (pp1) - (pp2) ) / ( 12.0 * (h) * (h) ) )
213 #define DERIV5P_3(pm2,pm1,p0,pp1,pp2,h) ( (-(pm2) + 2.0 * (pm1) - 2.0 * (pp1) + (pp2) ) / ( 2.0 * (h) * (h) * (h) ) )
214 #define DERIV5P_4(pm2,pm1,p0,pp1,pp2,h) ( ( (pm2) - 4.0 * (pm1) + 6.0 * (p0) - 4.0 * (pp1) + (pp2) ) / ( (h) * (h) * (h) * (h) ) )
215 
216 /*----- SWITCHES -----*/
217 /*---------- internal types ----------*/
218 
219 /**
220  * components of antenna-pattern function: q_l = {a(t), b(t)}
221  */
222 typedef enum {
223  AMCOMP_NONE = -1, /**< no antenna pattern function: (equivalent "a = 1") */
224  AMCOMP_A = 0, /**< a(t) */
225  AMCOMP_B, /**< b(t) */
228 
229 /** parameters for metric-integration */
230 typedef struct {
231  int errnum; /**< store XLAL error of any failures within integrator */
232  double epsrel; /**< relative error tolerance for GSL integration routines */
233  double epsabs; /**< absolute error tolerance for GSL integration routines */
234  double intT; /**< length of integration time segments for phase integrals */
235  DetectorMotionType detMotionType; /**< which detector-motion to use in metric integration */
236  const DopplerCoordinateSystem *coordSys;/**< Doppler coordinate system in use */
237  const gsl_matrix *coordTransf; /**< coordinate transform to apply to coordinate system */
238  int coord1, coord2; /**< coordinate indexes of the two components of the derivative-product Phi_i_Phi_j to compute*/
239  int coord; /**< coordinate index of the component for single phase-derivative Phi_i compute */
240  AM_comp_t amcomp1, amcomp2; /**< two AM components q_l q_m */
241  const PulsarDopplerParams *dopplerPoint;/**< Doppler params to compute metric for */
242  REAL8 startTime; /**< GPS start time of observation */
243  REAL8 refTime; /**< GPS reference time for pulsar parameters */
244  REAL8 Tspan; /**< length of observation time in seconds */
245  const LALDetector *site; /**< detector site to compute metric for */
246  const EphemerisData *edat; /**< ephemeris data */
247  vect3Dlist_t *rOrb_n; /**< list of orbital-radius derivatives at refTime of order n = 0, 1, ... */
248  BOOLEAN approxPhase; /**< use an approximate phase-model, neglecting Roemer delay in spindown coordinates (or orders >= 1) */
249 } intparams_t;
250 
251 
252 /*---------- Global variables ----------*/
253 
254 /* Some local constants. */
255 #define rOrb_c (LAL_AU_SI / LAL_C_SI)
256 #define rEarth_c (LAL_REARTH_SI / LAL_C_SI)
257 #define vOrb_c (LAL_TWOPI * LAL_AU_SI / LAL_C_SI / LAL_YRSID_SI)
258 
259 /*---------- internal prototypes ----------*/
261 static gsl_matrix *XLALComputeFisherFromAtoms( const FmetricAtoms_t *atoms, PulsarAmplitudeParams Amp );
262 
263 static double CW_am1_am2_Phi_i_Phi_j( double tt, void *params );
264 static double CW_Phi_i( double tt, void *params );
265 
266 static double XLALAverage_am1_am2_Phi_i_Phi_j( const intparams_t *params, double *relerr_max );
267 static double XLALCovariance_Phi_ij( const MultiLALDetector *multiIFO, const MultiNoiseFloor *multiNoiseFloor, const LALSegList *segList,
268  const intparams_t *params, double *relerr_max );
269 
270 static UINT4 findHighestGCSpinOrder( const DopplerCoordinateSystem *coordSys );
271 
272 /*==================== FUNCTION DEFINITIONS ====================*/
273 
274 /// \addtogroup UniversalDopplerMetric_h
275 /// @{
276 
277 /**
278  * Integrate a general quadruple product CW_am1_am2_Phi_i_Phi_j() from 0 to 1.
279  * This implements the expression \f$ \langle<q_1 q_2 \phi_i \phi_j\rangle \f$
280  * for single-IFO average over the observation time.
281  *
282  * The input parameters correspond to CW_am1_am2_Phi_i_Phi_j()
283  */
284 static double
286 {
287  intparams_t par = ( *params ); /* struct-copy, as the 'coord' field has to be changeable */
288  gsl_function integrand;
289  double epsrel = params->epsrel;
290  double epsabs = params->epsabs;
291  const size_t limit = 512;
292  gsl_integration_workspace *wksp = NULL;
293  int stat;
294 
295  integrand.params = ( void * )&par;
296 
297  const UINT4 intN = ( UINT4 ) ceil( params->Tspan / params->intT );
298  const REAL8 dT = 1.0 / intN;
299 
300  REAL8 res = 0;
301  REAL8 abserr2 = 0;
302 
303  /* allocate workspace for adaptive integration */
304  wksp = gsl_integration_workspace_alloc( limit );
305  XLAL_CHECK_REAL8( wksp != NULL, XLAL_EFAULT );
306 
307  const double scale12 = GET_COORD_SCALE( par.coordSys, par.coord1 ) * GET_COORD_SCALE( par.coordSys, par.coord2 );
308 
309  /* compute <q_1 q_2 phi_i phi_j> as an integral from tt=0 to tt=1 */
310  integrand.function = &CW_am1_am2_Phi_i_Phi_j;
311  for ( UINT4 n = 0; n < intN; n ++ ) {
312  REAL8 ti = 1.0 * n * dT;
313  REAL8 tf = MYMIN( ( n + 1.0 ) * dT, 1.0 );
314  REAL8 err_n, res_n;
315 
316  XLAL_CALLGSL( stat = gsl_integration_qag( &integrand, ti, tf, epsabs, epsrel, limit, GSL_INTEG_GAUSS61, wksp, &res_n, &err_n ) );
317  /* NOTE: we don't fail if the requested level of accuracy was not reached, rather we only output a warning
318  * and try to estimate the final accuracy of the integral
319  */
320  if ( stat != 0 ) {
321  XLALPrintWarning( "\n%s: GSL-integration 'gsl_integration_qag()' of <am1_am2_Phi_i Phi_j> did not reach requested precision!\n", __func__ );
322  XLALPrintWarning( "xlalErrno=%i, Segment n=%d, Result = %g, abserr=%g ==> relerr = %.2e > %.2e\n",
323  par.errnum, n, res_n, err_n, fabs( err_n / res_n ), epsrel );
324  /* XLAL_ERROR_REAL8( XLAL_EFUNC ); */
325  }
326 
327  res_n *= scale12;
328  err_n *= scale12;
329 
330  res += res_n;
331  abserr2 += SQUARE( err_n );
332 
333  } /* for n < intN */
334 
335  REAL8 relerr = RELERR( sqrt( abserr2 ), fabs( res ) );
336  if ( relerr_max ) {
337  ( *relerr_max ) = relerr;
338  }
339 
340  gsl_integration_workspace_free( wksp );
341 
342  return res;
343 
344 } /* XLALAverage_am1_am2_Phi_i_Phi_j() */
345 
346 
347 /**
348  * For gsl-integration: general quadruple product between two antenna-pattern functions
349  * am1, am2 in {a(t),b(t)} and two phase-derivatives phi_i * phi_j,
350  * i.e. compute an expression of the form
351  * \f$ q_1(t) q_2(t) \phi_i(t) \phi_j(t) \f$ , where \f$ q_i = \{a(t), b(t)\} \f$ .
352  *
353  * NOTE: this can be 'truncated' to any sub-expression by using
354  * AMCOMP_NONE for antenna-pattern component and DOPPLERCOORD_NONE for DopplerCoordinate,
355  * eg in this way this function can be used to compute \f$ a^2(t), b^2(t), a(t) b(t) \f$ ,
356  * or \f$ phi_i(t) phi_j(t) \f$ .
357  */
358 static double
359 CW_am1_am2_Phi_i_Phi_j( double tt, void *params )
360 {
362  if ( par->errnum ) {
363  return GSL_NAN;
364  }
365 
366  REAL8 am1, am2, phi_i, phi_j, ret;
367 
368  am1 = am2 = 1.0; /* default */
369  phi_i = phi_j = 1.0;
370 
371  /* do we need any antenna-pattern functions in here? */
372  if ( ( par->amcomp1 != AMCOMP_NONE ) || ( par->amcomp1 != AMCOMP_NONE ) ) {
373  REAL8 ttSI;
374  LIGOTimeGPS ttGPS;
375  SkyPosition skypos;
376  REAL8 ai, bi;
377 
379  skypos.longitude = par->dopplerPoint->Alpha;
380  skypos.latitude = par->dopplerPoint->Delta;
381 
382  ttSI = par->startTime + tt * par->Tspan; /* current GPS time in seconds */
383  XLALGPSSetREAL8( &ttGPS, ttSI );
384 
385  int errnum;
386  XLAL_TRY( XLALComputeAntennaPatternCoeffs( &ai, &bi, &skypos, &ttGPS, par->site, par->edat ), errnum );
387  if ( errnum ) {
388  XLALPrintError( "%s: Call to XLALComputeAntennaPatternCoeffs() failed!\n", __func__ );
389  GSL_ERROR_VAL( "Failure in CW_am1_am2_Phi_i_Phi_j", GSL_EFAILED, GSL_NAN );
390  }
391 
392  /* first antenna-pattern component */
393  if ( par->amcomp1 == AMCOMP_A ) {
394  am1 = ai;
395  } else if ( par->amcomp1 == AMCOMP_B ) {
396  am1 = bi;
397  }
398  /* second antenna-pattern component */
399  if ( par->amcomp2 == AMCOMP_A ) {
400  am2 = ai;
401  } else if ( par->amcomp2 == AMCOMP_B ) {
402  am2 = bi;
403  }
404 
405  } /* if any antenna-pattern components needed */
406 
407  /* first Doppler phase derivative */
408  if ( GET_COORD_ID( par->coordSys, par->coord1 ) != DOPPLERCOORD_NONE ) {
409  par->coord = par->coord1;
410  phi_i = CW_Phi_i( tt, params );
411  } else {
412  phi_i = 1.0;
413  }
414 
415  /* second Doppler phase derivative */
416  if ( GET_COORD_ID( par->coordSys, par->coord2 ) != DOPPLERCOORD_NONE ) {
417  par->coord = par->coord2;
418  phi_j = CW_Phi_i( tt, params );
419  } else {
420  phi_j = 1.0;
421  }
422 
423  ret = am1 * am2 * phi_i * phi_j;
424 
426  "amcomp1=%d amcomp2=%d coord1='%s' coord2='%s' tt=%f am1=%f am2=%f phi_i=%f phi_j=%f ret=%f\n",
427  par->amcomp1, par->amcomp2,
428  GET_COORD_NAME( par->coordSys, par->coord1 ), GET_COORD_NAME( par->coordSys, par->coord2 ),
429  tt, am1, am2, phi_i, phi_j, ret );
430 
431  return ( ret );
432 
433 } /* CW_am1_am2_Phi_i_Phi_j() */
434 
435 
436 REAL8
437 XLALComputePhaseDerivative( REAL8 t, ///< time 't' to compute derivative at: (effectively interpreted as SSB time if approxPhase given)
438  const PulsarDopplerParams *dopplerPoint, ///< phase-evolution ('doppler') parameters to compute phase-derivative at
439  DopplerCoordinateID coordID, ///< coord-ID of coordinate wrt which to compute phase derivative
440  const EphemerisData *edat, ///< ephemeris data
441  const LALDetector *site, ///< optional detector (if NULL: use H1 as default)
442  BOOLEAN includeRoemer ///< whether to include Roemer-delay correction 'tSSB = t + dRoemer(t)' or not
443  )
444 {
445  XLAL_CHECK_REAL8( dopplerPoint != NULL, XLAL_EINVAL );
446  XLAL_CHECK_REAL8( edat != NULL, XLAL_EINVAL );
447  XLAL_CHECK_REAL8( ( coordID > DOPPLERCOORD_NONE ) && ( coordID < DOPPLERCOORD_LAST ), XLAL_EINVAL );
448 
449  const DopplerCoordinateSystem coordSys = {
450  .dim = 1,
451  .coordIDs = { coordID },
452  };
453 
454  REAL8 refTime8 = XLALGPSGetREAL8( &( dopplerPoint->refTime ) );
455 
456  intparams_t params = {
457  .detMotionType = DETMOTION_SPIN | DETMOTION_ORBIT, // doesn't matter, dummy value
458  .coordSys = &coordSys,
459  .dopplerPoint = dopplerPoint,
460  .startTime = t,
461  .refTime = refTime8,
462  .Tspan = 0, // doesn't matter, dummy value
463  .site = site ? site :&lalCachedDetectors[LAL_LHO_4K_DETECTOR], // fall back to H1 if passed as NULL
464  .edat = edat,
465  .approxPhase = includeRoemer, // whether to include Roemer-delay correction tSSB = t + dRoemer(t)
466  };
467 
468  double scale = DopplerCoordinates[coordID].scale;
469 
470  return ( REAL8 )( scale * CW_Phi_i( 0, &params ) );
471 
472 } // XLALComputePhaseDerivativeSSB()
473 
474 /**
475  * Partial derivative of continuous-wave (CW) phase, with respect
476  * to Doppler coordinate 'i' := intparams_t->phderiv
477  *
478  * Time is in 'natural units' of Tspan, i.e. tt is in [0, 1] corresponding
479  * to GPS-times in [startTime, startTime + Tspan ]
480  *
481  */
482 static double
483 CW_Phi_i( double tt, void *params )
484 {
486  if ( par->errnum ) {
487  return GSL_NAN;
488  }
489 
490  vect3D_t nn_equ, nn_ecl; /* skypos unit vector */
491  vect3D_t nDeriv_i; /* derivative of sky-pos vector wrt i */
492 
493  /* positions/velocities at time tt: */
494  PosVel3D_t XLAL_INIT_DECL( spin_posvel );
495  PosVel3D_t XLAL_INIT_DECL( orbit_posvel );
496  PosVel3D_t XLAL_INIT_DECL( posvel );
497 
498  /* positions in ecliptic plane */
499  vect3D_t XLAL_INIT_DECL( ecl_orbit_pos );
500  vect3D_t XLAL_INIT_DECL( ecl_pos );
501 
502  /* get skypos-vector */
503  const REAL8 cosa = cos( par->dopplerPoint->Alpha );
504  const REAL8 sina = sin( par->dopplerPoint->Alpha );
505  const REAL8 cosd = cos( par->dopplerPoint->Delta );
506  const REAL8 sind = sin( par->dopplerPoint->Delta );
507 
508  /* ... in an equatorial coordinate-frame */
509  nn_equ[0] = cosd * cosa;
510  nn_equ[1] = cosd * sina;
511  nn_equ[2] = sind;
512 
513  /* and in an ecliptic coordinate-frame */
514  EQU_VECT_TO_ECL( nn_ecl, nn_equ );
515 
516  /* get current detector position r(t) and velocity v(t) */
517  REAL8 ttSI = par->startTime + tt * par->Tspan; /* current GPS time in seconds */
518  LIGOTimeGPS ttGPS;
519  XLALGPSSetREAL8( &ttGPS, ttSI );
520  if ( XLALDetectorPosVel( &spin_posvel, &orbit_posvel, &ttGPS, par->site, par->edat, par->detMotionType ) != XLAL_SUCCESS ) {
521  par->errnum = xlalErrno;
522  XLALPrintError( "%s: Call to XLALDetectorPosVel() failed!\n", __func__ );
523  return GSL_NAN;
524  }
525 
526  /* XLALDetectorPosVel() returns detector positions and velocities from XLALBarycenter(),
527  which are in units of LAL_C_SI, so we multiply by LAL_C_SI to get back to SI units.
528  We then divide by the internal scale SCALE_R. */
529  MULT_VECT( spin_posvel.pos, LAL_C_SI / SCALE_R );
530  MULT_VECT( orbit_posvel.pos, LAL_C_SI / SCALE_R );
531  MULT_VECT( spin_posvel.vel, LAL_C_SI / SCALE_R );
532  MULT_VECT( orbit_posvel.vel, LAL_C_SI / SCALE_R );
533 
534  /* calculate detector total motion = spin motion + orbital motion */
535  COPY_VECT( posvel.pos, spin_posvel.pos );
536  ADD_VECT( posvel.pos, orbit_posvel.pos );
537  COPY_VECT( posvel.vel, spin_posvel.vel );
538  ADD_VECT( posvel.vel, orbit_posvel.vel );
539 
540  /* compute detector positions projected onto ecliptic plane */
541  EQU_VECT_TO_ECL( ecl_orbit_pos, orbit_posvel.pos );
542  EQU_VECT_TO_ECL( ecl_pos, posvel.pos );
543 
544  /* get frequency of Doppler point */
545  const REAL8 Freq = par->dopplerPoint->fkdot[0];
546 
547  /* get time span, normalised by internal scale SCALE_R */
548  const REAL8 Tspan = par->Tspan / SCALE_T;
549 
550  /* account for referenceTime != startTime, in units of SCALE_T */
551  REAL8 tau0 = ( par->startTime - par->refTime ) / SCALE_T;
552 
553  /* barycentric time-delay since reference time, in units of SCALE_T */
554  REAL8 tau = tau0 + ( tt * Tspan );
555 
556  /* correct for time-delay from SSB to detector (Roemer delay), neglecting relativistic effects */
557  if ( !par->approxPhase ) {
558  /* SSB time-delay in internal scaled units */
559  REAL8 dTRoemer = DOT_VECT( nn_equ, posvel.pos ) * ( SCALE_R / LAL_C_SI / SCALE_T );
560 
561  tau += dTRoemer;
562  }
563 
564  /* get 'reduced' detector position of order 'n': r_n(t) [passed in as argument]
565  * defined as: r_n(t) = r(t) - dot{r_orb}(tau_ref) tau - 1/2! ddot{r_orb}(tau_re) tau^2 - ....
566  */
567  vect3D_t rr_ord_Equ, rr_ord_Ecl;
568  UINT4 i, n;
569  COPY_VECT( rr_ord_Equ, posvel.pos ); /* 0th order */
570  if ( par->rOrb_n ) {
571  /* par->rOrb_n are derivatives with SI time units, so multiply tau by SCALE_T */
572  const double tauSI = tau * SCALE_T;
573  for ( n = 0; n < par->rOrb_n->length; n ++ ) {
574  /* par->rOrb_n->data[n][i] is calculated from detector positions given by XLALBarycenter(),
575  which are in units of LAL_C_SI, so we multiply by LAL_C_SI to get back to SI units.
576  We then divide by the internal scale SCALE_R. */
577  REAL8 pre_n = LAL_FACT_INV[n] * pow( tauSI, n ) * ( LAL_C_SI / SCALE_R );
578  for ( i = 0; i < 3; i++ ) {
579  rr_ord_Equ[i] -= pre_n * par->rOrb_n->data[n][i];
580  }
581  }
582  } /* if rOrb_n */
583  EQU_VECT_TO_ECL( rr_ord_Ecl, rr_ord_Equ ); /* convert into ecliptic coordinates */
584 
585  // ---------- prepare shortcuts for binary orbital parameters ----------
586  // See Leaci, Prix, PRD91, 102003 (2015): DOI:10.1103/PhysRevD.91.102003
587  REAL8 orb_asini = par->dopplerPoint->asini;
588  REAL8 orb_Omega = ( LAL_TWOPI / par->dopplerPoint->period );
589  REAL8 orb_kappa = par->dopplerPoint->ecc * cos( par->dopplerPoint->argp ); // Eq.(33)
590  REAL8 orb_eta = par->dopplerPoint->ecc * sin( par->dopplerPoint->argp ); // Eq.(34)
591 
592  REAL8 orb_phase = par->dopplerPoint->argp + orb_Omega * ( ttSI - XLALGPSGetREAL8( &( par->dopplerPoint->tp ) ) ); // Eq.(35),(36)
593  REAL8 sinPsi = sin( orb_phase );
594  REAL8 cosPsi = cos( orb_phase );
595  REAL8 sin2Psi = sin( 2.0 * orb_phase );
596  REAL8 cos2Psi = cos( 2.0 * orb_phase );
597 
598  /* now compute the requested (possibly linear combination of) phase derivative(s) */
599  REAL8 phase_deriv = 0.0;
600  for ( int coord = 0; coord < ( int )par->coordSys->dim; ++coord ) {
601 
602  /* get the coefficient used to multiply phase derivative term */
603  REAL8 coeff = 0.0;
604  if ( par->coordTransf != NULL ) {
605  coeff = gsl_matrix_get( par->coordTransf, par->coord, coord );
606  } else if ( par->coord == coord ) {
607  coeff = 1.0;
608  }
609  if ( coeff == 0.0 ) {
610  continue;
611  }
612  coeff *= GET_COORD_SCALE( par->coordSys, coord ) / GET_COORD_SCALE( par->coordSys, par->coord );
613 
614  /* compute the phase derivative term */
615  REAL8 ret = 0.0;
616  const DopplerCoordinateID deriv = GET_COORD_ID( par->coordSys, coord );
617  switch ( deriv ) {
618 
619  case DOPPLERCOORD_FREQ: /**< Frequency [Units: Hz]. */
620  case DOPPLERCOORD_GC_NU0: /**< Global correlation frequency [Units: Hz]. Activates 'reduced' detector position. */
621  ret = LAL_TWOPI * tau * LAL_FACT_INV[1];
622  break;
623  case DOPPLERCOORD_F1DOT: /**< First spindown [Units: Hz/s]. */
624  case DOPPLERCOORD_GC_NU1: /**< Global correlation first spindown [Units: Hz/s]. Activates 'reduced' detector position. */
625  ret = LAL_TWOPI * POW2( tau ) * LAL_FACT_INV[2];
626  break;
627  case DOPPLERCOORD_F2DOT: /**< Second spindown [Units: Hz/s^2]. */
628  case DOPPLERCOORD_GC_NU2: /**< Global correlation second spindown [Units: Hz/s^2]. Activates 'reduced' detector position. */
629  ret = LAL_TWOPI * POW3( tau ) * LAL_FACT_INV[3];
630  break;
631  case DOPPLERCOORD_F3DOT: /**< Third spindown [Units: Hz/s^3]. */
632  case DOPPLERCOORD_GC_NU3: /**< Global correlation third spindown [Units: Hz/s^3]. Activates 'reduced' detector position. */
633  ret = LAL_TWOPI * POW4( tau ) * LAL_FACT_INV[4];
634  break;
635  case DOPPLERCOORD_F4DOT: /**< Fourth spindown [Units: Hz/s^4]. */
636  case DOPPLERCOORD_GC_NU4: /**< Global correlation fourth spindown [Units: Hz/s^4]. Activates 'reduced' detector position. */
637  ret = LAL_TWOPI * POW5( tau ) * LAL_FACT_INV[5];
638  break;
639 
640  case DOPPLERCOORD_ALPHA: /**< Right ascension [Units: radians]. Uses 'reduced' detector position. */
641  nDeriv_i[0] = - cosd * sina;
642  nDeriv_i[1] = cosd * cosa;
643  nDeriv_i[2] = 0;
644  ret = LAL_TWOPI * Freq * DOT_VECT( rr_ord_Equ, nDeriv_i );
645  break;
646  case DOPPLERCOORD_DELTA: /**< Declination [Units: radians]. Uses 'reduced' detector position. */
647  nDeriv_i[0] = - sind * cosa;
648  nDeriv_i[1] = - sind * sina;
649  nDeriv_i[2] = cosd;
650  ret = LAL_TWOPI * Freq * DOT_VECT( rr_ord_Equ, nDeriv_i );
651  break;
652 
653  case DOPPLERCOORD_N2X_EQU: /**< X component of constrained sky position in equatorial coordinates [Units: none]. Uses 'reduced' detector position. */
654  ret = LAL_TWOPI * Freq * ( rr_ord_Equ[0] - ( nn_equ[0] / nn_equ[2] ) * rr_ord_Equ[2] );
655  break;
656  case DOPPLERCOORD_N2Y_EQU: /**< Y component of constrained sky position in equatorial coordinates [Units: none]. Uses 'reduced' detector position. */
657  ret = LAL_TWOPI * Freq * ( rr_ord_Equ[1] - ( nn_equ[1] / nn_equ[2] ) * rr_ord_Equ[2] );
658  break;
659 
660  case DOPPLERCOORD_N2X_ECL: /**< X component of constrained sky position in ecliptic coordinates [Units: none]. Uses 'reduced' detector position. */
661  ret = LAL_TWOPI * Freq * ( rr_ord_Ecl[0] - ( nn_ecl[0] / nn_ecl[2] ) * rr_ord_Ecl[2] );
662  break;
663  case DOPPLERCOORD_N2Y_ECL: /**< Y component of constrained sky position in ecliptic coordinates [Units: none]. Uses 'reduced' detector position. */
664  ret = LAL_TWOPI * Freq * ( rr_ord_Ecl[1] - ( nn_ecl[1] / nn_ecl[2] ) * rr_ord_Ecl[2] );
665  break;
666 
667  case DOPPLERCOORD_N3X_EQU: /**< X component of unconstrained super-sky position in equatorial coordinates [Units: none]. */
668  ret = LAL_TWOPI * Freq * posvel.pos[0];
669  break;
670  case DOPPLERCOORD_N3Y_EQU: /**< Y component of unconstrained super-sky position in equatorial coordinates [Units: none]. */
671  ret = LAL_TWOPI * Freq * posvel.pos[1];
672  break;
673  case DOPPLERCOORD_N3Z_EQU: /**< Z component of unconstrained super-sky position in equatorial coordinates [Units: none]. */
674  ret = LAL_TWOPI * Freq * posvel.pos[2];
675  break;
676 
677  case DOPPLERCOORD_N3X_ECL: /**< X component of unconstrained super-sky position in ecliptic coordinates [Units: none]. */
678  ret = LAL_TWOPI * Freq * ecl_pos[0];
679  break;
680  case DOPPLERCOORD_N3Y_ECL: /**< Y component of unconstrained super-sky position in ecliptic coordinates [Units: none]. */
681  ret = LAL_TWOPI * Freq * ecl_pos[1];
682  break;
683  case DOPPLERCOORD_N3Z_ECL: /**< Z component of unconstrained super-sky position in ecliptic coordinates [Units: none]. */
684  ret = LAL_TWOPI * Freq * ecl_pos[2];
685  break;
686 
687  case DOPPLERCOORD_N3SX_EQU: /**< X spin-component of unconstrained super-sky position in equatorial coordinates [Units: none]. */
688  ret = LAL_TWOPI * Freq * spin_posvel.pos[0];
689  break;
690  case DOPPLERCOORD_N3SY_EQU: /**< Y spin-component of unconstrained super-sky position in equatorial coordinates [Units: none]. */
691  ret = LAL_TWOPI * Freq * spin_posvel.pos[1];
692  break;
693 
694  case DOPPLERCOORD_N3OX_ECL: /**< X orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none]. */
695  ret = LAL_TWOPI * Freq * ecl_orbit_pos[0];
696  break;
697  case DOPPLERCOORD_N3OY_ECL: /**< Y orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none]. */
698  ret = LAL_TWOPI * Freq * ecl_orbit_pos[1];
699  break;
700  case DOPPLERCOORD_N3OZ_ECL: /**< Z orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none]. */
701  ret = LAL_TWOPI * Freq * ecl_orbit_pos[2];
702  break;
703 
704  // ---------- binary orbital parameters ----------
705  // Phase derivates taken from Eq.(39) in Leaci, Prix, PRD91, 102003 (2015): DOI:10.1103/PhysRevD.91.102003
706  case DOPPLERCOORD_ASINI: /**< Projected semimajor axis of binary orbit in small-eccentricy limit (ELL1 model) [Units: light seconds]. */
707  ret = - LAL_TWOPI * Freq * ( sinPsi + 0.5 * orb_kappa * sin2Psi - 0.5 * orb_eta * cos2Psi );
708  break;
709  case DOPPLERCOORD_TASC: /**< Time of ascension (neutron star crosses line of nodes moving away from observer) for binary orbit (ELL1 model) [Units: GPS s]. */
710  ret = LAL_TWOPI * Freq * orb_asini * orb_Omega * ( cosPsi + orb_kappa * cos2Psi + orb_eta * sin2Psi );
711  break;
712  case DOPPLERCOORD_PORB: /**< Period of binary orbit (ELL1 model) [Units: s]. */
713  ret = Freq * orb_asini * orb_Omega * orb_phase * ( cosPsi + orb_kappa * cos2Psi + orb_eta * sin2Psi );
714  break;
715  case DOPPLERCOORD_KAPPA: /**< Lagrange parameter 'kappa = ecc * cos(argp)', ('ecc' = eccentricity, 'argp' = argument of periapse) of binary orbit (ELL1 model) [Units: none] */
716  ret = - LAL_PI * Freq * orb_asini * sin2Psi;
717  break;
718  case DOPPLERCOORD_ETA: /**< Lagrange parameter 'eta = ecc * sin(argp) of binary orbit (ELL1 model) [Units: none] */
719  ret = LAL_PI * Freq * orb_asini * cos2Psi;
720  break;
721 
722  // --------- rescaled binary orbital parameters for (approximately) flat metric
723  case DOPPLERCOORD_DASC: /**< Distance traversed on the arc of binary orbit (ELL1 model) 'dasc = 2 * pi * (ap/porb) * tasc' [Units: light second]." */
724  ret = LAL_TWOPI * Freq * ( cosPsi + orb_kappa * cos2Psi + orb_eta * sin2Psi );
725  break;
726 
727  case DOPPLERCOORD_VP: /**< Rescaled (by asini) differential-coordinate 'dvp = asini * dOMEGA', ('OMEGA' = 2 * pi/'porb') of binary orbit (ELL1 model) [Units: (light second)/(GPS second)]. */
728  ret = - LAL_TWOPI * Freq * ( orb_phase / orb_Omega ) * ( cosPsi + orb_kappa * cos2Psi + orb_eta * sin2Psi );
729  break;
730 
731  case DOPPLERCOORD_KAPPAP: /**< Rescaled (by asini) differential-coordinate 'dkappap = asini * dkappa' [Units: light seconds]. */
732  ret = - LAL_PI * Freq * sin2Psi;
733  break;
734 
735  case DOPPLERCOORD_ETAP: /**< Rescaled (by asini) differential-coordinate 'detap = asini * deta' [Units: light seconds]. */
736  ret = LAL_PI * Freq * cos2Psi;
737  break;
738 
739  // ------------------------------------------------
740  default:
741  par->errnum = XLAL_EINVAL;
742  XLALPrintError( "%s: Unknown phase-derivative type '%d'\n", __func__, deriv );
743  return GSL_NAN;
744  break;
745 
746  } /* switch deriv */
747 
748  phase_deriv += coeff * ret;
749 
750  }
751 
752  return phase_deriv;
753 
754 } /* CW_Phi_i() */
755 
756 
757 /**
758  * Given a GPS time and detector, return the current position (and velocity) of the detector.
759  */
760 int
761 XLALDetectorPosVel( PosVel3D_t *spin_posvel, /**< [out] instantaneous sidereal position and velocity vector */
762  PosVel3D_t *orbit_posvel, /**< [out] instantaneous orbital position and velocity vector */
763  const LIGOTimeGPS *tGPS, /**< [in] GPS time */
764  const LALDetector *site, /**< [in] detector info */
765  const EphemerisData *edat, /**< [in] ephemeris data */
766  DetectorMotionType detMotionType /**< [in] detector motion type */
767  )
768 {
769  EarthState earth;
770  BarycenterInput XLAL_INIT_DECL( baryinput );
772  PosVel3D_t Det_wrt_Earth;
773  PosVel3D_t PtoleOrbit;
774  PosVel3D_t Spin_z, Spin_xy;
775  const vect3D_t eZ = {0, -LAL_SINIEARTH, LAL_COSIEARTH}; /* ecliptic z-axis in equatorial coordinates */
776 
777  XLAL_CHECK( tGPS, XLAL_EFAULT );
780 
781  XLAL_CHECK( detMotionType > 0, XLAL_EINVAL, "Invalid detector motion type '%d'", detMotionType );
782 
783  /* ----- find ephemeris-based position of Earth wrt to SSB at this moment */
785 
786  /* ----- find ephemeris-based position of detector wrt to SSB */
787  baryinput.tgps = *tGPS;
788  baryinput.site = *site;
789  baryinput.site.location[0] /= LAL_C_SI;
790  baryinput.site.location[1] /= LAL_C_SI;
791  baryinput.site.location[2] /= LAL_C_SI;
792  baryinput.alpha = 0;
793  baryinput.delta = 0;
794  baryinput.dInv = 0;
795  XLAL_CHECK( XLALBarycenter( &emit, &baryinput, &earth ) == XLAL_SUCCESS, XLAL_EFUNC );
796 
797  /* ----- determine position-vector of detector wrt center of Earth */
798  COPY_VECT( Det_wrt_Earth.pos, emit.rDetector );
799  SUB_VECT( Det_wrt_Earth.pos, earth.posNow );
800  COPY_VECT( Det_wrt_Earth.vel, emit.vDetector );
801  SUB_VECT( Det_wrt_Earth.vel, earth.velNow );
802 
803  /* compute ecliptic-z projected spin motion */
804  REAL8 pz = DOT_VECT( Det_wrt_Earth.pos, eZ );
805  REAL8 vz = DOT_VECT( Det_wrt_Earth.vel, eZ );
806  COPY_VECT( Spin_z.pos, eZ );
807  MULT_VECT( Spin_z.pos, pz );
808  COPY_VECT( Spin_z.vel, eZ );
809  MULT_VECT( Spin_z.vel, vz );
810 
811  /* compute ecliptic-xy projected spin motion */
812  COPY_VECT( Spin_xy.pos, Det_wrt_Earth.pos );
813  SUB_VECT( Spin_xy.pos, Spin_z.pos );
814  COPY_VECT( Spin_xy.vel, Det_wrt_Earth.vel );
815  SUB_VECT( Spin_xy.vel, Spin_z.vel );
816 
817  /* ----- Ptolemaic special case: orbital motion on a circle */
818  if ( detMotionType & DETMOTION_PTOLEORBIT ) {
819  XLAL_CHECK( XLALPtolemaicPosVel( &PtoleOrbit, tGPS ) == XLAL_SUCCESS, XLAL_EFUNC );
820  }
821 
822  /* ----- return the requested type of detector motion */
823  if ( spin_posvel ) {
824  switch ( detMotionType & DETMOTION_MASKSPIN ) {
825 
826  case 0: /**< No spin motion */
827  ZERO_VECT( spin_posvel->pos );
828  ZERO_VECT( spin_posvel->vel );
829  break;
830 
831  case DETMOTION_SPIN: /**< Full spin motion */
832  COPY_VECT( spin_posvel->pos, Det_wrt_Earth.pos );
833  COPY_VECT( spin_posvel->vel, Det_wrt_Earth.vel );
834  break;
835 
836  case DETMOTION_SPINZ: /**< Ecliptic-Z component of spin motion only */
837  COPY_VECT( spin_posvel->pos, Spin_z.pos );
838  COPY_VECT( spin_posvel->vel, Spin_z.vel );
839  break;
840 
841  case DETMOTION_SPINXY: /**< Ecliptic-X+Y components of spin motion only */
842  COPY_VECT( spin_posvel->pos, Spin_xy.pos );
843  COPY_VECT( spin_posvel->vel, Spin_xy.vel );
844  break;
845 
846  default:
847  XLAL_ERROR( XLAL_EINVAL, "%s: Illegal spin motion '%d'\n\n", __func__, detMotionType & DETMOTION_MASKSPIN );
848  break;
849  }
850  }
851  if ( orbit_posvel ) {
852  switch ( detMotionType & DETMOTION_MASKORBIT ) {
853 
854  case 0: /**< No orbital motion */
855  ZERO_VECT( orbit_posvel->pos );
856  ZERO_VECT( orbit_posvel->vel );
857  break;
858 
859  case DETMOTION_ORBIT: /**< Ephemeris-based orbital motion */
860  COPY_VECT( orbit_posvel->pos, earth.posNow );
861  COPY_VECT( orbit_posvel->vel, earth.velNow );
862  break;
863 
864  case DETMOTION_PTOLEORBIT: /**< Ptolemaic (circular) orbital motion */
865  COPY_VECT( orbit_posvel->pos, PtoleOrbit.pos );
866  COPY_VECT( orbit_posvel->vel, PtoleOrbit.vel );
867  break;
868 
869  default:
870  XLAL_ERROR( XLAL_EINVAL, "%s: Illegal orbit motion '%d'\n\n", __func__, detMotionType & DETMOTION_MASKORBIT );
871  break;
872  }
873  }
874 
875  return XLAL_SUCCESS;
876 
877 } /* XLALDetectorPosVel() */
878 
879 
880 
881 /**
882  * Compute position and velocity assuming a purely "Ptolemaic" orbital motion
883  * (i.e. on a circle) around the sun, approximating Earth's orbit
884  */
885 int
886 XLALPtolemaicPosVel( PosVel3D_t *posvel, /**< [out] instantaneous position and velocity vector */
887  const LIGOTimeGPS *tGPS /**< [in] GPS time */
888  )
889 {
890  REAL8 tMidnight, tAutumn;
891  REAL8 phiOrb; /* Earth orbital revolution angle, in radians. */
892  REAL8 sinOrb, cosOrb;
893 
894  if ( !posvel || !tGPS ) {
895  XLALPrintError( "%s: Illegal NULL pointer passed!\n", __func__ );
897  }
898 
899  XLAL_CHECK( XLALGetEarthTimes( tGPS, &tMidnight, &tAutumn ) == XLAL_SUCCESS, XLAL_EFUNC );
900 
901  phiOrb = - LAL_TWOPI * tAutumn / LAL_YRSID_SI;
902  sinOrb = sin( phiOrb );
903  cosOrb = cos( phiOrb );
904 
905  /* Get instantaneous position. */
906  posvel->pos[0] = rOrb_c * cosOrb;
907  posvel->pos[1] = rOrb_c * sinOrb * LAL_COSIEARTH;
908  posvel->pos[2] = rOrb_c * sinOrb * LAL_SINIEARTH;
909 
910  /* Get instantaneous velocity. */
911  posvel->vel[0] = -vOrb_c * sinOrb;
912  posvel->vel[1] = vOrb_c * cosOrb * LAL_COSIEARTH;
913  posvel->vel[2] = vOrb_c * cosOrb * LAL_SINIEARTH;
914 
915  return XLAL_SUCCESS;
916 
917 } /* XLALPtolemaicPosVel() */
918 
919 
920 
921 /**
922  * Compute a pure phase-deriv covariance \f$ [\phi_i, \phi_j] = \langle phi_i phi_j\rangle - \langle phi_i\rangle\langle phi_j\rangle \f$
923  * which gives a component of the "phase metric".
924  *
925  * NOTE: for passing unit noise-weights, set MultiNoiseFloor->length=0 (but multiNoiseFloor==NULL is invalid)
926  */
927 static double
928 XLALCovariance_Phi_ij( const MultiLALDetector *multiIFO, //!< [in] detectors to use
929  const MultiNoiseFloor *multiNoiseFloor, //!< [in] corresponding noise floors for weights, NULL means unit-weights
930  const LALSegList *segList, //!< [in] segment list
931  const intparams_t *params, //!< [in] integration parameters
932  double *relerr_max //!< [in] maximal error for integration
933  )
934 {
935  XLAL_CHECK_REAL8( multiIFO != NULL, XLAL_EINVAL );
936  UINT4 numDet = multiIFO->length;
938 
939  // either no noise-weights given (multiNoiseFloor->length=0) or same number of detectors
940  XLAL_CHECK_REAL8( multiNoiseFloor != NULL, XLAL_EINVAL );
941  BOOLEAN haveNoiseWeights = ( multiNoiseFloor->length > 0 );
942  XLAL_CHECK_REAL8( !haveNoiseWeights || ( multiNoiseFloor->length == numDet ), XLAL_EINVAL );
943 
945  UINT4 Nseg = segList->length;
946 
947  /* sanity-check: don't allow any AM-coeffs being turned on here! */
948  if ( params->amcomp1 != AMCOMP_NONE || params->amcomp2 != AMCOMP_NONE ) {
949  XLALPrintError( "%s: Illegal input, amcomp[12] must be set to AMCOMP_NONE!\n", __func__ );
951  }
952 
953  /* store detector weights and accumulate total weight */
954  REAL8 total_weight = 0.0, weights[numDet];
955  for ( UINT4 X = 0; X < numDet; X ++ ) {
956  weights[X] = haveNoiseWeights ? multiNoiseFloor->sqrtSn[X] : 1.0;
957  total_weight += weights[X];
958  }
959  XLAL_CHECK_REAL8( total_weight > 0, XLAL_EDOM, "Detectors noise-floors given but all zero!" );
960 
961  /* ---------- set up GSL integration ---------- */
962 
963  /* constants passed to GSL integration function */
964  const double epsrel = params->epsrel;
965  const double epsabs = params->epsabs;
966  const size_t limit = 512;
967 
968  /* array of structs storing input and output info for GSL integration */
969  UINT4 intN[Nseg][numDet];
970  typedef struct {
971  intparams_t par; /* integration parameters */
972  double ti; /* integration start time */
973  double tf; /* integration end time */
974  gsl_integration_workspace *wksp ; /* GSL integration workspace */
975  double av_ij; /* value of integral <phi_i phi_j> */
976  double av_i; /* value of integral <phi_i> */
977  double av_j; /* value of integral <phi_j> */
978  double av_ij_err_sq; /* squared error in value of integral <phi_i phi_j> */
979  double av_i_err_sq; /* squared error in value of integral <phi_i> */
980  double av_j_err_sq; /* squared error in value of integral <phi_j> */
981  } InputOutputInfo;
982  InputOutputInfo *intInOut[Nseg][numDet];
983 
984  // loop over segments and detectors
985  for ( size_t k = 0; k < Nseg; ++k ) {
986  for ( size_t X = 0; X < numDet; ++X ) {
987 
988  intparams_t par = ( *params ); /* struct-copy, as the 'coord' field has to be changeable */
989 
990  // set start time and time span for this segment
991  const LIGOTimeGPS *startTime = &( segList->segs[k].start );
992  const LIGOTimeGPS *endTime = &( segList->segs[k].end );
993  const REAL8 Tspan = XLALGPSDiff( endTime, startTime );
994  par.startTime = XLALGPSGetREAL8( startTime );
995  par.Tspan = Tspan;
996 
997  // set detector for phase integrals
998  par.site = &multiIFO->sites[X];
999 
1000  // calculate how many 'units' integral is split into
1001  intN[k][X] = ( UINT4 ) ceil( par.Tspan / par.intT );
1002 
1003  // allocate memory of input/output info
1004  intInOut[k][X] = XLALCalloc( intN[k][X], sizeof( *intInOut[k][X] ) );
1005  XLAL_CHECK_REAL8( intInOut[k][X] != NULL, XLAL_ENOMEM );
1006 
1007  // initialise input info
1008  for ( size_t n = 0; n < intN[k][X]; ++n ) {
1009 
1010  InputOutputInfo *io = &intInOut[k][X][n];
1011 
1012  io->par = par;
1013 
1014  const double dT = 1.0 / intN[k][X];
1015  io->ti = 1.0 * n * dT;
1016  io->tf = MYMIN( ( n + 1.0 ) * dT, 1.0 );
1017 
1018  XLAL_CHECK_REAL8( ( io->wksp = gsl_integration_workspace_alloc( limit ) ) != NULL, XLAL_ENOMEM );
1019 
1020  } /* for n < intN */
1021 
1022  } // for X < numDet
1023  } // for k < Nseg
1024 
1025  /* ---------- perform GSL integration ---------- */
1026 
1027  // turn off GSL error handling
1028  gsl_error_handler_t *saveGSLErrorHandler;
1029  saveGSLErrorHandler = gsl_set_error_handler_off();
1030 
1031  // loop over segments, detectors, and integration 'units'
1032  // together using a single index, for parallelisation
1033  {
1034  size_t index_max = 0;
1035  for ( size_t k = 0; k < Nseg; ++k ) {
1036  for ( size_t X = 0; X < numDet; ++X ) {
1037  index_max += intN[k][X];
1038  }
1039  }
1040  #pragma omp parallel for schedule(static)
1041  for ( size_t indx = 0; indx < index_max; ++indx ) {
1042  // break single index into 'k', 'X', and 'n'
1043  size_t k = 0, X = 0, n = 0, index_break = indx + 1;
1044  for ( k = 0; k < Nseg; ++k ) {
1045  for ( X = 0; X < numDet; ++X ) {
1046  if ( index_break > intN[k][X] ) {
1047  index_break -= intN[k][X];
1048  } else {
1049  n = index_break - 1;
1050  index_break = 0;
1051  break;
1052  }
1053  }
1054  if ( index_break == 0 ) {
1055  break;
1056  }
1057  }
1058 
1059  InputOutputInfo *io = &intInOut[k][X][n];
1060 
1061  gsl_function integrand;
1062  integrand.params = ( void * ) & ( io->par );
1063 
1064  int stat;
1065  double res, abserr;
1066 
1067  const double scale1 = GET_COORD_SCALE( io->par.coordSys, io->par.coord1 );
1068  const double scale2 = GET_COORD_SCALE( io->par.coordSys, io->par.coord2 );
1069  const double scale12 = scale1 * scale2;
1070 
1071  /* compute <phi_i phi_j> */
1072  integrand.function = &CW_am1_am2_Phi_i_Phi_j;
1073  stat = gsl_integration_qag( &integrand, io->ti, io->tf, epsabs, epsrel, limit, GSL_INTEG_GAUSS61, io->wksp, &res, &abserr );
1074  if ( stat != 0 ) {
1075  XLALPrintWarning( "\n%s: GSL-integration 'gsl_integration_qag()' of <Phi_i Phi_j> did not reach requested precision!\n", __func__ );
1076  XLALPrintWarning( "xlalErrno=%i, seg=%zu, av_ij_n=%g, abserr=%g\n", io->par.errnum, n, res, abserr );
1077  io->av_ij = GSL_NAN;
1078  } else {
1079  io->av_ij = scale12 * res;
1080  io->av_ij_err_sq = SQUARE( scale12 * abserr );
1081  }
1082 
1083  /* compute <phi_i> */
1084  integrand.function = &CW_Phi_i;
1085  io->par.coord = io->par.coord1;
1086  stat = gsl_integration_qag( &integrand, io->ti, io->tf, epsabs, epsrel, limit, GSL_INTEG_GAUSS61, io->wksp, &res, &abserr );
1087  if ( stat != 0 ) {
1088  XLALPrintWarning( "\n%s: GSL-integration 'gsl_integration_qag()' of <Phi_i> did not reach requested precision!\n", __func__ );
1089  XLALPrintWarning( "xlalErrno=%i, seg=%zu, av_i_n=%g, abserr=%g\n", io->par.errnum, n, res, abserr );
1090  io->av_i = GSL_NAN;
1091  } else {
1092  io->av_i = scale1 * res;
1093  io->av_i_err_sq = SQUARE( scale1 * abserr );
1094  }
1095 
1096  /* compute <phi_j> */
1097  integrand.function = &CW_Phi_i;
1098  io->par.coord = io->par.coord2;
1099  stat = gsl_integration_qag( &integrand, io->ti, io->tf, epsabs, epsrel, limit, GSL_INTEG_GAUSS61, io->wksp, &res, &abserr );
1100  if ( stat != 0 ) {
1101  XLALPrintWarning( "\n%s: GSL-integration 'gsl_integration_qag()' of <Phi_j> did not reach requested precision!\n", __func__ );
1102  XLALPrintWarning( "xlalErrno=%i, seg=%zu, av_j_n=%g, abserr=%g\n", io->par.errnum, n, res, abserr );
1103  io->av_j = GSL_NAN;
1104  } else {
1105  io->av_j = scale2 * res;
1106  io->av_j_err_sq = SQUARE( scale2 * abserr );
1107  }
1108 
1109  } /* for indx < index_max */
1110  }
1111 
1112  // restore GSL error handling
1113  gsl_set_error_handler( saveGSLErrorHandler );
1114 
1115  /* ---------- compute final result ---------- */
1116 
1117  double ret = 0, relerr_max_sq = 0;
1118 
1119  // loop over segments
1120  for ( UINT4 k = 0; k < Nseg; k ++ ) {
1121 
1122  double av_ij = 0, av_ij_err_sq = 0;
1123  double av_i = 0, av_i_err_sq = 0;
1124  double av_j = 0, av_j_err_sq = 0;
1125 
1126  // loop over detectors and integration 'units'
1127  for ( UINT4 X = 0; X < numDet; X ++ ) {
1128  for ( size_t n = 0; n < intN[k][X]; ++n ) {
1129 
1130  const InputOutputInfo *io = &intInOut[k][X][n];
1131 
1132  /* accumulate <phi_i phi_j> */
1133  av_ij += weights[X] * io->av_ij;
1134  av_ij_err_sq += weights[X] * io->av_ij_err_sq;
1135 
1136  /* accumulate <phi_i> */
1137  av_i += weights[X] * io->av_i;
1138  av_i_err_sq += weights[X] * io->av_i_err_sq;
1139 
1140  /* accumulate <phi_j> */
1141  av_j += weights[X] * io->av_j;
1142  av_j_err_sq += weights[X] * io->av_j_err_sq;
1143 
1144  } /* for n < intN */
1145 
1146  } // for X < numDet
1147 
1148  // normalise by total weight
1149  av_ij /= total_weight;
1150  av_i /= total_weight;
1151  av_j /= total_weight;
1152  av_ij_err_sq /= total_weight;
1153  av_i_err_sq /= total_weight;
1154  av_j_err_sq /= total_weight;
1155 
1156  // work out maximum relative error for this segment
1157  const double av_ij_relerr = RELERR( sqrt( av_ij_err_sq ), fabs( av_ij ) );
1158  const double av_i_relerr = RELERR( sqrt( av_i_err_sq ), fabs( av_i ) );
1159  const double av_j_relerr = RELERR( sqrt( av_j_err_sq ), fabs( av_j ) );
1160  const double relerr_max_k = MYMAX( av_ij_relerr, MYMAX( av_i_relerr, av_j_relerr ) );
1161 
1162  ret += av_ij - av_i * av_j;
1163  relerr_max_sq += SQUARE( relerr_max_k );
1164 
1165  } // for k < Nseg
1166 
1167  ret /= Nseg;
1168  relerr_max_sq /= Nseg;
1169 
1170  if ( relerr_max ) {
1171  ( *relerr_max ) = sqrt( relerr_max_sq );
1172  }
1173 
1174  /* ----- cleanup ----- */
1175 
1176  for ( size_t k = 0; k < Nseg; ++k ) {
1177  for ( size_t X = 0; X < numDet; ++X ) {
1178  for ( size_t n = 0; n < intN[k][X]; ++n ) {
1179  InputOutputInfo *io = &intInOut[k][X][n];
1180  gsl_integration_workspace_free( io->wksp );
1181  }
1182  XLALFree( intInOut[k][X] );
1183  }
1184  }
1185 
1186  return ret;
1187 
1188 } /* XLALCovariance_Phi_ij() */
1189 
1190 
1191 /**
1192  * Calculate an approximate "phase-metric" with the specified parameters.
1193  *
1194  * Note: if this function is called with multiple detectors, the phase components
1195  * are averaged over detectors as well as time. This is a somewhat ad-hoc approach;
1196  * if you want a more rigorous multi-detector metric you need to use the full
1197  * Fstat-metric, as computed by XLALComputeDopplerFstatMetric().
1198  *
1199  * Return NULL on error.
1200  */
1202 XLALComputeDopplerPhaseMetric( const DopplerMetricParams *metricParams, /**< input parameters determining the metric calculation */
1203  const EphemerisData *edat /**< ephemeris data */
1204  )
1205 {
1206  intparams_t XLAL_INIT_DECL( intparams );
1207 
1208  /* ---------- sanity/consistency checks ---------- */
1209  XLAL_CHECK_NULL( metricParams != NULL, XLAL_EINVAL );
1210  XLAL_CHECK_NULL( edat != NULL, XLAL_EINVAL );
1211  XLAL_CHECK_NULL( XLALSegListIsInitialized( &( metricParams->segmentList ) ), XLAL_EINVAL, "Passed un-initialzied segment list 'metricParams->segmentList'\n" );
1212  UINT4 Nseg = metricParams->segmentList.length;
1213 
1214  UINT4 dim = metricParams->coordSys.dim;
1215  const DopplerCoordinateSystem *coordSys = &( metricParams->coordSys );
1216  // ----- check that {n2x_equ, n2y_equ} are not used at the equator (delta=0), as metric is undefined there
1217  BOOLEAN have_n2xy = 0;
1218  for ( UINT4 i = 0; i < dim; i ++ ) {
1219  if ( ( coordSys->coordIDs[i] == DOPPLERCOORD_N2X_EQU ) || ( coordSys->coordIDs[i] == DOPPLERCOORD_N2Y_EQU ) ) {
1220  have_n2xy = 1;
1221  }
1222  }
1223  BOOLEAN at_equator = ( metricParams->signalParams.Doppler.Delta == 0 );
1224  XLAL_CHECK_NULL( !( at_equator && have_n2xy ), XLAL_EINVAL, "Can't use 'n2x_equ','n2y_equ' at equator (Delta=0): metric is singular there" );
1225 
1226  // ----- allocate output DopplerPhaseMetric() struct
1227  DopplerPhaseMetric *metric = XLALCalloc( 1, sizeof( *metric ) );
1228  XLAL_CHECK_NULL( metric != NULL, XLAL_ENOMEM );
1229 
1230  // ----- compute segment-list midTime
1231  LIGOTimeGPS midTime;
1232  {
1233  const LIGOTimeGPS *startTime = &( metricParams->segmentList.segs[0].start );
1234  const LIGOTimeGPS *endTime = &( metricParams->segmentList.segs[Nseg - 1].end );
1235  const REAL8 Tspan = XLALGPSDiff( endTime, startTime );
1236  midTime = *startTime;
1237  XLALGPSAdd( &midTime, 0.5 * Tspan );
1238  }
1239 
1240  /* ---------- prepare output metric ---------- */
1241  if ( ( metric->g_ij = gsl_matrix_calloc( dim, dim ) ) == NULL ) {
1242  XLALPrintError( "%s: gsl_matrix_calloc(%d, %d) failed.\n\n", __func__, dim, dim );
1244  }
1245 
1246  /* ---------- set up integration parameters ---------- */
1247  intparams.coordSys = coordSys;
1248  intparams.edat = edat;
1249  intparams.refTime = XLALGPSGetREAL8( &midTime ); /* always compute metric at midTime, transform to refTime later */
1250  intparams.dopplerPoint = &( metricParams->signalParams.Doppler );
1251  intparams.detMotionType = metricParams->detMotionType;
1252  intparams.approxPhase = metricParams->approxPhase;
1253  /* deactivate antenna-patterns for phase-metric */
1254  intparams.amcomp1 = AMCOMP_NONE;
1255  intparams.amcomp2 = AMCOMP_NONE;
1256 
1257  /* if using 'global correlation' frequency variables, determine the highest spindown order: */
1258  UINT4 maxorder = findHighestGCSpinOrder( coordSys );
1259  if ( maxorder > 0 ) {
1260 
1261  /* compute rOrb(t) derivatives at reference time */
1262  if ( ( intparams.rOrb_n = XLALComputeOrbitalDerivatives( maxorder, &intparams.dopplerPoint->refTime, edat ) ) == NULL ) {
1263  XLALPrintError( "%s: XLALComputeOrbitalDerivatives() failed.\n", __func__ );
1265  }
1266  if ( lalDebugLevel & LALINFOBIT ) {
1267  /* diagnostic / debug output */
1268  fprintf( stderr, "%s: rOrb_n(%d) = [ ", __func__, intparams.dopplerPoint->refTime.gpsSeconds );
1269  for ( UINT4 n = 0; n < intparams.rOrb_n->length; n++ ) {
1270  fprintf( stderr, "[%g, %g, %g]%s", intparams.rOrb_n->data[n][0], intparams.rOrb_n->data[n][1], intparams.rOrb_n->data[n][2],
1271  ( n < intparams.rOrb_n->length - 1 ) ? ", " : " ]\n" );
1272  }
1273  }
1274 
1275  }
1276 
1277  metric->maxrelerr = 0;
1278  double err = 0;
1279 
1280  /* ========== use numerically-robust method to compute metric ========== */
1281 
1282  /* allocate memory for coordinate transform */
1283  gsl_matrix *transform = gsl_matrix_alloc( dim, dim );
1284  XLAL_CHECK_NULL( transform != NULL, XLAL_ENOMEM );
1285  gsl_matrix_set_identity( transform );
1286  intparams.coordTransf = transform;
1287 
1288  for ( size_t n = 1; n <= dim; ++n ) {
1289 
1290  /* NOTE: this level of accuracy is only achievable *without* AM-coefficients involved
1291  * which are computed in REAL4 precision. For the current function this is OK, as this
1292  * function is only supposed to compute *pure* phase-derivate covariances.
1293  */
1294  intparams.epsrel = 1e-6;
1295  /* we need an abs-cutoff as well, as epsrel can be too restrictive for small integrals */
1296  intparams.epsabs = 1e-3;
1297  /* NOTE: this numerical integration still runs into problems when integrating over
1298  * long durations (~O(23d)), as the integrands are oscillatory functions on order of ~1d
1299  * and convergence degrades.
1300  * As a solution, we split the integral into 'intN' units of ~1 day duration, and compute
1301  * the final integral as a sum over partial integrals.
1302  * It is VERY important to ensure that 'intT' is not exactly 1 day, since then an integrand
1303  * with period ~1 day may integrate to zero, which is both slower and MUCH more difficult
1304  * for the numerical integration functions (since the integrand them becomes small with
1305  * respect to any integration errors).
1306  */
1307  intparams.intT = 0.9 * LAL_DAYSID_SI;
1308 
1309  /* allocate memory for Cholesky decomposition */
1310  gsl_matrix *cholesky = gsl_matrix_alloc( n, n );
1311  XLAL_CHECK_NULL( cholesky != NULL, XLAL_ENOMEM );
1312 
1313  /* create views of n-by-n submatrices of metric and coordinate transform */
1314  gsl_matrix_view g_ij_n = gsl_matrix_submatrix( metric->g_ij, 0, 0, n, n );
1315  gsl_matrix_view transform_n = gsl_matrix_submatrix( transform, 0, 0, n, n );
1316 
1317  /* try this loop a certain number of times */
1318  const size_t max_tries = 64;
1319  size_t tries = 0;
1320  while ( ++tries <= max_tries ) {
1321 
1322  /* ----- compute last row/column of n-by-n submatrix of metric ----- */
1323  for ( size_t i = 0; i < n; ++i ) {
1324  const size_t j = n - 1;
1325 
1326  /* g_ij = [Phi_i, Phi_j] */
1327  intparams.coord1 = i;
1328  intparams.coord2 = j;
1329  REAL8 gg = XLALCovariance_Phi_ij( &metricParams->multiIFO, &metricParams->multiNoiseFloor, &metricParams->segmentList,
1330  &intparams, &err );
1331  XLAL_CHECK_NULL( !gsl_isnan( gg ), XLAL_EFUNC, "%s: integration of phase metric g_{i=%zu,j=%zu} failed (n=%zu, tries=%zu)", __func__, i, j, n, tries );
1332  gsl_matrix_set( &g_ij_n.matrix, i, j, gg );
1333  gsl_matrix_set( &g_ij_n.matrix, j, i, gg );
1334  metric->maxrelerr = MYMAX( metric->maxrelerr, err );
1335 
1336  } /* for i < n */
1337 
1338  /* ----- compute L D L^T Cholesky decomposition of metric ----- */
1339  XLAL_CHECK_NULL( XLALCholeskyLDLTDecompMetric( &cholesky, &g_ij_n.matrix ) == XLAL_SUCCESS, XLAL_EFUNC );
1340  gsl_vector_view D = gsl_matrix_diagonal( cholesky );
1341  if ( ( tries > 1 ) && ( lalDebugLevel & LALINFOBIT ) ) {
1342  /* diagnostic / debug output */
1343  fprintf( stdout, "%s: n=%zu, try=%zu, Cholesky diagonal =", __func__, n, tries );
1344  XLALfprintfGSLvector( stdout, "%0.2e", &D.vector );
1345  }
1346 
1347  /* ----- check that all diagonal elements D are positive after at least 1 try; if so, exit try loop */
1348  if ( ( tries > 1 ) && ( gsl_vector_min( &D.vector ) > 0.0 ) ) {
1349  break;
1350  }
1351 
1352  /* zero out all but last row of L, since we do not want to coordinates before 'n' */
1353  if ( n > 1 ) {
1354  gsl_matrix_view cholesky_nm1 = gsl_matrix_submatrix( cholesky, 0, 0, n - 1, n );
1355  gsl_matrix_set_identity( &cholesky_nm1.matrix );
1356  }
1357 
1358  /* multiply transform by inverse of L (with unit diagonal), to transform 'n'th coordinates so that metric is diagonal */
1359  gsl_blas_dtrsm( CblasLeft, CblasLower, CblasNoTrans, CblasUnit, 1.0, cholesky, &transform_n.matrix );
1360 
1361  /* decrease relative error tolerances; don't do this too quickly, since
1362  too-stringent tolerances may make GSL integration fail to converge */
1363  intparams.epsrel = intparams.epsrel * 0.9;
1364  /* decrease absolute error tolerances; don't do this too quickly, since
1365  too-stringent tolerances may make GSL integration fail to converge,
1366  and only after a certain number of tries, otherwise small integrals
1367  fail to converge */
1368  if ( tries >= 8 ) {
1369  intparams.epsabs = intparams.epsabs * 0.9;
1370  }
1371  /* reduce the length of integration time units, but stop at 900s,
1372  and ensure that 'intT' does NOT become divisible by 1/day, for
1373  the same reason given at the initialisation of 'intT' above */
1374  intparams.intT = MYMAX( 900, intparams.intT * 0.9 );
1375 
1376  }
1377  XLAL_CHECK_NULL( tries <= max_tries, XLAL_EMAXITER, "%s: convergence of phase metric failed (n=%zu)", __func__, n );
1378 
1379  /* free memory which is then re-allocated in next loop */
1380  gsl_matrix_free( cholesky );
1381 
1382  }
1383 
1384  /* apply inverse of transform^T to metric, to get back original coordinates */
1385  gsl_matrix_transpose( transform );
1386  XLAL_CHECK_NULL( XLALInverseTransformMetric( &metric->g_ij, transform, metric->g_ij ) == XLAL_SUCCESS, XLAL_EFUNC );
1387 
1388  /* transform phase metric reference time from midTime to refTime */
1389  const REAL8 Dtau = XLALGPSDiff( &( metricParams->signalParams.Doppler.refTime ), &midTime );
1390  XLAL_CHECK_NULL( XLALChangeMetricReferenceTime( &metric->g_ij, NULL, metric->g_ij, &( metricParams->coordSys ), Dtau ) == XLAL_SUCCESS, XLAL_EFUNC );
1391 
1392  /* ----- if requested, project g_ij onto coordinate 'projectCoord' */
1393  if ( metricParams->projectCoord >= 0 ) {
1394  UINT4 projCoord = ( UINT4 )metricParams->projectCoord;
1395  if ( XLALProjectMetric( &metric->g_ij, metric->g_ij, projCoord ) != XLAL_SUCCESS ) {
1396  XLALPrintError( "%s: failed to project g_ij onto coordinate '%d'. errno=%d\n", __func__, projCoord, xlalErrno );
1398  }
1399  } /* if projectCoordinate >= 0 */
1400 
1401  /* ---- check that metric is positive definite, by checking determinants of submatrices */
1402  for ( size_t n = 1; n <= dim; ++n ) {
1403  gsl_matrix_view g_ij_n = gsl_matrix_submatrix( metric->g_ij, 0, 0, n, n );
1404  const double det_n = XLALMetricDeterminant( &g_ij_n.matrix );
1405  XLAL_CHECK_NULL( det_n > 0, XLAL_EFAILED, "%s: could not compute a positive-definite phase metric (n=%zu, det_n=%0.3e)", __func__, n, det_n );
1406  }
1407  if ( lalDebugLevel & LALINFOBIT ) {
1408  /* diagnostic / debug output */
1409  fprintf( stdout, "%s: phase metric:", __func__ );
1410  XLALfprintfGSLmatrix( stdout, "%0.15e", metric->g_ij );
1411  }
1412 
1413  /* attach the metricParams struct as 'meta-info' to the output */
1414  metric->meta = ( *metricParams );
1415 
1416  /* free memory */
1417  XLALDestroyVect3Dlist( intparams.rOrb_n );
1418  gsl_matrix_free( transform );
1419 
1420  return metric;
1421 
1422 } /* XLALComputeDopplerPhaseMetric() */
1423 
1424 
1425 /** Free a DopplerPhaseMetric structure */
1426 void
1428 {
1429  if ( !metric ) {
1430  return;
1431  }
1432 
1433  gsl_matrix_free( metric->g_ij );
1434 
1435  XLALFree( metric );
1436 
1437  return;
1438 
1439 } /* XLALDestroyDopplerPhaseMetric() */
1440 
1441 
1442 /**
1443  * Calculate the general (single-segment coherent, or multi-segment semi-coherent)
1444  * *full* (multi-IFO) Fstat-metrix and the Fisher-matrix derived in \cite Prix07 .
1445  *
1446  * The semi-coherent metrics \f$ g_{ij} \f$ over \f$ N \f$ segments are computed according to
1447  *
1448  * \f[ \overline{g}_{ij} \equiv \frac{1}{N} \sum_{k=1}^{N} g_{ij,k} \f]
1449  *
1450  * where \f$ g_{ij,k} \f$ is the coherent single-segment metric of segment k
1451  *
1452  * Note: The returned DopplerFstatMetric struct contains the matrices
1453  * g_ij (the phase metric), gF_ij (the F-metric), gFav_ij (the average F-metric),
1454  * m1_ij, m2_ij, m3_ij (auxiliary matrices)
1455  * and Fisher_ab (the full 4+n dimensional Fisher matrix).
1456  *
1457  * The returned metric struct also carries the meta-info about
1458  * the metrics in the field 'DopplerMetricParams meta'.
1459  *
1460  * Note2: for backwards-compatibility, we treat params->Nseg==0 equivalent to Nseg==1, ie
1461  * compute the coherent, single-segment metrics
1462  *
1463  * Return NULL on error.
1464  */
1466 XLALComputeDopplerFstatMetric( const DopplerMetricParams *metricParams, /**< input parameters determining the metric calculation */
1467  const EphemerisData *edat /**< ephemeris data */
1468  )
1469 {
1470  XLAL_CHECK_NULL( metricParams, XLAL_EINVAL, "Invalid NULL input 'metricParams'\n" );
1471  XLAL_CHECK_NULL( XLALSegListIsInitialized( &( metricParams->segmentList ) ), XLAL_EINVAL, "Passed un-initialzied segment list 'metricParams->segmentList'\n" );
1472 
1473  UINT4 dim = metricParams->coordSys.dim;
1474  const DopplerCoordinateSystem *coordSys = &( metricParams->coordSys );
1475  // ----- check that {n2x_equ, n2y_equ} are not used at the equator (delta=0), as metric is undefined there
1476  BOOLEAN have_n2xy = 0;
1477  for ( UINT4 i = 0; i < dim; i ++ ) {
1478  if ( ( coordSys->coordIDs[i] == DOPPLERCOORD_N2X_EQU ) || ( coordSys->coordIDs[i] == DOPPLERCOORD_N2Y_EQU ) ) {
1479  have_n2xy = 1;
1480  }
1481  }
1482  BOOLEAN at_equator = ( metricParams->signalParams.Doppler.Delta == 0 );
1483  XLAL_CHECK_NULL( !( at_equator && have_n2xy ), XLAL_EINVAL, "Can't use 'n2x_equ','n2y_equ' at equator (Delta=0): metric is singular there" );
1484 
1485  UINT4 Nseg = metricParams->segmentList.length; // number of semi-coherent segments to average metrics over
1486  XLAL_CHECK_NULL( Nseg >= 1, XLAL_EINVAL, "Got empty segment list metricParams->segmentList, needs to contain at least 1 segments\n" );
1487 
1488  DopplerFstatMetric *metric = NULL;
1489 
1490  LALSegList segList_k;
1491  LALSeg segment_k;
1492  XLALSegListInit( &segList_k ); // prepare single-segment list containing segment k
1493  segList_k.arraySize = 1;
1494  segList_k.length = 1;
1495  segList_k.segs = &segment_k;
1496 
1497  DopplerMetricParams metricParams_k = ( *metricParams ); // *copy* of input parameters to be used for single-segment coherent metrics
1498  metricParams_k.segmentList = segList_k;
1499 
1500  for ( UINT4 k = 0; k < Nseg; k ++ ) {
1501  DopplerFstatMetric *metric_k; // per-segment coherent metric
1502  FmetricAtoms_t *atoms = NULL;
1503 
1504  // setup 1-segment segment-list pointing k-th segment
1505  metricParams_k.segmentList.segs[0] = metricParams->segmentList.segs[k];
1506 
1507  /* ---------- compute Fmetric 'atoms', ie the averaged <a^2>, <a b Phi_i>, <a^2 Phi_i Phi_j>, etc ---------- */
1508  if ( ( atoms = XLALComputeAtomsForFmetric( &metricParams_k, edat ) ) == NULL ) {
1509  XLALPrintError( "%s: XLALComputeAtomsForFmetric() failed. errno = %d\n\n", __func__, xlalErrno );
1511  }
1512 
1513  /* ----- compute the F-metric gF_ij and related matrices ---------- */
1514  REAL8 h0 = metricParams->signalParams.Amp.aPlus + sqrt( POW2( metricParams->signalParams.Amp.aPlus ) - POW2( metricParams->signalParams.Amp.aCross ) );
1515  REAL8 cosi;
1516  if ( h0 > 0 ) {
1517  cosi = metricParams->signalParams.Amp.aCross / h0;
1518  } else {
1519  cosi = 0;
1520  }
1521  REAL8 psi = metricParams->signalParams.Amp.psi;
1522 
1523  if ( ( metric_k = XLALComputeFmetricFromAtoms( atoms, cosi, psi ) ) == NULL ) {
1524  XLALPrintError( "%s: XLALComputeFmetricFromAtoms() failed, errno = %d\n\n", __func__, xlalErrno );
1525  XLALDestroyFmetricAtoms( atoms );
1527  }
1528 
1529  /* ----- compute the full 4+n dimensional Fisher matrix ---------- */
1530  if ( ( metric_k->Fisher_ab = XLALComputeFisherFromAtoms( atoms, metricParams->signalParams.Amp ) ) == NULL ) {
1531  XLALPrintError( "%s: XLALComputeFisherFromAtoms() failed. errno = %d\n\n", __func__, xlalErrno );
1532  XLALDestroyFmetricAtoms( atoms );
1535  }
1536 
1537  XLALDestroyFmetricAtoms( atoms );
1538 
1539  /* ---- add DopplerFstatMetric results over segments ----- */
1540  if ( metric == NULL ) {
1541  metric = metric_k; // just use DopplerFstatMetric from 1st segment
1542  } else {
1543 
1544  // add matrices
1545  gsl_matrix_add( metric->gF_ij, metric_k->gF_ij );
1546  gsl_matrix_add( metric->gFav_ij, metric_k->gFav_ij );
1547  gsl_matrix_add( metric->m1_ij, metric_k->m1_ij );
1548  gsl_matrix_add( metric->m2_ij, metric_k->m2_ij );
1549  gsl_matrix_add( metric->m3_ij, metric_k->m3_ij );
1550  gsl_matrix_add( metric->Fisher_ab, metric_k->Fisher_ab );
1551 
1552  // add errors in quadrature
1553  metric->maxrelerr = sqrt( SQUARE( metric->maxrelerr ) + SQUARE( metric_k->maxrelerr ) );
1554 
1555  // add SNR^2
1556  metric->rho2 += metric_k->rho2;
1557 
1558  XLALDestroyDopplerFstatMetric( metric_k );
1559 
1560  }
1561 
1562  } // for k < Nseg
1563 
1564  // semi-coherent metric: <g_ij> = (1/Nseg) sum_{k=1}^Nseg g_{k,ij}
1565  {
1566  const double scale = 1.0 / Nseg;
1567 
1568  // scale matrices
1569  gsl_matrix_scale( metric->gF_ij, scale );
1570  gsl_matrix_scale( metric->gFav_ij, scale );
1571  gsl_matrix_scale( metric->m1_ij, scale );
1572  gsl_matrix_scale( metric->m2_ij, scale );
1573  gsl_matrix_scale( metric->m3_ij, scale );
1574  gsl_matrix_scale( metric->Fisher_ab, scale );
1575 
1576  // scale errors
1577  metric->maxrelerr *= scale;
1578 
1579  // scale SNR^2
1580  metric->rho2 *= scale;
1581  }
1582 
1583  /* ----- if requested, project gF_ij and gFav_ij onto coordinate 'projectCoord' */
1584  if ( metricParams->projectCoord >= 0 ) {
1585  UINT4 projCoord = ( UINT4 )metricParams->projectCoord;
1586  if ( XLALProjectMetric( &metric->gF_ij, metric->gF_ij, projCoord ) != XLAL_SUCCESS ) {
1587  XLALPrintError( "%s: failed to project gF_ij onto coordinate '%d'. errno=%d\n", __func__, projCoord, xlalErrno );
1589  }
1590  if ( XLALProjectMetric( &metric->gFav_ij, metric->gFav_ij, projCoord ) != XLAL_SUCCESS ) {
1591  XLALPrintError( "%s: failed to project gFav_ij onto coordinate '%d'. errno=%d\n", __func__, projCoord, xlalErrno );
1593  }
1594  } /* if projectCoordinate >= 0 */
1595 
1596  /* attach the metricParams struct as 'meta-info' to the output */
1597  metric->meta = ( *metricParams );
1598 
1599  return metric;
1600 
1601 } /* XLALComputeDopplerFstatMetric() */
1602 
1603 
1604 /** Free a DopplerFstatMetric structure */
1605 void
1607 {
1608  if ( !metric ) {
1609  return;
1610  }
1611 
1612  gsl_matrix_free( metric->gF_ij );
1613  gsl_matrix_free( metric->gFav_ij );
1614  gsl_matrix_free( metric->m1_ij );
1615  gsl_matrix_free( metric->m2_ij );
1616  gsl_matrix_free( metric->m3_ij );
1617  gsl_matrix_free( metric->Fisher_ab );
1618 
1619  XLALFree( metric );
1620 
1621  return;
1622 
1623 } /* XLALDestroyDopplerFstatMetric() */
1624 
1625 
1626 /**
1627  * Function to the compute the FmetricAtoms_t, from which the F-metric and Fisher-matrix can be computed.
1628  *
1629  * NOTE: if MultiNoiseFloor.length=0, unit noise weights are assumed.
1630  */
1632 XLALComputeAtomsForFmetric( const DopplerMetricParams *metricParams, /**< input parameters determining the metric calculation */
1633  const EphemerisData *edat /**< ephemeris data */
1634  )
1635 {
1636  FmetricAtoms_t *ret; /* return struct */
1637  intparams_t XLAL_INIT_DECL( intparams );
1638 
1639  UINT4 dim, i = -1, j = -1, X; /* index counters */
1640  REAL8 A, B, C; /* antenna-pattern coefficients (gsl-integrated) */
1641 
1642  const DopplerCoordinateSystem *coordSys;
1643 
1644  REAL8 max_relerr = 0;
1645  REAL8 relerr_thresh = 1e-2; /* relatively tolerant integration-threshold */
1646 
1647  /* ---------- sanity/consistency checks ---------- */
1648  if ( !metricParams || !edat ) {
1649  XLALPrintError( "\n%s: Illegal NULL pointer passed!\n\n", __func__ );
1651  }
1652  UINT4 numDet = metricParams->multiIFO.length;
1654  XLAL_CHECK_NULL( XLALSegListIsInitialized( &( metricParams->segmentList ) ), XLAL_EINVAL, "Passed un-initialzied segment list 'metricParams->segmentList'\n" );
1655  UINT4 Nseg = metricParams->segmentList.length;
1656  XLAL_CHECK_NULL( Nseg == 1, XLAL_EINVAL, "Segment list must only contain Nseg=1 segments, got Nseg=%d", Nseg );
1657 
1658  BOOLEAN haveNoiseWeights = ( metricParams->multiNoiseFloor.length > 0 );
1659  XLAL_CHECK_NULL( !haveNoiseWeights || metricParams->multiNoiseFloor.length == numDet, XLAL_EINVAL );
1660 
1661  LIGOTimeGPS *startTime = &( metricParams->segmentList.segs[0].start );
1662  LIGOTimeGPS *endTime = &( metricParams->segmentList.segs[0].end );
1663  REAL8 Tspan = XLALGPSDiff( endTime, startTime );
1664 
1665  const LIGOTimeGPS *refTime = &( metricParams->signalParams.Doppler.refTime );
1666 
1667  dim = metricParams->coordSys.dim; /* shorthand: number of Doppler dimensions */
1668  coordSys = &( metricParams->coordSys );
1669 
1670  /* ----- create output structure ---------- */
1671  XLAL_CHECK_NULL( ( ret = XLALCalloc( 1, sizeof( *ret ) ) ) != NULL, XLAL_ENOMEM, "%s: XLALCalloc(1,%zu) failed.\n", __func__, sizeof( *ret ) );
1672  XLAL_CHECK_NULL( ( ret->a_a_i = gsl_vector_calloc( dim ) ) != NULL, XLAL_ENOMEM, "%s: a_a_i = gsl_vector_calloc (%d) failed.\n", __func__, dim );
1673  XLAL_CHECK_NULL( ( ret->a_b_i = gsl_vector_calloc( dim ) ) != NULL, XLAL_ENOMEM, "%s: a_b_i = gsl_vector_calloc (%d) failed.\n", __func__, dim );
1674  XLAL_CHECK_NULL( ( ret->b_b_i = gsl_vector_calloc( dim ) ) != NULL, XLAL_ENOMEM, "%s: b_b_i = gsl_vector_calloc (%d) failed.\n", __func__, dim );
1675  XLAL_CHECK_NULL( ( ret->a_a_i_j = gsl_matrix_calloc( dim, dim ) ) != NULL, XLAL_ENOMEM, "%s: a_a_i_j = gsl_matrix_calloc (%d,%d) failed.\n", __func__, dim, dim );
1676  XLAL_CHECK_NULL( ( ret->a_b_i_j = gsl_matrix_calloc( dim, dim ) ) != NULL, XLAL_ENOMEM, "%s: a_b_i_j = gsl_matrix_calloc (%d,%d) failed.\n", __func__, dim, dim );
1677  XLAL_CHECK_NULL( ( ret->b_b_i_j = gsl_matrix_calloc( dim, dim ) ) != NULL, XLAL_ENOMEM, "%s: b_b_i_j = gsl_matrix_calloc (%d,%d) failed.\n", __func__, dim, dim );
1678 
1679  /* ---------- set up integration parameters ---------- */
1680  intparams.coordSys = coordSys;
1681  intparams.detMotionType = metricParams->detMotionType;
1682  intparams.dopplerPoint = &metricParams->signalParams.Doppler;
1683  intparams.startTime = XLALGPSGetREAL8( startTime );
1684  intparams.refTime = XLALGPSGetREAL8( refTime );
1685  intparams.Tspan = Tspan;
1686  intparams.edat = edat;
1687 
1688  /* NOTE: this level of accuracy should be compatible with AM-coefficients involved
1689  * which are computed in REAL4 precision. We therefor cannot go lower than this it seems,
1690  * otherwise the gsl-integration fails to converge in some cases.
1691  */
1692  intparams.epsrel = 1e-4;
1693  intparams.epsabs = 0;
1694  /* NOTE: this numerical integration runs into problems when integrating over
1695  * several days (~O(5d)), as the integrands are oscillatory functions on order of ~1/4d
1696  * and convergence degrades.
1697  * As a solution, we split the integral into 'intN' units of 1/4 day duration, and compute
1698  * the final integral as a sum over partial integrals.
1699  */
1700  intparams.intT = 0.25 * LAL_DAYSID_SI;
1701 
1702  /* if using 'global correlation' frequency variables, determine the highest spindown order: */
1703  UINT4 maxorder = findHighestGCSpinOrder( coordSys );
1704  if ( maxorder > 0 ) {
1705 
1706  /* compute rOrb(t) derivatives at reference time */
1707  if ( ( intparams.rOrb_n = XLALComputeOrbitalDerivatives( maxorder, &intparams.dopplerPoint->refTime, edat ) ) == NULL ) {
1708  XLALPrintError( "%s: XLALComputeOrbitalDerivatives() failed.\n", __func__ );
1710  }
1711 
1712  }
1713 
1714  /* ----- integrate antenna-pattern coefficients A, B, C */
1715  REAL8 sum_weights = 0;
1716  A = B = C = 0;
1717  for ( X = 0; X < numDet; X ++ ) {
1718  REAL8 weight = haveNoiseWeights ? metricParams->multiNoiseFloor.sqrtSn[X] : 1.0;
1719  sum_weights += weight;
1720  REAL8 av, relerr;
1721  intparams.site = &( metricParams->multiIFO.sites[X] );
1722 
1723  intparams.coord1 = -1;
1724  intparams.coord2 = -1;
1725 
1726  /* A = < a^2 > (67)*/
1727  intparams.amcomp1 = AMCOMP_A;
1728  intparams.amcomp2 = AMCOMP_A;
1729  av = XLALAverage_am1_am2_Phi_i_Phi_j( &intparams, &relerr );
1730  max_relerr = MYMAX( max_relerr, relerr );
1731  if ( xlalErrno ) {
1732  goto failed;
1733  }
1734  A += weight * av;
1735 
1736  /* B = < b^2 > (67) */
1737  intparams.amcomp1 = AMCOMP_B;
1738  intparams.amcomp2 = AMCOMP_B;
1739  av = XLALAverage_am1_am2_Phi_i_Phi_j( &intparams, &relerr );
1740  max_relerr = MYMAX( max_relerr, relerr );
1741  if ( xlalErrno ) {
1742  goto failed;
1743  }
1744  B += weight * av;
1745 
1746  /* C = < a b > (67) */
1747  intparams.amcomp1 = AMCOMP_A;
1748  intparams.amcomp2 = AMCOMP_B;
1749  av = XLALAverage_am1_am2_Phi_i_Phi_j( &intparams, &relerr );
1750  max_relerr = MYMAX( max_relerr, relerr );
1751  if ( xlalErrno ) {
1752  goto failed;
1753  }
1754  C += weight * av;
1755 
1756  } /* for X < numDetectors */
1757 
1758  REAL8 norm_weight = 1.0 / sum_weights;
1759 
1760  A *= norm_weight;
1761  B *= norm_weight;
1762  C *= norm_weight;
1763 
1764  ret->a_a = A;
1765  ret->b_b = B;
1766  ret->a_b = C;
1767 
1768  /* ---------- compute components of the phase-metric ---------- */
1769  for ( i = 0; i < dim; i ++ ) {
1770  for ( j = 0; j <= i; j ++ ) {
1771  REAL8 a_a_i_j, b_b_i_j, a_b_i_j;
1772  REAL8 a_a_i, b_b_i, a_b_i;
1773 
1774  a_a_i_j = b_b_i_j = a_b_i_j = 0;
1775  a_a_i = b_b_i = a_b_i = 0;
1776 
1777  for ( X = 0; X < numDet; X ++ ) {
1778  REAL8 weight = haveNoiseWeights ? metricParams->multiNoiseFloor.sqrtSn[X] : 1.0;
1779  REAL8 av, relerr;
1780  intparams.site = &( metricParams->multiIFO.sites[X] );
1781 
1782  /* ------------------------------ */
1783  intparams.coord1 = i;
1784  intparams.coord2 = j;
1785 
1786  /* <a^2 Phi_i Phi_j> */
1787  intparams.amcomp1 = AMCOMP_A;
1788  intparams.amcomp2 = AMCOMP_A;
1789  av = XLALAverage_am1_am2_Phi_i_Phi_j( &intparams, &relerr );
1790  max_relerr = MYMAX( max_relerr, relerr );
1791  if ( xlalErrno ) {
1792  goto failed;
1793  }
1794  a_a_i_j += weight * av;
1795 
1796  /* <b^2 Phi_i Phi_j> */
1797  intparams.amcomp1 = AMCOMP_B;
1798  intparams.amcomp2 = AMCOMP_B;
1799  av = XLALAverage_am1_am2_Phi_i_Phi_j( &intparams, &relerr );
1800  max_relerr = MYMAX( max_relerr, relerr );
1801  if ( xlalErrno ) {
1802  goto failed;
1803  }
1804  b_b_i_j += weight * av;
1805 
1806  /* <a b Phi_i Phi_j> */
1807  intparams.amcomp1 = AMCOMP_A;
1808  intparams.amcomp2 = AMCOMP_B;
1809  av = XLALAverage_am1_am2_Phi_i_Phi_j( &intparams, &relerr );
1810  max_relerr = MYMAX( max_relerr, relerr );
1811  if ( xlalErrno ) {
1812  goto failed;
1813  }
1814  a_b_i_j += weight * av;
1815 
1816  /* ------------------------------ */
1817  intparams.coord1 = i;
1818  intparams.coord2 = -1;
1819 
1820  /* <a^2 Phi_i> */
1821  intparams.amcomp1 = AMCOMP_A;
1822  intparams.amcomp2 = AMCOMP_A;
1823  av = XLALAverage_am1_am2_Phi_i_Phi_j( &intparams, &relerr );
1824  max_relerr = MYMAX( max_relerr, relerr );
1825  if ( xlalErrno ) {
1826  goto failed;
1827  }
1828  a_a_i += weight * av;
1829 
1830  /* <b^2 Phi_i> */
1831  intparams.amcomp1 = AMCOMP_B;
1832  intparams.amcomp2 = AMCOMP_B;
1833  av = XLALAverage_am1_am2_Phi_i_Phi_j( &intparams, &relerr );
1834  max_relerr = MYMAX( max_relerr, relerr );
1835  if ( xlalErrno ) {
1836  goto failed;
1837  }
1838  b_b_i += weight * av;
1839 
1840  /* <a b Phi_i> */
1841  intparams.amcomp1 = AMCOMP_A;
1842  intparams.amcomp2 = AMCOMP_B;
1843  av = XLALAverage_am1_am2_Phi_i_Phi_j( &intparams, &relerr );
1844  max_relerr = MYMAX( max_relerr, relerr );
1845  if ( xlalErrno ) {
1846  goto failed;
1847  }
1848  a_b_i += weight * av;
1849 
1850  } /* for X < numDetectors */
1851 
1852  gsl_vector_set( ret->a_a_i, i, a_a_i * norm_weight );
1853  gsl_vector_set( ret->a_b_i, i, a_b_i * norm_weight );
1854  gsl_vector_set( ret->b_b_i, i, b_b_i * norm_weight );
1855 
1856  gsl_matrix_set( ret->a_a_i_j, i, j, a_a_i_j * norm_weight );
1857  gsl_matrix_set( ret->a_a_i_j, j, i, a_a_i_j * norm_weight );
1858 
1859  gsl_matrix_set( ret->a_b_i_j, i, j, a_b_i_j * norm_weight );
1860  gsl_matrix_set( ret->a_b_i_j, j, i, a_b_i_j * norm_weight );
1861 
1862  gsl_matrix_set( ret->b_b_i_j, i, j, b_b_i_j * norm_weight );
1863  gsl_matrix_set( ret->b_b_i_j, j, i, b_b_i_j * norm_weight );
1864 
1865  } /* for j <= i */
1866 
1867  } /* for i < dim */
1868 
1869  /* return error-estimate */
1870  ret->maxrelerr = max_relerr;
1871 
1872  /* free memory */
1873  XLALDestroyVect3Dlist( intparams.rOrb_n );
1874 
1875  /* FIXME: should probably not be hardcoded */
1876  if ( max_relerr > relerr_thresh ) {
1877  XLALPrintError( "Maximal relative F-metric error too high: %.2e > %.2e\n", max_relerr, relerr_thresh );
1878  XLALDestroyFmetricAtoms( ret );
1880  } else {
1881  XLALPrintInfo( "\nMaximal relative error in F-metric: %.2e\n", max_relerr );
1882  }
1883 
1884  return ret;
1885 
1886 failed:
1887  XLALDestroyFmetricAtoms( ret );
1888  XLALPrintError( "%s: XLALAverage_am1_am2_Phi_i_Phi_j() FAILED with errno = %d: am1 = %d, am2 = %d, coord1 = '%s', coord2 = '%s'\n",
1889  __func__, xlalErrno, intparams.amcomp1, intparams.amcomp2,
1890  GET_COORD_NAME( intparams.coordSys, intparams.coord1 ), GET_COORD_NAME( intparams.coordSys, intparams.coord2 )
1891  );
1893 
1894 } /* XLALComputeAtomsForFmetric() */
1895 
1896 
1897 /**
1898  * Parse a detector-motion type string into the corresponding enum-number,
1899  */
1900 int
1901 XLALParseDetectorMotionString( const CHAR *detMotionString )
1902 {
1903 
1904  XLAL_CHECK( detMotionString, XLAL_EINVAL );
1905 
1906  for ( int i = 0; DetectorMotionNames[i].type > 0; ++i ) {
1907  if ( strcmp( detMotionString, DetectorMotionNames[i].name ) != 0 ) {
1908  continue;
1909  }
1910  return DetectorMotionNames[i].type; /* found the right entry */
1911  }
1912 
1913  XLAL_ERROR( XLAL_EINVAL, "Could not parse '%s' into a valid detector-motion type!", detMotionString );
1914 
1915 } /* XLALParseDetectorMotionString() */
1916 
1917 
1918 /**
1919  * Provide a pointer to a static string containing the DopplerCoordinate-name
1920  * cooresponding to the enum DopplerCoordinateID
1921  */
1922 const CHAR *
1924 {
1925 
1926  for ( int i = 0; DetectorMotionNames[i].type > 0; ++i ) {
1927  if ( DetectorMotionNames[i].type != detMotionType ) {
1928  continue;
1929  }
1930  return DetectorMotionNames[i].name; /* found the right entry */
1931  }
1932 
1933  XLAL_ERROR_NULL( XLAL_EINVAL, "Could not parse '%d' into a valid detector-motion name!", detMotionType );
1934 
1935 } /* XLALDetectorMotionName() */
1936 
1937 
1938 
1939 /**
1940  * Parse a DopplerCoordinate-name into the corresponding DopplerCoordinateID
1941  */
1942 int
1944 {
1945  int i;
1946 
1947  if ( !coordName ) {
1949  }
1950 
1951  for ( i = 0; i < DOPPLERCOORD_LAST; i ++ ) {
1952  if ( DopplerCoordinates[i].name && strcmp( coordName, DopplerCoordinates[i].name ) ) {
1953  continue;
1954  }
1955  return i; /* found the right entry */
1956  }
1957 
1958  XLALPrintError( "\nCould not parse '%s' into a valid coordinate-ID!\n\n", coordName );
1960 
1961 } /* XLALParseDopplerCoordinateString() */
1962 
1963 /**
1964  * Given a LALStringVector of coordinate-names, parse them into a
1965  * 'DopplerCoordinateSystem', namely a list of coordinate-IDs
1966  */
1967 int
1968 XLALDopplerCoordinateNames2System( DopplerCoordinateSystem *coordSys, /**< [out] pointer to coord-system struct */
1969  const LALStringVector *coordNames /**< [in] list of coordinate names */
1970  )
1971 {
1972  UINT4 i;
1973 
1974  if ( !coordSys || !coordNames ) {
1976  }
1977 
1978  coordSys->dim = coordNames->length;
1979  for ( i = 0; i < coordNames->length; i++ ) {
1980  coordSys->coordIDs[i] = ( DopplerCoordinateID )XLALParseDopplerCoordinateString( coordNames->data[i] );
1981  if ( xlalErrno ) {
1983  }
1984  }
1985 
1986  return XLAL_SUCCESS;
1987 
1988 } /* XLALDopplerCoordinateNames2System() */
1989 
1990 
1991 
1992 /**
1993  * Given a coordinate ID 'coordID', return its dimension within the given coordinate system 'coordSys',
1994  * or return -1 if 'coordID' is not found
1995  */
1997 {
1998  for ( int i = 0; i < ( ( int )coordSys->dim ); ++i ) {
1999  if ( coordSys->coordIDs[i] == coordID ) {
2000  return i;
2001  }
2002  }
2003  return -1;
2004 }
2005 
2006 
2007 
2008 /**
2009  * Provide a pointer to a static string containing the DopplerCoordinate-name
2010  * cooresponding to the enum DopplerCoordinateID
2011  */
2012 const CHAR *
2014 {
2015  if ( coordID >= DOPPLERCOORD_LAST ) {
2016  XLAL_ERROR_NULL( XLAL_EINVAL, "coordID '%d' outside valid range [0, %d]\n\n", coordID, DOPPLERCOORD_LAST - 1 );
2017  }
2018 
2019  if ( !DopplerCoordinates[coordID].name ) {
2020  XLAL_ERROR_NULL( XLAL_EINVAL, "coordID '%d' has no associated name\n\n", coordID );
2021  }
2022 
2023  return ( DopplerCoordinates[coordID].name );
2024 
2025 } /* XLALDopplerCoordinateName() */
2026 
2027 
2028 /**
2029  * Provide a pointer to a static string containing the a descriptive
2030  * 'help-string' describing the coordinate DopplerCoordinateID
2031  */
2032 const CHAR *
2034 {
2035  if ( coordID >= DOPPLERCOORD_LAST ) {
2036  XLAL_ERROR_NULL( XLAL_EINVAL, "coordID '%d' outside valid range [0, %d]\n\n", coordID, DOPPLERCOORD_LAST - 1 );
2037  }
2038 
2039  if ( !DopplerCoordinates[coordID].help ) {
2040  XLAL_ERROR_NULL( XLAL_EINVAL, "coordID '%d' has no associated help text\n\n", coordID );
2041  }
2042 
2043  return ( DopplerCoordinates[coordID].help );
2044 
2045 } /* XLALDopplerCoordinateHelp() */
2046 
2047 /**
2048  * Return a string (allocated here) containing a full name - helpstring listing
2049  * for all doppler-coordinates DopplerCoordinateID allowed by UniversalDopplerMetric.c
2050  */
2051 CHAR *
2053 {
2054  CHAR *helpstr;
2055  const CHAR *name;
2056  const CHAR *help;
2057  UINT4 i, len, maxlen = 0;
2058  CHAR buf[512];
2059  CHAR fmt[512];
2060 
2061 #define HEADER "Doppler-coordinate names and explanations:\n--------------------------------------------------\n"
2062  if ( ( helpstr = XLALCalloc( strlen( HEADER ) + 1, sizeof( CHAR ) ) ) == NULL ) {
2064  }
2065  strcpy( helpstr, HEADER );
2066  len = strlen( helpstr );
2067 
2068  /* get maximal field-length of coordinate names */
2069  for ( i = 0; i < DOPPLERCOORD_LAST; i ++ ) {
2070  if ( ( name = XLALDopplerCoordinateName( ( DopplerCoordinateID ) i ) ) == NULL ) {
2072  }
2073  maxlen = MYMAX( maxlen, strlen( name ) );
2074  sprintf( fmt, "%%-%ds: %%s\n", maxlen + 2 );
2075  }
2076 
2077  /* assemble help-lines */
2078  for ( i = 0; i < DOPPLERCOORD_LAST; i ++ ) {
2079  if ( ( name = XLALDopplerCoordinateName( ( DopplerCoordinateID ) i ) ) == NULL ) {
2081  }
2082  if ( ( help = XLALDopplerCoordinateHelp( ( DopplerCoordinateID ) i ) ) == NULL ) {
2084  }
2085 
2086  snprintf( buf, sizeof( buf ) - 1, fmt, name, help );
2087  len += strlen( buf ) + 1;
2088  if ( ( helpstr = XLALRealloc( helpstr, len ) ) == NULL ) {
2090  }
2091 
2092  helpstr = strcat( helpstr, buf );
2093 
2094  } /* for i < DOPPLERCOORD_LAST */
2095 
2096  return ( helpstr );
2097 
2098 } /* XLALDopplerCoordinateHelpAll() */
2099 
2100 /**
2101  * Free a FmetricAtoms_t structure, allowing any pointers to be NULL
2102  */
2103 void
2105 {
2106  if ( !atoms ) {
2107  return;
2108  }
2109 
2110  if ( atoms->a_a_i ) {
2111  gsl_vector_free( atoms->a_a_i );
2112  }
2113  if ( atoms->a_b_i ) {
2114  gsl_vector_free( atoms->a_b_i );
2115  }
2116  if ( atoms->b_b_i ) {
2117  gsl_vector_free( atoms->b_b_i );
2118  }
2119 
2120  if ( atoms->a_a_i_j ) {
2121  gsl_matrix_free( atoms->a_a_i_j );
2122  }
2123  if ( atoms->a_b_i_j ) {
2124  gsl_matrix_free( atoms->a_b_i_j );
2125  }
2126  if ( atoms->b_b_i_j ) {
2127  gsl_matrix_free( atoms->b_b_i_j );
2128  }
2129 
2130  XLALFree( atoms );
2131 
2132  return;
2133 
2134 } /* XLALDestroyFmetricAtoms() */
2135 
2136 
2137 /**
2138  * Compute the 'F-metric' gF_ij (and also gFav_ij, m1_ij, m2_ij, m3_ij)
2139  * from the given FmetricAtoms and the signal amplitude parameters.
2140  *
2141  */
2142 static DopplerFstatMetric *
2144 {
2145  DopplerFstatMetric *metric; /* output matrix */
2146 
2147  UINT4 dim, i, j; /* Doppler index counters */
2148  REAL8 A, B, C, D; /* 'standard' antenna-pattern coefficients (gsl-integrated, though) */
2149  REAL8 alpha1, alpha2, alpha3, eta2, cos2psi, sin2psi;
2150 
2151  if ( !atoms ) {
2152  XLALPrintError( "%s: illegal NULL input.\n\n", __func__ );
2154  }
2155 
2156  if ( !atoms->a_a_i || !atoms->a_b_i || !atoms->b_b_i || !atoms->a_a_i_j || !atoms->a_b_i_j || !atoms->b_b_i_j ) {
2157  XLALPrintError( "%s: input Fmetric-atoms not fully allocated.\n\n", __func__ );
2159  }
2160 
2161  dim = atoms->a_a_i->size;
2162 
2163  /* allocate output metric structure */
2164  if ( ( metric = XLALCalloc( 1, sizeof( *metric ) ) ) == NULL ) {
2165  XLALPrintError( "%s: XLALCalloc ( 1, %zu) failed.\n\n", __func__, sizeof( *metric ) );
2167  }
2168  metric->gF_ij = gsl_matrix_calloc( dim, dim );
2169  metric->gFav_ij = gsl_matrix_calloc( dim, dim );
2170  metric->m1_ij = gsl_matrix_calloc( dim, dim );
2171  metric->m2_ij = gsl_matrix_calloc( dim, dim );
2172  metric->m3_ij = gsl_matrix_calloc( dim, dim );
2173 
2174  if ( !metric->gF_ij || !metric->gFav_ij || !metric->m1_ij || !metric->m2_ij || !metric->m3_ij ) {
2175  XLALPrintError( "%s: failed to gsl_matrix_calloc(%d,%d) for gF_ij, gFav_ij, m1_ij, m2_ij, m3_ij\n\n", __func__, dim, dim );
2178  }
2179 
2180  A = atoms->a_a;
2181  B = atoms->b_b;
2182  C = atoms->a_b;
2183 
2184  D = A * B - C * C; /* determinant of [A, C; C, B] */
2185 
2186  /* get amplitude-parameter factors alpha_1, alpha_2, alpha_3 */
2187  eta2 = SQUARE( cosi );
2188  cos2psi = cos( 2.0 * psi );
2189  sin2psi = sin( 2.0 * psi );
2190  alpha1 = 0.25 * SQUARE( 1.0 + eta2 ) * SQUARE( cos2psi ) + eta2 * SQUARE( sin2psi );
2191  alpha2 = 0.25 * SQUARE( 1.0 + eta2 ) * SQUARE( sin2psi ) + eta2 * SQUARE( cos2psi );
2192  alpha3 = 0.25 * SQUARE( 1.0 - eta2 ) * sin2psi * cos2psi;
2193 
2194  metric->rho2 = alpha1 * A + alpha2 * B + 2.0 * alpha3 * C;
2195 
2196  metric->maxrelerr = atoms->maxrelerr;
2197 
2198  /* ---------- compute components of the metric ---------- */
2199  for ( i = 0; i < dim; i ++ ) {
2200  REAL8 a_a_i, b_b_i, a_b_i;
2201 
2202  a_a_i = gsl_vector_get( atoms->a_a_i, i );
2203  a_b_i = gsl_vector_get( atoms->a_b_i, i );
2204  b_b_i = gsl_vector_get( atoms->b_b_i, i );
2205 
2206  for ( j = 0; j <= i; j ++ ) {
2207  REAL8 a_a_i_j, b_b_i_j, a_b_i_j;
2208  REAL8 a_a_j, b_b_j, a_b_j;
2209 
2210  REAL8 P1_ij, P2_ij, P3_ij; /* ingredients to m_r_ij */
2211  REAL8 Q1_ij, Q2_ij, Q3_ij; /* ingredients to m_r_ij */
2212  REAL8 gg;
2213 
2214  a_a_j = gsl_vector_get( atoms->a_a_i, j );
2215  a_b_j = gsl_vector_get( atoms->a_b_i, j );
2216  b_b_j = gsl_vector_get( atoms->b_b_i, j );
2217 
2218  a_a_i_j = gsl_matrix_get( atoms->a_a_i_j, i, j );
2219  a_b_i_j = gsl_matrix_get( atoms->a_b_i_j, i, j );
2220  b_b_i_j = gsl_matrix_get( atoms->b_b_i_j, i, j );
2221 
2222  /* trivial assignments, see Eq.(76) in \cite Prix07 */
2223  P1_ij = a_a_i_j;
2224  P2_ij = b_b_i_j;
2225  P3_ij = a_b_i_j;
2226 
2227  /* bit more involved, see Eq.(80)-(82) in \cite Prix07 [includes *explicit* index-symmetrization!!] */
2228  Q1_ij = A * a_b_i * a_b_j + B * a_a_i * a_a_j - C * ( a_a_i * a_b_j + a_a_j * a_b_i ); /* (80) symmetrized */
2229  Q1_ij /= D;
2230 
2231  Q2_ij = A * b_b_i * b_b_j + B * a_b_i * a_b_j - C * ( a_b_i * b_b_j + a_b_j * b_b_i ); /* (81) symmetrized */
2232  Q2_ij /= D;
2233 
2234  Q3_ij = 0.5 * A * ( a_b_i * b_b_j + a_b_j * b_b_i )
2235  + 0.5 * B * ( a_b_i * a_a_j + a_b_j * a_a_i )
2236  - 0.5 * C * ( b_b_i * a_a_j + b_b_j * a_a_i + 2.0 * a_b_i * a_b_j ); /* (83) symmetrized */
2237  Q3_ij /= D;
2238 
2239  /* put the pieces together to compute m1_ij, m2_ij and m3_ij according to (85) */
2240  gg = P1_ij - Q1_ij;
2241  gsl_matrix_set( metric->m1_ij, i, j, gg );
2242  gsl_matrix_set( metric->m1_ij, j, i, gg );
2243 
2244  gg = P2_ij - Q2_ij;
2245  gsl_matrix_set( metric->m2_ij, i, j, gg );
2246  gsl_matrix_set( metric->m2_ij, j, i, gg );
2247 
2248  gg = P3_ij - Q3_ij;
2249  gsl_matrix_set( metric->m3_ij, i, j, gg );
2250  gsl_matrix_set( metric->m3_ij, j, i, gg );
2251 
2252 
2253  /* assemble the 'full' F-stat metric from these ingredients, see Eq.(87) */
2254  gg = alpha1 * gsl_matrix_get( metric->m1_ij, i, j ) + alpha2 * gsl_matrix_get( metric->m2_ij, i, j )
2255  + 2.0 * alpha3 * gsl_matrix_get( metric->m3_ij, i, j );
2256  gg /= metric->rho2;
2257 
2258  gsl_matrix_set( metric->gF_ij, i, j, gg );
2259  gsl_matrix_set( metric->gF_ij, j, i, gg );
2260 
2261  /* compute 'average' F-stat metric as given by Eq.(93) */
2262  gg = B * gsl_matrix_get( metric->m1_ij, i, j ) + A * gsl_matrix_get( metric->m2_ij, i, j )
2263  - 2.0 * C * gsl_matrix_get( metric->m3_ij, i, j );
2264  gg /= 2.0 * D;
2265 
2266  gsl_matrix_set( metric->gFav_ij, i, j, gg );
2267  gsl_matrix_set( metric->gFav_ij, j, i, gg );
2268 
2269  } /* for j <= i */
2270 
2271  } /* for i < dim */
2272 
2273  return metric;
2274 
2275 } /* XLALComputeFmetricFromAtoms() */
2276 
2277 
2278 /**
2279  * Function to compute *full* 4+n dimensional Fisher matric for the
2280  * full CW parameter-space of Amplitude + Doppler parameters !
2281  */
2282 static gsl_matrix *
2284 {
2285  gsl_matrix *fisher = NULL; /* output matrix */
2286 
2287  UINT4 dimDoppler, dimFull, i, j;
2288  REAL8 al1, al2, al3;
2289 
2290  /* check input consistency */
2291  if ( !atoms ) {
2292  XLALPrintError( "%s: illegal NULL input.\n\n", __func__ );
2294  }
2295 
2296  if ( !atoms->a_a_i || !atoms->a_b_i || !atoms->b_b_i || !atoms->a_a_i_j || !atoms->a_b_i_j || !atoms->b_b_i_j ) {
2297  XLALPrintError( "%s: input Fmetric-atoms not fully allocated.\n\n", __func__ );
2299  }
2300 
2301  REAL8 A1, A2, A3, A4;
2302  {
2303  PulsarAmplitudeVect Amu;
2304  if ( XLALAmplitudeParams2Vect( Amu, Amp ) != XLAL_SUCCESS ) {
2305  XLALPrintError( "%s: XLALAmplitudeParams2Vect() failed with errno = %d\n\n", __func__, xlalErrno );
2307  }
2308 
2309  A1 = Amu[0];
2310  A2 = Amu[1];
2311  A3 = Amu[2];
2312  A4 = Amu[3];
2313  }
2314 
2315  dimDoppler = atoms->a_a_i->size;
2316  dimFull = 4 + dimDoppler; /* 4 amplitude params + n Doppler params */
2317 
2318  if ( ( fisher = gsl_matrix_calloc( dimFull, dimFull ) ) == NULL ) {
2319  XLALPrintError( "%s: gsl_matric_calloc(%d,%d) failed.\n\n", __func__, dimFull, dimFull );
2321  }
2322 
2323  /* ----- set pure Amplitude block 4x4: M_mu_nu ---------- */
2324  {
2325  REAL8 A, B, C;
2326  A = atoms->a_a;
2327  B = atoms->b_b;
2328  C = atoms->a_b;
2329 
2330  gsl_matrix_set( fisher, 0, 0, A );
2331  gsl_matrix_set( fisher, 2, 2, A );
2332 
2333  gsl_matrix_set( fisher, 1, 1, B );
2334  gsl_matrix_set( fisher, 3, 3, B );
2335 
2336  gsl_matrix_set( fisher, 0, 1, C );
2337  gsl_matrix_set( fisher, 1, 0, C );
2338  gsl_matrix_set( fisher, 2, 3, C );
2339  gsl_matrix_set( fisher, 3, 2, C );
2340  } /* amplitude-param block M_mu_nu */
2341 
2342 
2343  /* ----- set Doppler block (4+i,4+j) ---------- */
2344  al1 = SQUARE( A1 ) + SQUARE( A3 );
2345  al2 = A1 * A2 + A3 * A4;
2346  al3 = SQUARE( A2 ) + SQUARE( A4 );
2347 
2348  for ( i = 0; i < dimDoppler; i ++ ) {
2349  for ( j = 0; j <= i; j ++ ) {
2350  REAL8 gg, a_a_i_j, a_b_i_j, b_b_i_j;
2351 
2352  a_a_i_j = gsl_matrix_get( atoms->a_a_i_j, i, j );
2353  a_b_i_j = gsl_matrix_get( atoms->a_b_i_j, i, j );
2354  b_b_i_j = gsl_matrix_get( atoms->b_b_i_j, i, j );
2355 
2356  gg = al1 * a_a_i_j + 2.0 * al2 * a_b_i_j + al3 * b_b_i_j;
2357 
2358  gsl_matrix_set( fisher, 4 + i, 4 + j, gg );
2359  gsl_matrix_set( fisher, 4 + j, 4 + i, gg );
2360 
2361  } /* for j <= i */
2362  } /* for i < dimDoppler */
2363 
2364 
2365  /* ----- compute mixed Amplitude-Doppler block ( non-square ) */
2366  for ( i = 0; i < dimDoppler; i ++ ) {
2367  REAL8 a_a_i, a_b_i, b_b_i;
2368  REAL8 AR[4];
2369  UINT4 mu;
2370 
2371  a_a_i = gsl_vector_get( atoms->a_a_i, i );
2372  a_b_i = gsl_vector_get( atoms->a_b_i, i );
2373  b_b_i = gsl_vector_get( atoms->b_b_i, i );
2374 
2375  AR[0] = A3 * a_a_i + A4 * a_b_i;
2376  AR[1] = A3 * a_b_i + A4 * b_b_i;
2377  AR[2] = -A1 * a_a_i - A2 * a_b_i;
2378  AR[3] = -A1 * a_b_i - A2 * b_b_i;
2379 
2380  for ( mu = 0; mu < 4; mu ++ ) {
2381  gsl_matrix_set( fisher, mu, 4 + i, AR[mu] );
2382  gsl_matrix_set( fisher, 4 + i, mu, AR[mu] );
2383  }
2384 
2385  } /* for i < dimDoppler */
2386 
2387 
2388  return fisher;
2389 
2390 } /* XLALComputeFisherFromAtoms() */
2391 
2392 
2393 /** Convert 3-D vector from equatorial into ecliptic coordinates */
2394 void
2396 {
2397  static mat33_t rotEqu2Ecl = { { 1.0, 0, 0 },
2398  { 0.0, LAL_COSIEARTH, LAL_SINIEARTH },
2399  { 0.0, -LAL_SINIEARTH, LAL_COSIEARTH }
2400  };
2401 
2402  XLALmatrix33_in_vect3( out, rotEqu2Ecl, in );
2403 
2404 } /* XLALequatorialVect2ecliptic() */
2405 
2406 /** Convert 3-D vector from ecliptic into equatorial coordinates */
2407 void
2409 {
2410  static mat33_t rotEcl2Equ = { { 1.0, 0, 0 },
2411  { 0.0, LAL_COSIEARTH, -LAL_SINIEARTH },
2412  { 0.0, LAL_SINIEARTH, LAL_COSIEARTH }
2413  };
2414 
2415  XLALmatrix33_in_vect3( out, rotEcl2Equ, in );
2416 
2417 } /* XLALeclipticVect2equatorial() */
2418 
2419 /** compute matrix product mat . vect */
2420 void
2422 {
2423 
2424  UINT4 i, j;
2425  for ( i = 0; i < 3; i ++ ) {
2426  out[i] = 0;
2427  for ( j = 0; j < 3; j ++ ) {
2428  out[i] += mat[i][j] * in[j];
2429  }
2430  }
2431 
2432 } /* XLALmatrix33_in_vect3() */
2433 
2434 /**
2435  * Compute time-derivatives up to 'maxorder' of the Earths' orbital position vector
2436  * \f$ r_{\mathrm{orb}}(t) \f$ .
2437  *
2438  * Algorithm: using 5-point differentiation expressions on r_orb(t) returned from XLALBarycenterEarth().
2439  *
2440  * Returns a vector of derivatives \f$ \frac{d^n\,r_{\mathrm{orb}}}{d\,t^n} \f$ at the given
2441  * GPS time. Note, the return vector includes the zeroth-order derivative, so we return
2442  * (maxorder + 1) derivatives: n = 0 ... maxorder
2443  *
2444  */
2445 vect3Dlist_t *
2446 XLALComputeOrbitalDerivatives( UINT4 maxorder, /**< [in] highest derivative-order to compute */
2447  const LIGOTimeGPS *tGPS, /**< [in] GPS time at which to compute the derivatives */
2448  const EphemerisData *edat /**< [in] ephemeris data */
2449  )
2450 {
2451  EarthState earth;
2452  LIGOTimeGPS ti;
2453  REAL8 h = 0.5 * 86400.0; /* finite-differencing step-size for rOrb. Before CAREFUL before changing this! */
2454  vect3D_t r0m2h, r0mh, r0, r0_h, r0_2h;
2455  vect3Dlist_t *ret = NULL;
2456 
2457  /* check input consistency */
2458  if ( maxorder > MAX_SPDNORDER ) {
2459  XLALPrintError( "%s: maxorder = %d too large, currently supports only up to maxorder = %d.\n", __func__, maxorder, MAX_SPDNORDER );
2461  }
2462 
2463  if ( !tGPS ) {
2464  XLALPrintError( "%s: invalid NULL pointer received for 'tGPS'.\n", __func__ );
2466  }
2467  if ( !edat ) {
2468  XLALPrintError( "%s: invalid NULL pointer received for 'edat'.\n", __func__ );
2470  }
2471 
2472 
2473  /* ----- find Earth's position at the 5 points: t0 -2h, t0-h, t0, t0+h, t0 + 2h ----- */
2474 
2475  /* t = t0 */
2476  ti = ( *tGPS );
2477  if ( XLALBarycenterEarth( &earth, &ti, edat ) != XLAL_SUCCESS ) {
2478  XLALPrintError( "%s: call to XLALBarycenterEarth() failed!\n\n", __func__ );
2480  }
2481  COPY_VECT( r0, earth.posNow );
2482 
2483  /* t = t0 - h*/
2484  ti.gpsSeconds = ( *tGPS ).gpsSeconds - h;
2485  if ( XLALBarycenterEarth( &earth, &ti, edat ) != XLAL_SUCCESS ) {
2486  XLALPrintError( "%s: call to XLALBarycenterEarth() failed!\n\n", __func__ );
2488  }
2489  COPY_VECT( r0mh, earth.posNow );
2490 
2491  /* t = t0 - 2h*/
2492  ti.gpsSeconds = ( *tGPS ).gpsSeconds - 2 * h;
2493  if ( XLALBarycenterEarth( &earth, &ti, edat ) != XLAL_SUCCESS ) {
2494  XLALPrintError( "%s: call to XLALBarycenterEarth() failed!\n\n", __func__ );
2496  }
2497  COPY_VECT( r0m2h, earth.posNow );
2498 
2499  /* t = t0 + h*/
2500  ti.gpsSeconds = ( *tGPS ).gpsSeconds + h;
2501  if ( XLALBarycenterEarth( &earth, &ti, edat ) != XLAL_SUCCESS ) {
2502  XLALPrintError( "%s: call to XLALBarycenterEarth() failed!\n\n", __func__ );
2504  }
2505  COPY_VECT( r0_h, earth.posNow );
2506 
2507  /* t = t0 + 2h*/
2508  ti.gpsSeconds = ( *tGPS ).gpsSeconds + 2 * h;
2509  if ( XLALBarycenterEarth( &earth, &ti, edat ) != XLAL_SUCCESS ) {
2510  XLALPrintError( "%s: call to XLALBarycenterEarth() failed!\n\n", __func__ );
2512  }
2513  COPY_VECT( r0_2h, earth.posNow );
2514 
2515  /* use these 5 points to estimate derivatives */
2516  UINT4 i;
2517  vect3D_t rdn[MAX_SPDNORDER + 1];
2518  COPY_VECT( rdn[0], r0 ); // 0th order is imply r_orb(t0)
2519  for ( i = 0; i < 3; i ++ ) {
2520  rdn[1][i] = DERIV5P_1( r0m2h[i], r0mh[i], r0[i], r0_h[i], r0_2h[i], h );
2521  rdn[2][i] = DERIV5P_2( r0m2h[i], r0mh[i], r0[i], r0_h[i], r0_2h[i], h );
2522  rdn[3][i] = DERIV5P_3( r0m2h[i], r0mh[i], r0[i], r0_h[i], r0_2h[i], h );
2523  rdn[4][i] = DERIV5P_4( r0m2h[i], r0mh[i], r0[i], r0_h[i], r0_2h[i], h );
2524  } /* for i < 3 */
2525 
2526  /* allocate return list */
2527  if ( ( ret = XLALCalloc( 1, sizeof( *ret ) ) ) == NULL ) {
2528  XLALPrintError( "%s: failed to XLALCalloc(1,%zu)\n", __func__, sizeof( *ret ) );
2530  }
2531  ret->length = maxorder + 1;
2532  if ( ( ret->data = XLALCalloc( ret->length, sizeof( *ret->data ) ) ) == NULL ) {
2533  XLALPrintError( "%s: failed to XLALCalloc(%d,%zu)\n", __func__, ret->length, sizeof( *ret->data ) );
2534  XLALFree( ret );
2536  }
2537 
2538  UINT4 n;
2539  for ( n = 0; n <= maxorder; n ++ ) {
2540  COPY_VECT( ret->data[n], rdn[n] );
2541  }
2542 
2543  return ret;
2544 
2545 } /* XLALComputeOrbitalDerivatives() */
2546 
2547 void
2549 {
2550  if ( !list ) {
2551  return;
2552  }
2553 
2554  if ( list->data ) {
2555  XLALFree( list->data );
2556  }
2557 
2558  XLALFree( list );
2559 
2560  return;
2561 
2562 } /* XLALDestroyVect3Dlist() */
2563 
2564 /**
2565  * Return the highest 'global-correlation' spindown order found in this coordinate system.
2566  * Counting nu0 = order1, nu1 = order2, nu2 = order3, ...,
2567  * order = 0 therefore means there are no GC spin coordinates at all
2568  */
2569 static UINT4
2571 {
2572 
2573  UINT4 maxorder = 0;
2574  UINT4 i;
2575 
2576  for ( i = 0; i < coordSys->dim; i ++ ) {
2577  UINT4 order = 0;
2578  if ( coordSys->coordIDs[i] == DOPPLERCOORD_GC_NU0 ) {
2579  order = 1;
2580  }
2581  if ( coordSys->coordIDs[i] == DOPPLERCOORD_GC_NU1 ) {
2582  order = 2;
2583  }
2584  if ( coordSys->coordIDs[i] == DOPPLERCOORD_GC_NU2 ) {
2585  order = 3;
2586  }
2587  if ( coordSys->coordIDs[i] == DOPPLERCOORD_GC_NU3 ) {
2588  order = 4;
2589  }
2590  if ( coordSys->coordIDs[i] == DOPPLERCOORD_GC_NU4 ) {
2591  order = 5;
2592  }
2593  maxorder = MYMAX( maxorder, order );
2594  }
2595 
2596  return maxorder;
2597 } /* findHighestSpinOrder() */
2598 
2599 /// @}
#define __func__
log an I/O error, i.e.
int j
int k
#define D(j)
UINT2 A
Definition: SFTnaming.c:46
UINT2 B
Definition: SFTnaming.c:47
#define fprintf
#define GET_COORD_SCALE(coordSys, coord)
#define COPY_VECT(dst, src)
copy 3 components of Euklidean vector
#define POW4(a)
#define MAX_SPDNORDER
#define DERIV5P_2(pm2, pm1, p0, pp1, pp2, h)
const double scale
multiplicative scaling factor of the coordinate
#define vOrb_c
#define DERIV5P_1(pm2, pm1, p0, pp1, pp2, h)
5-point derivative formulas (eg see http://math.fullerton.edu/mathews/articles/2003NumericalDiffFormu...
const DetectorMotionType type
#define DERIV5P_4(pm2, pm1, p0, pp1, pp2, h)
#define ZERO_VECT(v)
Operations on 3-dim vectors
#define MYMIN(a, b)
#define SUB_VECT(dst, src)
#define rOrb_c
#define POW5(a)
static const struct @10 DopplerCoordinates[DOPPLERCOORD_LAST]
Array of descriptor structs for each Doppler coordinate name.
static const struct @9 DetectorMotionNames[]
Array of symbolic 'names' for various detector-motions.
#define DOT_VECT(u, v)
Simple Euklidean scalar product for two 3-dim vectors in cartesian coords.
#define SCALE_R
#define RELERR(dx, x)
const char *const help
help string explaining the coordinate's meaning and units
#define EQU_VECT_TO_ECL(dst, src)
Convert 3-D vector from equatorial into ecliptic coordinates.
#define SQUARE(x)
#define HEADER
#define POW3(a)
#define DETMOTION_NAMES(orbit_type, spin_orbit_name, nospin_orbit_name)
const char *const name
coordinate name
#define ADD_VECT(dst, src)
#define MULT_VECT(v, lam)
AM_comp_t
components of antenna-pattern function: q_l = {a(t), b(t)}
@ AMCOMP_NONE
no antenna pattern function: (equivalent "a = 1")
@ AMCOMP_B
b(t)
@ AMCOMP_A
a(t)
#define GET_COORD_NAME(coordSys, coord)
#define GET_COORD_ID(coordSys, coord)
#define MYMAX(a, b)
#define SCALE_T
#define DERIV5P_3(pm2, pm1, p0, pp1, pp2, h)
#define XLAL_CALLGSL(statement)
double e
REAL8 coeff(qfvars *vars, REAL8 x)
Definition: cdfwchisq.c:620
void order(qfvars *vars)
Definition: cdfwchisq.c:56
const double vz
const LALDetector lalCachedDetectors[LAL_NUM_DETECTORS]
static const REAL8 LAL_FACT_INV[]
int XLALAmplitudeParams2Vect(PulsarAmplitudeVect A_Mu, const PulsarAmplitudeParams Amp)
Convert amplitude params from 'physical' coordinates into 'canonical' coordinates .
int XLALGetEarthTimes(const LIGOTimeGPS *tepoch, REAL8 *tMidnight, REAL8 *tAutumn)
This function takes a GPS time from tepoch and uses it to assign tAutumn and tMidnight,...
Definition: GetEarthTimes.c:76
int XLALBarycenterEarth(EarthState *earth, const LIGOTimeGPS *tGPS, const EphemerisData *edat)
Computes the position and orientation of the Earth, at some arrival time , specified LIGOTimeGPS inp...
Definition: LALBarycenter.c:78
int XLALBarycenter(EmissionTime *emit, const BarycenterInput *baryinput, const EarthState *earth)
Transforms from detector arrival time in GPS (as specified in the LIGOTimeGPS structure) to pulse em...
int XLALComputeAntennaPatternCoeffs(REAL8 *ai, REAL8 *bi, const SkyPosition *skypos, const LIGOTimeGPS *tGPS, const LALDetector *site, const EphemerisData *edat)
Compute single time-stamp antenna-pattern coefficients a(t), b(t) Note: this function uses REAL8 prec...
Definition: LALComputeAM.c:79
#define LAL_COSIEARTH
#define LAL_DAYSID_SI
#define LAL_PI
#define LAL_YRSID_SI
#define LAL_TWOPI
#define LAL_SINIEARTH
unsigned char BOOLEAN
double REAL8
#define XLAL_INIT_DECL(var,...)
char CHAR
uint32_t UINT4
#define lalDebugLevel
LALINFOBIT
LAL_LHO_4K_DETECTOR
void * XLALCalloc(size_t m, size_t n)
void * XLALRealloc(void *p, size_t n)
void XLALFree(void *p)
void void int XLALfprintfGSLmatrix(FILE *fp, const char *fmt, const gsl_matrix *gij) _LAL_GCC_VPRINTF_FORMAT_(2)
int XLALfprintfGSLvector(FILE *fp, const char *fmt, const gsl_vector *vect) _LAL_GCC_VPRINTF_FORMAT_(2)
void LogPrintf(LogLevel_t, const char *format,...) _LAL_GCC_PRINTF_FORMAT_(2
LOG_DETAIL
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
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
double XLALMetricDeterminant(const gsl_matrix *g_ij)
Compute the determinant of a metric .
Definition: MetricUtils.c:98
REAL8 PulsarAmplitudeVect[4]
Struct for 'canonical' coordinates in amplitude-params space A^mu = {A1, A2, A3, A4}.
int XLALSegListInit(LALSegList *seglist)
int XLALSegListIsInitialized(const LALSegList *seglist)
COORDINATESYSTEM_EQUATORIAL
int XLALDetectorPosVel(PosVel3D_t *spin_posvel, PosVel3D_t *orbit_posvel, const LIGOTimeGPS *tGPS, const LALDetector *site, const EphemerisData *edat, DetectorMotionType detMotionType)
Given a GPS time and detector, return the current position (and velocity) of the detector.
static double XLALAverage_am1_am2_Phi_i_Phi_j(const intparams_t *params, double *relerr_max)
Integrate a general quadruple product CW_am1_am2_Phi_i_Phi_j() from 0 to 1.
int XLALDopplerCoordinateNames2System(DopplerCoordinateSystem *coordSys, const LALStringVector *coordNames)
Given a LALStringVector of coordinate-names, parse them into a 'DopplerCoordinateSystem',...
const CHAR * XLALDetectorMotionName(DetectorMotionType detMotionType)
Provide a pointer to a static string containing the DopplerCoordinate-name cooresponding to the enum ...
static double XLALCovariance_Phi_ij(const MultiLALDetector *multiIFO, const MultiNoiseFloor *multiNoiseFloor, const LALSegList *segList, const intparams_t *params, double *relerr_max)
Compute a pure phase-deriv covariance which gives a component of the "phase metric".
REAL8 vect3D_t[3]
3D vector
static UINT4 findHighestGCSpinOrder(const DopplerCoordinateSystem *coordSys)
Return the highest 'global-correlation' spindown order found in this coordinate system.
DopplerCoordinateID
enum listing symbolic 'names' for all Doppler Coordinates supported by the metric codes in FstatMetri...
CHAR * XLALDopplerCoordinateHelpAll(void)
Return a string (allocated here) containing a full name - helpstring listing for all doppler-coordina...
static gsl_matrix * XLALComputeFisherFromAtoms(const FmetricAtoms_t *atoms, PulsarAmplitudeParams Amp)
Function to compute full 4+n dimensional Fisher matric for the full CW parameter-space of Amplitude +...
static double CW_am1_am2_Phi_i_Phi_j(double tt, void *params)
For gsl-integration: general quadruple product between two antenna-pattern functions am1,...
void XLALDestroyFmetricAtoms(FmetricAtoms_t *atoms)
Free a FmetricAtoms_t structure, allowing any pointers to be NULL.
DetectorMotionType
Bitfield of different types of detector-motion to use in order to compute the Doppler-metric.
void XLALeclipticVect2equatorial(vect3D_t out, const vect3D_t in)
Convert 3-D vector from ecliptic into equatorial coordinates.
static DopplerFstatMetric * XLALComputeFmetricFromAtoms(const FmetricAtoms_t *atoms, REAL8 cosi, REAL8 psi)
Compute the 'F-metric' gF_ij (and also gFav_ij, m1_ij, m2_ij, m3_ij) from the given FmetricAtoms and ...
REAL8 XLALComputePhaseDerivative(REAL8 t, const PulsarDopplerParams *dopplerPoint, DopplerCoordinateID coordID, const EphemerisData *edat, const LALDetector *site, BOOLEAN includeRoemer)
FmetricAtoms_t * XLALComputeAtomsForFmetric(const DopplerMetricParams *metricParams, const EphemerisData *edat)
Function to the compute the FmetricAtoms_t, from which the F-metric and Fisher-matrix can be computed...
int XLALFindDopplerCoordinateInSystem(const DopplerCoordinateSystem *coordSys, const DopplerCoordinateID coordID)
Given a coordinate ID 'coordID', return its dimension within the given coordinate system 'coordSys',...
void XLALDestroyDopplerFstatMetric(DopplerFstatMetric *metric)
Free a DopplerFstatMetric structure.
const CHAR * XLALDopplerCoordinateHelp(DopplerCoordinateID coordID)
Provide a pointer to a static string containing the a descriptive 'help-string' describing the coordi...
static double CW_Phi_i(double tt, void *params)
Partial derivative of continuous-wave (CW) phase, with respect to Doppler coordinate 'i' := intparams...
void XLALmatrix33_in_vect3(vect3D_t out, mat33_t mat, const vect3D_t in)
compute matrix product mat .
void XLALDestroyDopplerPhaseMetric(DopplerPhaseMetric *metric)
Free a DopplerPhaseMetric structure.
REAL8 mat33_t[3][3]
3x3 matrix, useful for spatial 3D vector operations
const CHAR * XLALDopplerCoordinateName(DopplerCoordinateID coordID)
Provide a pointer to a static string containing the DopplerCoordinate-name cooresponding to the enum ...
void XLALequatorialVect2ecliptic(vect3D_t out, const vect3D_t in)
Convert 3-D vector from equatorial into ecliptic coordinates.
vect3Dlist_t * XLALComputeOrbitalDerivatives(UINT4 maxorder, const LIGOTimeGPS *tGPS, const EphemerisData *edat)
Compute time-derivatives up to 'maxorder' of the Earths' orbital position vector .
int XLALParseDetectorMotionString(const CHAR *detMotionString)
Parse a detector-motion type string into the corresponding enum-number,.
DopplerFstatMetric * XLALComputeDopplerFstatMetric(const DopplerMetricParams *metricParams, const EphemerisData *edat)
Calculate the general (single-segment coherent, or multi-segment semi-coherent) full (multi-IFO) Fsta...
#define POW2(a)
Function to compute the full F-statistic metric, including antenna-pattern functions from multi-detec...
int XLALPtolemaicPosVel(PosVel3D_t *posvel, const LIGOTimeGPS *tGPS)
Compute position and velocity assuming a purely "Ptolemaic" orbital motion (i.e.
void XLALDestroyVect3Dlist(vect3Dlist_t *list)
int XLALParseDopplerCoordinateString(const CHAR *coordName)
Parse a DopplerCoordinate-name into the corresponding DopplerCoordinateID.
DopplerPhaseMetric * XLALComputeDopplerPhaseMetric(const DopplerMetricParams *metricParams, const EphemerisData *edat)
Calculate an approximate "phase-metric" with the specified parameters.
@ 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_ETA
Lagrange parameter 'eta = ecc * sin(argp) of binary orbit (ELL1 model) [Units: none].
@ DOPPLERCOORD_N3X_ECL
X component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_KAPPA
Lagrange parameter 'kappa = ecc * cos(argp)', ('ecc' = eccentricity, 'argp' = argument of periapse) o...
@ DOPPLERCOORD_TASC
Time of ascension (neutron star crosses line of nodes moving away from observer) for binary orbit (EL...
@ 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_ETAP
Rescaled (by asini) differential-coordinate 'detap = asini * deta' [Units: light seconds].
@ DOPPLERCOORD_KAPPAP
Rescaled (by asini) differential-coordinate 'dkappap = asini * dkappa' [Units: light seconds].
@ DOPPLERCOORD_ASINI
Projected semimajor axis of binary orbit in small-eccentricy limit (ELL1 model) [Units: light seconds...
@ DOPPLERCOORD_N3Z_EQU
Z component of unconstrained super-sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_GC_NU4
Global correlation fourth spindown [Units: Hz/s^4].
@ DOPPLERCOORD_F2DOT
Second spindown [Units: Hz/s^2].
@ DOPPLERCOORD_DASC
Distance traversed on the arc of binary orbit (ELL1 model) 'dasc = 2 * pi * (ap/porb) * tasc' [Units:...
@ 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_LAST
@ DOPPLERCOORD_GC_NU1
Global correlation first spindown [Units: Hz/s].
@ DOPPLERCOORD_GC_NU0
Global correlation frequency [Units: Hz].
@ DOPPLERCOORD_N3OZ_ECL
Z orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ 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_F4DOT
Fourth spindown [Units: Hz/s^4].
@ 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_VP
Rescaled (by asini) differential-coordinate 'dvp = asini * dOMEGA', ('OMEGA' = 2 * pi/'porb') of bina...
@ DOPPLERCOORD_N3Y_ECL
Y component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_PORB
Period of binary orbit (ELL1 model) [Units: s].
@ 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].
@ DETMOTION_SPIN
Full spin motion.
@ DETMOTION_MASKSPIN
Mask for spin motion bits.
@ DETMOTION_PTOLEORBIT
Ptolemaic (circular) orbital motion.
@ DETMOTION_SPINXY
Ecliptic-X+Y components of spin motion only.
@ DETMOTION_MASKORBIT
Mask for orbital motion bits.
@ DETMOTION_SPINZ
Ecliptic-Z component of spin motion only.
@ DETMOTION_ORBIT
Ephemeris-based orbital motion.
#define XLAL_ERROR_REAL8(...)
#define XLAL_ERROR_NULL(...)
#define xlalErrno
int int int XLALPrintInfo(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
#define XLAL_TRY(statement, errnum)
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,...)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EMAXITER
XLAL_EDOM
XLAL_EINVAL
XLAL_EFAILED
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
LIGOTimeGPS * XLALGPSSetREAL8(LIGOTimeGPS *epoch, REAL8 t)
REAL8 XLALGPSGetREAL8(const LIGOTimeGPS *epoch)
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
list mu
n
out
list weights
Basic input structure to LALBarycenter.c.
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
Struct to hold the output of XLALComputeDopplerFstatMetric(), including meta-info on the number of di...
gsl_matrix * m3_ij
Fstat-metric sub components.
double maxrelerr
estimate for largest relative error in Fmetric component integrations
gsl_matrix * gFav_ij
'average' Fstat-metric
gsl_matrix * gF_ij
full F-statistic metric gF_ij, including antenna-pattern effects (see )
DopplerMetricParams meta
"meta-info" describing/specifying the type of Doppler metric
REAL8 rho2
signal SNR rho^2 = A^mu M_mu_nu A^nu
gsl_matrix * Fisher_ab
Full 4+n dimensional Fisher matrix, ie amplitude + Doppler space.
meta-info specifying a Doppler-metric
MultiLALDetector multiIFO
detectors to compute metric for
DetectorMotionType detMotionType
the type of detector-motion assumed: full spin+orbit, pure orbital, Ptole, ...
BOOLEAN approxPhase
use an approximate phase-model, neglecting Roemer delay in spindown coordinates
PulsarParams signalParams
parameter-space point to compute metric for (doppler + amplitudes)
INT4 projectCoord
project metric onto subspace orthogonal to this axis (-1 = none, 0 = 1st coordinate,...
LALSegList segmentList
segment list: Nseg segments of the form (startGPS endGPS numSFTs)
MultiNoiseFloor multiNoiseFloor
and associated per-detector noise-floors to be used for weighting.
DopplerCoordinateSystem coordSys
number of dimensions and coordinate-IDs of Doppler-metric
Struct to hold the output of XLALComputeDopplerPhaseMetric(), including meta-info on the number of di...
double maxrelerr
estimate for largest relative error in phase-metric component integrations
gsl_matrix * g_ij
symmetric matrix holding the phase-metric, averaged over segments
DopplerMetricParams meta
"meta-info" describing/specifying the type of Doppler metric
Basic output structure of LALBarycenterEarth.c.
REAL8 velNow[3]
dimensionless velocity of Earth's center at tgps, extrapolated from JPL DE405 ephemeris
REAL8 posNow[3]
Cartesian coords of Earth's center at tgps, extrapolated from JPL DE405 ephemeris; units= sec.
Basic output structure produced by LALBarycenter.c.
This structure contains all information about the center-of-mass positions of the Earth and Sun,...
Struct to hold the 'atoms', ie weighted phase-derivative averages like from which the F-metric is co...
double maxrelerr
estimate for largest relative error in metric component integrations
LIGOTimeGPS end
LIGOTimeGPS start
UINT4 length
LALSeg * segs
size_t arraySize
array of detectors definitions 'LALDetector'
UINT4 length
number of detectors
LALDetector sites[PULSAR_MAX_DETECTORS]
array of site information
array of detector-specific 'noise floors' (ie PSD values), assumed constant over the frequency-band o...
REAL8 sqrtSn[PULSAR_MAX_DETECTORS]
per-IFO sqrt(PSD) values , where
UINT4 length
number of detectors
Small Container to hold two 3D vectors: position and velocity.
Type containing the JKS 'amplitude parameters' {h0, cosi, phi0, psi}.
REAL8 aCross
Signal amplitude (cross)
REAL8 psi
polarization angle psi
REAL8 aPlus
Signal amplitude (plus)
Type containing the 'Doppler-parameters' affecting the time-evolution of the phase.
REAL8 Delta
Sky position: DEC (latitude) in equatorial coords and radians.
LIGOTimeGPS refTime
Reference time of pulsar parameters (in SSB!)
PulsarAmplitudeParams Amp
'Amplitude-parameters': h0, cosi, phi0, psi
PulsarDopplerParams Doppler
'Phase-evolution parameters': {skypos, fkdot, orbital params }
REAL8 longitude
REAL8 latitude
CoordinateSystem system
parameters for metric-integration
const DopplerCoordinateSystem * coordSys
Doppler coordinate system in use.
REAL8 startTime
GPS start time of observation.
int coord
coordinate index of the component for single phase-derivative Phi_i compute
double intT
length of integration time segments for phase integrals
double epsabs
absolute error tolerance for GSL integration routines
const PulsarDopplerParams * dopplerPoint
Doppler params to compute metric for.
BOOLEAN approxPhase
use an approximate phase-model, neglecting Roemer delay in spindown coordinates (or orders >= 1)
DetectorMotionType detMotionType
which detector-motion to use in metric integration
double epsrel
relative error tolerance for GSL integration routines
vect3Dlist_t * rOrb_n
list of orbital-radius derivatives at refTime of order n = 0, 1, ...
const EphemerisData * edat
ephemeris data
int errnum
store XLAL error of any failures within integrator
const gsl_matrix * coordTransf
coordinate transform to apply to coordinate system
const LALDetector * site
detector site to compute metric for
REAL8 Tspan
length of observation time in seconds
REAL8 refTime
GPS reference time for pulsar parameters.
variable-length list of 3D vectors
vect3D_t * data
array of 3D vectors
UINT4 length
number of elements
enum @4 site