LALSimulation  5.4.0.1-fe68b98
LALSimIMRPhenomTPHM_EulerAngles.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2020 Hector Estelles
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with with program; see the file COPYING. If not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301 USA
18  */
19 
20 
21 /**
22  * \author Hector Estelles
23  */
24 
25 #include <math.h>
26 
27 /* LAL Header Files */
28 #include <lal/LALSimIMR.h>
29 #include <lal/LALConstants.h>
30 #include <lal/Date.h>
31 #include <lal/Units.h>
32 
33 /* GSL Header Files */
34 #include <gsl/gsl_linalg.h>
35 #include <gsl/gsl_complex.h>
36 #include <gsl/gsl_complex_math.h>
37 #include <gsl/gsl_roots.h>
38 
39 #include <lal/XLALGSL.h>
40 #include <gsl/gsl_errno.h>
41 #include <gsl/gsl_spline.h>
42 #include <lal/LALAdaptiveRungeKuttaIntegrator.h>
43 
44 #include <gsl/gsl_sf_legendre.h>
45 #include <gsl/gsl_sf_gamma.h>
46 
47 #include <lal/LALSimInspiralPrecess.h>
48 
50 
51 #define MIN(a,b) (((a)<(b))?(a):(b))
52 
53 // FIXME: reference equations to PhenomTP paper and future TPHM paper.
54 
55 /* Routines and wrappers to compute precessing Euler angles for 'twisting-up' IMRPhenomTHM into IMRPhenomTPHM */
56 
57 /* ************************************************************* */
58 /* ************** STRUCTS and AUXILIARY FUNCTIONS ************** */
59 /* ************************************************************* */
60 
61 /* Collection of functions and structs needed for the Euler angle routines */
62 
63 // Tolerance for integrator in numerical angles routine
64 #define LAL_ST4_ABSOLUTE_TOLERANCE 1.e-12
65 #define LAL_ST4_RELATIVE_TOLERANCE 1.e-12
66 
67 /* MECO time fit: Needed for option EulerRDVersion=2. */
68 static double IMRPhenomT_MECOTime(double eta, double S, double dchi, double delta);
69 static double IMRPhenomT_MECOTime(double eta, double S, double dchi, double delta){
70 
71  double nospin, eqspin, uneqspin;
72 
73  nospin = (-236.05916243036953 - 831.5431113584278*eta)/(1 + 20.613030474069898*eta);
74 
75  eqspin = (48.728774732041 + 8.161848266333303*eta + 146.22536542867712*eta*eta)*S + (-13.633462771084439 + 97.744032521123*eta - 333.74293640880865*eta*eta)*S*S +
76  (-76.67450724471107 + 1030.4869060625915*eta - 3160.810490374918*eta*eta)*S*S*S + (-20.941621754455277 - 26.348664719217858*eta + 778.3469028250508*eta*eta)*S*S*S*S +
77  (86.59936006891306 - 1115.1092303071634*eta + 3348.2374777841*eta*eta)*S*S*S*S*S + (13.413110005766034 + 46.573063129481895*eta - 631.7603046833175*eta*eta)*S*S*S*S*S*S;
78 
79  uneqspin = -1385.6109494038105*dchi*delta*(1 - 2.465600020183968*eta)*eta*eta + 10.837064426546098*dchi*dchi*eta*eta*eta + 355.1229694251773*dchi*delta*(1 - 4.1057183984004695*eta)*eta*eta*S;
80 
81  return (nospin + eqspin + uneqspin);
82 }
83 
84 /* Struct needed for computing gamma from the minimal rotation condition. */
85 typedef struct taggammaIntegration
86 {
87  gsl_spline *alpha_spline;
88  gsl_spline *cosbeta_spline;
89  gsl_interp_accel *alpha_acc;
90  gsl_interp_accel *cosbeta_acc;
91 }
93 
94 /* Function to return RHS of minimal rotation condition equation, for computing gamma angle. */
95 static double f_alphadotcosi( double x, void * inparams )
96 {
98  REAL8 alphadot = gsl_spline_eval_deriv( params->alpha_spline, x, params->alpha_acc );
99  REAL8 cosbeta = gsl_spline_eval( params->cosbeta_spline, x, params->cosbeta_acc );
100 
101  return -1. * alphadot * cosbeta;
102 }
103 
104 
105 /* Functions to perform a ZYZ rotation of a given vector by some given angles. */
106 void IMRPhenomT_rotate_z(REAL8 cosangle, REAL8 sinangle, REAL8 *vx, REAL8 *vy, REAL8 *vz);
107 void IMRPhenomT_rotate_y(REAL8 cosangle, REAL8 sinangle, REAL8 *vx, REAL8 *vy, REAL8 *vz);
108 
109 void IMRPhenomT_rotate_z(REAL8 cosangle, REAL8 sinangle, REAL8 *vx, REAL8 *vy, REAL8 *vz)
110 {
111  const REAL8 tmpx = *vx;
112  const REAL8 tmpy = *vy;
113  const REAL8 tmpz = *vz;
114 
115  REAL8 tmp1 = tmpx*cosangle - tmpy*sinangle;
116  REAL8 tmp2 = tmpx*sinangle + tmpy*cosangle;
117 
118  *vx = tmp1;
119  *vy = tmp2;
120  *vz = tmpz;
121 }
122 
123 void IMRPhenomT_rotate_y(REAL8 cosangle, REAL8 sinangle, REAL8 *vx, REAL8 *vy, REAL8 *vz)
124 {
125  const REAL8 tmpx = *vx;
126  const REAL8 tmpy = *vy;
127  const REAL8 tmpz = *vz;
128 
129  REAL8 tmp1 = + tmpx*cosangle + tmpz*sinangle;
130  REAL8 tmp2 = - tmpx*sinangle + tmpz*cosangle;
131 
132  *vx = tmp1;
133  *vy = tmpy;
134  *vz = tmp2;
135 }
136 
137 /* Function to unwrap a time series that contains an angle, to obtain a continuous time series. */
138 void unwrap_array(double *in, double *out, int len);
139 void unwrap_array(double *in, double *out, int len) {
140  out[0] = in[0];
141  for (int i = 1; i < len; i++) {
142  double d = in[i] - in[i-1];
143  d = d > M_PI ? d - 2 * M_PI : (d < -M_PI ? d + 2 * M_PI : d);
144  out[i] = out[i-1] + d;
145  }
146 }
147 
148 /* Struct needed for numerical integration of the spin precessing equations */
149 typedef struct tagPhenomTPHMEvolution
150 {
151  gsl_spline *v_spline;
152  gsl_interp_accel *v_acc;
156 
157 /*******************************************************/
158 /*************** ANALYTICAL EULER ANGLES ***************/
159 /*******************************************************/
160 
161 
162 /** This function provides a wrapper to the analytical PN angle descriptions computed by
163 the IMRPhenomXP(HM) waveform models and routines to incorporate a simple approximation in the ringdown
164 for the precessing angles (see https://arxiv.org/pdf/1209.3712.pdf, https://arxiv.org/abs/1806.10734, https://arxiv.org/abs/2004.08302).
165 It provides also additional ways of treating the plunge-merger angles and possibility to compute third
166 Euler angle gamma directly from the minimal rotation condition. **/
167 
168 /* Summary of options:
169 - EulerRDVersion: 0 (no RD angles attached, MSA/NNLO computed for the whole waveform)
170  1 (MSA/NNLO computed up to the peak time of the 22 mode, then RD angles are attached.)
171  2 (MSA/NNLO computed up to MECO time, linear continuation performed up to the peak time, then RD angles are attached.)
172 
173 - GammaVersion: 0 (Gamma is computed from the MSA/NNLO PN expressions.)
174  1 (Gamma computed numerically from minimal rotation condition.)
175 */
176 
178  REAL8TimeSeries **alphaTS, /* Alpha angle time series [out] */
179  REAL8TimeSeries **cosbetaTS, /* cos(Beta) angle time series [out] */
180  REAL8TimeSeries **gammaTS, /* Gamma angle time series [out] */
181  REAL8Sequence *xorb, /* x(t)=v(t)^2 time series [in] */
182  IMRPhenomTWaveformStruct *pWF, /* IMRPhenomT* waveform struct */
183  IMRPhenomTPhase22Struct *pPhase, /* IMRPhenomT phase struct */
184  IMRPhenomXWaveformStruct *pWFX, /* IMRPhenomX waveform struct */
185  IMRPhenomXPrecessionStruct *pPrec, /* IMRPhenomX precessing struct */
186  INT4 EulerRDVersion, /* Version of ringdown angles attachment */
187  INT4 GammaVersion); /* Version of gamma evaluation */
188 
190  REAL8TimeSeries **alphaTS,
191  REAL8TimeSeries **cosbetaTS,
192  REAL8TimeSeries **gammaTS,
193  REAL8Sequence *xorb,
195  IMRPhenomTPhase22Struct *pPhase,
198  INT4 EulerRDVersion,
199  INT4 GammaVersion)
200 {
201 
202  /***** Set up analytical PN evaluation length for the different versions ****/
203 
204  size_t length = 0; // Initialize length
205  REAL8 tMECO = IMRPhenomT_MECOTime(pWF->eta, pWF->Shat, pWF->dchi, pWF->delta); //MECO time needed for EulerRDVersion=2
206 
207  /* If minimum time is greater that tMECO, EulerRDVersion=2 is not allowed. Defaulting to EulerRDVersion=1. */
208  if(pPhase->tmin>=tMECO - 10*pPhase->dtM && EulerRDVersion == 2)
209  {
210  EulerRDVersion = 1;
211  XLAL_PRINT_WARNING("Waveform is too short for EulerRDVersion=2. Defaulting to EulerRDVersion=1.\n");
212  }
213  if(pPhase->tmin>= - 10*pPhase->dtM && GammaVersion == 1)
214  {
215  GammaVersion = 0;
216  XLAL_PRINT_WARNING("Waveform is too short for GammaVersion=1. Defaulting to GammaVersion=0.\n");
217  }
218 
219  /* Check if spins are almost aligned. In this case, disable ringdown angles addition */
220  REAL8 AStol = 1E-4;
221  if((sqrt(pPrec->chi1x * pPrec->chi1x + pPrec->chi1y * pPrec->chi1y) < AStol) &&
222  (sqrt(pPrec->chi2x * pPrec->chi2x + pPrec->chi2y * pPrec->chi2y) < AStol))
223  {
224  EulerRDVersion = 0;
225  XLAL_PRINT_WARNING("System is almost aligned spin. Defaulting to EulerRDVersion=0.\n");
226  }
227 
228  /* Different versions of the ringdown Euler angles attachment.
229  Depending on the version, inspiral angles need to be computed only up to a certain length. */
230  if(EulerRDVersion == 0)
231  {
232  length = pPhase->wflength; // Whole waveform length
233  }
234  else if(EulerRDVersion == 1)
235  {
236  if(pPhase->tmin>=0) // If waveform contains only ringdown, set PN length to 0.
237  {
238  length = 0;
239  }
240  else{
241  length = floor((0.0 - pPhase->tmin)/pWF->dtM); // If not, set PN length from tmin to t=0 (peak time)
242  }
243  }
244  else if(EulerRDVersion == 2)
245  {
246  REAL8 t0 = tMECO;
247  length = floor((t0 - pPhase->tmin)/pWF->dtM); // set PN length from tmin to tMECO
248  }
249 
250  /* Offset for alpha angle. Essentially is the initial azimuthal angle of Lhat in the J frame */
251  REAL8 alphaOff = atan2(pPrec->S1y + pPrec->S2y, pPrec->S1x + pPrec->S2x) - LAL_PI;
252 
253  REAL8 tt;
254 
255  /* Analytical Euler angles from PhenomX implementation */
256  switch(pPrec->IMRPhenomXPrecVersion)
257  {
258  /* ~~~~~ Use NNLO PN Euler Angles - Appendix G of arXiv:2004.06503 and https://dcc.ligo.org/LIGO-T1500602 ~~~~~ */
259  case 101:
260  case 102:
261  case 103:
262  case 104:
263  {
264  REAL8 s, s2, cos_beta, omega, omega_cbrt2, omega_cbrt, logomega, v, L;
265 
266  /* Compute values at reference frequency */
267 
268  omega_cbrt2 = pow(LAL_PI*pWF->MfRef,2./3);
269  omega_cbrt = sqrt(omega_cbrt2);
270  omega = omega_cbrt2*omega_cbrt;
271  logomega = log(omega);
272 
273  REAL8 alphaRef = IMRPhenomX_PN_Euler_alpha_NNLO(pPrec,omega,omega_cbrt2,omega_cbrt,logomega) ;
274  REAL8 gammaRef = -IMRPhenomX_PN_Euler_epsilon_NNLO(pPrec,omega,omega_cbrt2,omega_cbrt,logomega);
275 
276  /* Loop for evaluating NNLO angles in the desired points */
277  for(UINT4 jdx = 0; jdx < length; jdx++)
278  {
279  omega_cbrt2 = xorb->data[jdx];
280  omega_cbrt = sqrt(omega_cbrt2);
281  v = omega_cbrt;
282  omega = omega_cbrt2*omega_cbrt;
283  logomega = log(omega);
284 
285  (*alphaTS)->data->data[jdx] = IMRPhenomX_PN_Euler_alpha_NNLO(pPrec,omega,omega_cbrt2,omega_cbrt,logomega) - alphaRef + alphaOff ;
286  (*gammaTS)->data->data[jdx] = -IMRPhenomX_PN_Euler_epsilon_NNLO(pPrec,omega,omega_cbrt2,omega_cbrt,logomega) - gammaRef - alphaOff ;
287 
288  L = XLALSimIMRPhenomXLPNAnsatz(v, pWFX->eta/v, pPrec->L0, pPrec->L1, pPrec->L2, pPrec->L3, pPrec->L4, pPrec->L5, pPrec->L6, pPrec->L7, pPrec->L8, pPrec->L8L);
289 
290  s = pPrec->Sperp / (L + pPrec->SL);
291  s2 = s*s;
292  cos_beta = copysign(1.0, L + pPrec->SL) / sqrt(1.0 + s2);
293 
294  (*cosbetaTS)->data->data[jdx] = cos_beta;
295  }
296 
297  break;
298  }
299  case 220:
300  case 221:
301  case 222:
302  case 223:
303  case 224:
304  {
305  vector vangles = {0.,0.,0.};
306  REAL8 v, cos_beta;
307 
308  /* ~~~~~ Euler Angles from Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967 ~~~~~ */
309  for(UINT4 jdx = 0; jdx < length; jdx++)
310  {
311  v = sqrt(xorb->data[jdx]);
312 
313  vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWFX,pPrec);
314 
315  (*alphaTS)->data->data[jdx] = vangles.x + alphaOff;
316  (*gammaTS)->data->data[jdx] = -vangles.y - alphaOff;
317  cos_beta = vangles.z;
318  (*cosbetaTS)->data->data[jdx] = cos_beta;
319  }
320 
321  break;
322  }
323  default:
324  {
325  XLAL_ERROR(XLAL_EINVAL,"Error: IMRPhenomXPrecessionVersion not recognized. Recommended default is 223.\n");
326  break;
327  }
328  }
329 
330  /**** COMPUTE GAMMA FROM MINIMAL ROTATION CONDITION (if specified) ****/
331  /* alpha and cosbeta are interpolated to compute the RHS of the minimal rotation condition.
332  Then gamma is integrated using Boole's rule. */
333  if(GammaVersion == 1 && length>0)
334  {
335  //printf("length: %zu\n",length);
336  // Generate time array
337  REAL8Sequence *timesPN = NULL;
338  timesPN = XLALCreateREAL8Sequence(length);
339  for(UINT8 i=0; i < length; i++)
340  {
341  timesPN->data[i] = i*pWF->dtM;
342  }
343 
344  // Interpolate alpha
345  gsl_interp_accel *accel_alpha;
346  gsl_spline *spline_alpha;
347 
348  accel_alpha = gsl_interp_accel_alloc();
349  spline_alpha = gsl_spline_alloc(gsl_interp_cspline, length);
350  gsl_spline_init(spline_alpha, timesPN->data, (*alphaTS)->data->data, length);
351 
352  // Interpolate cosbeta
353  gsl_interp_accel *accel_cosb;
354  gsl_spline *spline_cosb;
355 
356  accel_cosb = gsl_interp_accel_alloc();
357  spline_cosb = gsl_spline_alloc(gsl_interp_cspline, length);
358  gsl_spline_init(spline_cosb, timesPN->data, (*cosbetaTS)->data->data, length);
359 
360  // Setup helper struct for gamma integration
361  gammaIntegration gammaStruct;
362  gammaStruct.alpha_spline = spline_alpha;
363  gammaStruct.alpha_acc = accel_alpha;
364  gammaStruct.cosbeta_spline = spline_cosb;
365  gammaStruct.cosbeta_acc = accel_cosb;
366 
367  // Integrate gamma using Boole's rule. (https://en.wikipedia.org/wiki/Boole%27s_rule)
368  // Gamma is obtained through the minimal rotation condition: \dot{gamma} = -\dot{alpha(t)}*cos[beta(t)]
369  for( UINT4 i = 1; i < timesPN->length; i++ ){
370  double t1 = timesPN->data[i-1];
371  double t2 = timesPN->data[i];
372  (*gammaTS)->data->data[i] = (*gammaTS)->data->data[i-1] + (1.0/90.0)*(t2 - t1)*(7.0*f_alphadotcosi(t1,&gammaStruct)
373  + 32.0*f_alphadotcosi((t1+3.0*t2)/4.0,&gammaStruct)
374  + 12.0*f_alphadotcosi((t1+t2)/2.0,&gammaStruct)
375  + 32.0*f_alphadotcosi((3.0*t1+t2)/4.0,&gammaStruct)
376  + 7.0*f_alphadotcosi(t2,&gammaStruct));/* Boole's Rule */
377 
378  }
379 
380  // Free gsl spline objects
381  gsl_spline_free(spline_alpha);
382  gsl_spline_free(spline_cosb);
383  gsl_interp_accel_free(accel_alpha);
384  gsl_interp_accel_free(accel_cosb);
385 
386  XLALDestroyREAL8Sequence( timesPN);
387 
388  }
389 
390  /**** RINGDOWN APPROXIMATION FOR EULER ANGLES ****/
391  /* (see https://arxiv.org/pdf/1209.3712.pdf, https://arxiv.org/abs/1806.10734, https://arxiv.org/abs/2004.08302)
392  Analytical ringdown approximation for the Euler angles is discussed in the above papers.
393  Here, that approximation is attached to the Euler angles time series at t=0, i.e the peak time of the non-precessing 22 mode.
394  For EulerRDVersion=2, the angles inthe strong field regime between the MECO time (end of validity of PN framework) and the peak time
395  are modelled also by a simple analytical linear continuation, to try to mitigative possible PN pathologies in this region. */
396 
397  if(EulerRDVersion == 1)
398  {
399  /* Offsets for the ringdown angles, in order to impose continuity with inspiral angles */
400 
401  // Offsets initialization
402  REAL8 alphaRD0 = 0.0;
403  REAL8 cosbetaRD0 = 0.0;
404  REAL8 gammaRD0 = 0.0;
405 
406  /* If waveform too short to contain prepeak cycles, impose offsets */
407  if(pPhase->tmin>=-pWF->dtM)
408  {
409  alphaRD0 = alphaOff;
410  cosbetaRD0 = cos(pPrec->thetaJ_Sf);
411  gammaRD0 = -alphaOff;
412  length = 0;
413  }
414  /* If waveform long enough, set offsets for continuity with preRD region */
415  else{
416  alphaRD0 = (*alphaTS)->data->data[length-1];
417  cosbetaRD0 = (*cosbetaTS)->data->data[length-1];
418  gammaRD0 = (*gammaTS)->data->data[length-1];
419  }
420 
421  /* Compute Euler angles in the RD approximation */
422  for(UINT4 jdx = length; jdx < pPhase->wflength; jdx++)
423  {
424  tt = (jdx - length)*pWF->dtM;
425  (*alphaTS)->data->data[jdx] = alphaRD0 + pPhase->EulerRDslope*tt;
426  (*cosbetaTS)->data->data[jdx] = cosbetaRD0;
427  (*gammaTS)->data->data[jdx] = gammaRD0 - (*alphaTS)->data->data[jdx]*(*cosbetaTS)->data->data[jdx] + alphaRD0*cosbetaRD0;
428  }
429 
430  }
431 
432  /*** Alternative way of treating Euler angles during plunge-merger ***/
433  /* MSA/NNLO angles computed up tp the MECO time, and then linear continuation to the peak time. Then Euler RD angles are attached. */
434  else if(EulerRDVersion == 2)
435  {
436 
437  REAL8 t0 = tMECO;
438  size_t lengthInt = floor((t0 - pPhase->tmin)/pWF->dtM); // Length from tmin to tMECO
439  length = floor((0.0 - pPhase->tmin)/pWF->dtM); // Length from tmin to t=0
440 
441  /* beta values for computing beta derivative */
442  REAL8 beta1 = acos((*cosbetaTS)->data->data[lengthInt-1]);
443  REAL8 beta2 = acos((*cosbetaTS)->data->data[lengthInt-2]);
444  REAL8 beta3 = acos((*cosbetaTS)->data->data[lengthInt-3]);
445 
446  // Compute alpha and cosbeta derivatives at tMECO using three point backwards discretization.
447  REAL8 alphader = (3.0*(*alphaTS)->data->data[lengthInt-1] - 4.0*(*alphaTS)->data->data[lengthInt-2] + (*alphaTS)->data->data[lengthInt-3])/(2*pWF->dtM);
448  REAL8 betader = (3.0*beta1 - 4.0*beta2 + beta3)/(2*pWF->dtM);
449 
450  //printf("alphader: %.16f, betader: %.16f\n",alphader, betader);
451 
452  // Angle values at tMECO.
453  REAL8 alphaInt0 = (*alphaTS)->data->data[lengthInt-1];
454  REAL8 betaInt0 = beta1;
455  REAL8 gammaInt0 = (*gammaTS)->data->data[lengthInt-1];
456 
457 
458  // Compute alpha and cosbeta as a linear continuation from tMECO to peak time. Gamma analytically computed from minimal rotation condition.
459  int kk = 0;
460  REAL8 betalin = 0.0;
461 
462  if(fabs(betader)>1E-15)
463  {
464  for(UINT4 jdx = lengthInt; jdx < length; jdx++)
465  {
466  (*alphaTS)->data->data[jdx] = alphaInt0 + alphader*kk*pWF->dtM;
467  betalin = betaInt0 + betader*kk*pWF->dtM;
468  if(betalin > LAL_PI){betalin=LAL_PI; XLAL_PRINT_INFO("Warning: beta>180º reached, correcting to 180º.");}
469  if(betalin < 0.0){betalin=0.0; XLAL_PRINT_INFO("Warning: beta<0º reached, correcting to 0º.");}
470  (*cosbetaTS)->data->data[jdx] = cos(betalin);
471  (*gammaTS)->data->data[jdx] = gammaInt0 - (alphader/betader)*sin(betaInt0 + betader*kk*pWF->dtM) + (alphader/betader)*sin(betaInt0);
472  kk++;
473  }
474  }
475  else{
476  for(UINT4 jdx = lengthInt; jdx < length; jdx++)
477  {
478  tt = (jdx - lengthInt)*pWF->dtM;
479  (*alphaTS)->data->data[jdx] = alphaInt0 + alphader*kk*pWF->dtM;
480  betalin = betaInt0 + betader*kk*pWF->dtM;
481  if(betalin > LAL_PI){betalin=LAL_PI; XLAL_PRINT_INFO("Warning: beta>pi reached, correcting to pi.");}
482  if(betalin < 0.0){betalin=0.0; XLAL_PRINT_INFO("Warning: beta<0 reached, correcting to 0.");}
483  (*cosbetaTS)->data->data[jdx] = cos(betalin);
484  (*gammaTS)->data->data[jdx] = gammaInt0 - alphader*tt*cos(betaInt0);
485  kk++;
486  }
487  }
488 
489  // At peak time, attach Euler angles computed from the RD approximation.
490  for(UINT4 jdx = length; jdx < pPhase->wflength; jdx++)
491  {
492  tt = (jdx - length)*pWF->dtM;
493  (*alphaTS)->data->data[jdx] = (*alphaTS)->data->data[length-1] + pPhase->EulerRDslope*tt;
494  (*cosbetaTS)->data->data[jdx] = (*cosbetaTS)->data->data[length-1];
495  (*gammaTS)->data->data[jdx] = (*gammaTS)->data->data[length-1] - (*alphaTS)->data->data[jdx]*(*cosbetaTS)->data->data[jdx] + (*alphaTS)->data->data[length-1]*(*cosbetaTS)->data->data[length-1];
496  }
497 
498  }
499 
500  return XLAL_SUCCESS;
501 
502 }
503 
504 /*******************************************************/
505 /*************** NUMERICAL EULER ANGLES ****************/
506 /*******************************************************/
507 
508 /* This section provides the needed functions to perform the evolution of the spin precessing equations, that are specified in XLALSimInspiralSpinDerivativesAvg and
509 related SimInspiral code employing the PN expansion parameter v(t) computed from the orbital frequency predicted by IMRPhenomT, instead of evolving v from
510 a TaylorT* approximant. */
511 
512 /* Function needed by RungeKutta integrator, we just provide a trivial GSL_SUCCESS since TaylorT* stopping conditions do not apply here. */
513 int StoppingTest(double UNUSED t, const double UNUSED values[], double UNUSED dvalues[], void UNUSED *mparams);
514 
515 int StoppingTest(double UNUSED t, const double UNUSED values[], double UNUSED dvalues[], void UNUSED *mparams)
516 {
517  return GSL_SUCCESS;
518 }
519 
520 /* Function to provide RHS of the spin precessing equations to the integrator, analogous to XLALSimInspiralSpinTaylorT{1/4/5}Derivatives function.
521 Main difference is that here v(t) is read from an interpolated spline, instead of evolved from a vdot expression. */
523  REAL8 UNUSED t,
524  const REAL8 values[],
525  REAL8 dvalues[],
526  void *mparams
527  );
528 
530  REAL8 UNUSED t,
531  const REAL8 values[],
532  REAL8 dvalues[],
533  void *mparams
534  )
535 {
536  INT4 status;
537 
538  /* coordinates and derivatives */
539  REAL8 LNhx, LNhy, LNhz, S1x, S1y, S1z, S2x, S2y, S2z, E1x, E1y, E1z;
540  REAL8 dLNhx, dLNhy, dLNhz, dS1x, dS1y, dS1z, dS2x, dS2y, dS2z, dE1x, dE1y, dE1z;
541 
542  /* auxiliary variables */
543  REAL8 v;
544  REAL8 LNhdotS1, LNhdotS2;
545 
546  /* Structs */
547  PhenomTPHMEvolution *params = mparams;
548  XLALSimInspiralSpinTaylorTxCoeffs *Tparams = params->Tparams;
549 
550  /* copy variables */
551  LNhx = values[0] ; LNhy = values[1] ; LNhz = values[2] ;
552  S1x = values[3] ; S1y = values[4] ; S1z = values[5] ;
553  S2x = values[6] ; S2y = values[7] ; S2z = values[8];
554  E1x = values[9]; E1y = values[10]; E1z = values[11];
555 
556  /* Variables for relating integration time t with time employed in the interpolation of v(t) */
557  REAL8 toff = fabs(params->ToffSign);
558  REAL8 sign = copysign(1.0, params->ToffSign);
559 
560  /* Evaluate v(t) from an interpolation of IMRPhenomT v(t) */
561  v = gsl_spline_eval( params->v_spline, toff + sign*t, params->v_acc );
562 
563  /* Compute derivatives of quantities that are evolved */
564  LNhdotS1 = (LNhx*S1x + LNhy*S1y + LNhz*S1z);
565  LNhdotS2 = (LNhx*S2x + LNhy*S2y + LNhz*S2z);
566 
567  status = XLALSimInspiralSpinDerivativesAvg(&dLNhx,&dLNhy,&dLNhz,&dE1x,&dE1y,&dE1z,&dS1x,&dS1y,&dS1z,&dS2x,&dS2y,&dS2z,v,LNhx,LNhy,LNhz,E1x,E1y,E1z,S1x,S1y,S1z,S2x,S2y,S2z,LNhdotS1,LNhdotS2,Tparams);
568  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: function XLALSimInspiralSpinDerivatives has failed.");
569 
570  dvalues[0] = sign*dLNhx; dvalues[1] = sign*dLNhy ; dvalues[2] = sign*dLNhz;
571  dvalues[3] = sign*dS1x ; dvalues[4] = sign*dS1y ; dvalues[5] = sign*dS1z ;
572  dvalues[6] = sign*dS2x ; dvalues[7] = sign*dS2y ; dvalues[8] = sign*dS2z ;
573  dvalues[9] = sign*dE1x ; dvalues[10] = sign*dE1y ; dvalues[11] = sign*dE1z ;
574 
575  return GSL_SUCCESS;
576 }
577 
578 
579 /* Function to evolve LNhat and individual spins using IMRPhenomT phase evolution.
580  Similar to XLALSimInspiralSpinTaylorPNEvolveOrbit. */
581 
583  REAL8TimeSeries **V, /**< post-Newtonian parameter [returned]*/
584  REAL8TimeSeries **S1x, /**< Spin1 vector x component [returned]*/
585  REAL8TimeSeries **S1y, /**< " " " y component [returned]*/
586  REAL8TimeSeries **S1z, /**< " " " z component [returned]*/
587  REAL8TimeSeries **S2x, /**< Spin2 vector x component [returned]*/
588  REAL8TimeSeries **S2y, /**< " " " y component [returned]*/
589  REAL8TimeSeries **S2z, /**< " " " z component [returned]*/
590  REAL8TimeSeries **LNhatx, /**< unit orbital ang. mom. x [returned]*/
591  REAL8TimeSeries **LNhaty, /**< " " " y component [returned]*/
592  REAL8TimeSeries **LNhatz, /**< " " " z component [returned]*/
593  REAL8TimeSeries **E1x, /**< orb. plane basis vector x[returned]*/
594  REAL8TimeSeries **E1y, /**< " " " y component [returned]*/
595  REAL8TimeSeries **E1z, /**< " " " z component [returned]*/
596  REAL8Sequence *xorb, /**< squared velocity from IMRPhenomT phase evolution*/
597  REAL8 m1_SI, /**< Mass of companion 1 (kg) */
598  REAL8 m2_SI, /**< Mass of companion 2 (kg) */
599  REAL8 s1x, /**< x component of primary spin at reference frequency*/
600  REAL8 s1y, /**< y component of primary spin at reference frequency*/
601  REAL8 s1z, /**< z component of primary spin at reference frequency*/
602  REAL8 s2x, /**< x component of secondary spin at reference frequency*/
603  REAL8 s2y, /**< y component of secondary spin at reference frequency*/
604  REAL8 s2z, /**< z component of secondary spin at reference frequency*/
605  IMRPhenomTWaveformStruct *pWF, /**< PhenomTHM waveform struct*/
606  IMRPhenomTPhase22Struct *pPhase /**< PhenomT phase struct*/
607  );
608 
609 /**
610  * @addtogroup LALSimIMRPhenomT_c
611  * @{
612  *
613  * @name Routines for spin evolution
614  * @{
615  *
616  * @author Héctor Estellés
617  *
618  * @brief C Code for the spin evolution of the IMRPhenomTP(HM) models.
619  */
620 /**
621  * Function to return the time series for the evolution of the individual spins, the Newtonian angular momentum direction and the orbital plane basis vector E.
622  * It computes the frequency of the non-precessing 22 mode between minimum frequency and the peak time of the 22,
623  * and approximates the PN parameter v(t) from it. Thenn it calls the internal function IMRPhenomTPHM_EvolveOrbit which evolves the spin precessing PN evolution equations
624  * using the precomputed v(t) as a driver for the evolution.
625  */
626 
627 int XLALSimIMRPhenomTPHM_EvolveOrbit( //FIXME: maybe move to Euler angles? Maria will take a look
628  REAL8TimeSeries **V, /**< post-Newtonian parameter [returned]*/
629  REAL8TimeSeries **S1x, /**< Spin1 vector x component [returned]*/
630  REAL8TimeSeries **S1y, /**< Spin1 vector y component [returned]*/
631  REAL8TimeSeries **S1z, /**< Spin1 vector z component [returned]*/
632  REAL8TimeSeries **S2x, /**< Spin2 vector x component [returned]*/
633  REAL8TimeSeries **S2y, /**< Spin2 vector y component [returned]*/
634  REAL8TimeSeries **S2z, /**< Spin2 vector z component [returned]*/
635  REAL8TimeSeries **LNhatx, /**< unit orbital ang. mom. x [returned]*/
636  REAL8TimeSeries **LNhaty, /**< unit orbital ang. mom. y [returned]*/
637  REAL8TimeSeries **LNhatz, /**< unit orbital ang. mom. z [returned]*/
638  REAL8TimeSeries **E1x, /**< orb. plane basis vector x [returned]*/
639  REAL8TimeSeries **E1y, /**< orb. plane basis vector y [returned]*/
640  REAL8TimeSeries **E1z, /**< orb. plane basis vector z [returned]*/
641  REAL8 m1_SI, /**< Mass of companion 1 (kg) */
642  REAL8 m2_SI, /**< Mass of companion 2 (kg) */
643  REAL8 chi1x, /**< x component of primary spin*/
644  REAL8 chi1y, /**< y component of primary spin*/
645  REAL8 chi1z, /**< z component of primary spin */
646  REAL8 chi2x, /**< x component of secondary spin*/
647  REAL8 chi2y, /**< y component of secondary spin*/
648  REAL8 chi2z, /**< z component of secondary spin */
649  REAL8 deltaT, /**< sampling interval (s) */
650  REAL8 fmin, /**< starting GW frequency (Hz) */
651  REAL8 fRef, /**< reference GW frequency (Hz) */
652  REAL8 phiRef, /**< reference orbital phase (rad) */
653  LALDict *lalParams /**< LAL dictionary containing accessory parameters */
654  )
655 {
656 
657  int status;
658 
659  /* Sanity checks */
660  if(fRef < 0.0) { XLAL_ERROR(XLAL_EDOM, "fRef_In must be positive or set to 0 to ignore.\n"); }
661  if(deltaT <= 0.0) { XLAL_ERROR(XLAL_EDOM, "deltaT must be positive.\n"); }
662  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
663  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
664  if(fmin <= 0.0) { XLAL_ERROR(XLAL_EDOM, "f_min must be positive.\n"); }
665  if(fRef > 0.0 && fRef < fmin){ XLAL_ERROR(XLAL_EDOM, "fRef must be >= f_min or =0 to use f_min.\n"); }
666 
667  /* Swap components if m2>m1 */
668  if(m1_SI < m2_SI)
669  {
670  REAL8 auxswap;
671 
672  auxswap = m2_SI;
673  m2_SI = m1_SI;
674  m1_SI = auxswap;
675 
676  auxswap = chi2x;
677  chi2x = chi1x;
678  chi1x = auxswap;
679 
680  auxswap = chi2y;
681  chi2y = chi1y;
682  chi1y = auxswap;
683 
684  auxswap = chi2z;
685  chi2z = chi1z;
686  chi1z = auxswap;
687  }
688 
689  REAL8 mass_ratio = m1_SI / m2_SI;
690  REAL8 chi1L = chi1z;
691  REAL8 chi2L = chi2z;
692 
693  /*
694  Perform a basic sanity check on the region of the parameter space in which model is evaluated. Behaviour is as follows:
695  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
696  - For 200 > mass ratio > 20 and spins <= 0.9: print a warning message that model can be pathological.
697  - For mass ratios > 200: throw a hard error that model is not valid.
698  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
699  */
700 
701  if(mass_ratio > 20.0 && chi1L < 0.9 && m2_SI/LAL_MSUN_SI >= 0.5 ) { XLAL_PRINT_INFO("Warning: Extrapolating outside of Numerical Relativity calibration domain."); }
702  if(mass_ratio > 20.0 && (chi1L >= 0.9 || m2_SI/LAL_MSUN_SI < 0.5) ) { XLAL_PRINT_INFO("Warning: Model can be pathological at these parameters"); }
703  if(mass_ratio > 200. && fabs(mass_ratio - 200) > 1e-12) { XLAL_ERROR(XLAL_EDOM, "ERROR: Model not valid at mass ratios beyond 200."); } // The 1e-12 is to avoid rounding errors
704  if(fabs(chi1L) > 0.99 || fabs(chi2L) > 0.99) { XLAL_PRINT_INFO("Warning: Extrapolating to extremal spins, model is not trusted."); }
705 
706 
708  pWF = XLALMalloc(sizeof(IMRPhenomTWaveformStruct));
709  status = IMRPhenomTSetWaveformVariables(pWF, m1_SI, m2_SI, chi1z, chi2z, 1.0, deltaT, fmin, fRef, phiRef, lalParams);
710  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: Internal function IMRPhenomTSetWaveformVariables has failed.");
711 
712  IMRPhenomTPhase22Struct *pPhase;
713  pPhase = XLALMalloc(sizeof(IMRPhenomTPhase22Struct));
715  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: Internal function IMRPhenomTSetPhase22Coefficients has failed.");
716 
717  /* Length of the required sequence and length of the different inspiral regions of the 22 phase. */
718  size_t length = pPhase->wflength;
719  size_t length_insp_early = pPhase->wflength_insp_early;
720  size_t length_insp_late = pPhase->wflength_insp_late;
721  size_t length_insp = length_insp_early + length_insp_late;
722 
723  /*Initialize time */
724  REAL8 t, thetabar, theta, w22;
725  REAL8 factheta = pow(5.0,1./8);
726 
727  /* Initialize REAL8 sequences for storing the phase and the frequency of the 22 */
728  REAL8Sequence *xorb = NULL;
729  xorb = XLALCreateREAL8Sequence(length);
730 
731  /* Compute x=(0.5*omega_22)^(2/3).
732  Depending on the inspiral region, theta is defined with a fitted t0 parameter or with t0=0. */
733 
734  if(pWF->inspVersion!=0) // If reconstruction is non-default, this will compute frequency, phase and PN expansion parameter x from TaylorT3 with fitted t0 for the early inspiral region-
735  {
736  for(UINT4 jdx = 0; jdx < length_insp_early; jdx++)
737  {
738  t = pPhase->tmin + jdx*pWF->dtM;
739 
740  thetabar = pow(pWF->eta*(pPhase->tt0-t),-1./8);
741 
742  theta = factheta*thetabar;
743 
744  w22 = IMRPhenomTomega22(t, theta, pWF, pPhase);
745  xorb->data[jdx] = pow(0.5*w22,2./3);
746  }
747 
748  for(UINT4 jdx = length_insp_early; jdx < length_insp; jdx++) // For times later than the early-late inspiral boundary, it computes phase, frequency and x with the extended TaylorT3 (with tt0=0)
749  {
750  t = pPhase->tmin + jdx*pWF->dtM;
751 
752  thetabar = pow(-pWF->eta*t,-1./8);
753 
754  theta = factheta*thetabar;
755 
756  w22 = IMRPhenomTomega22(t, theta, pWF, pPhase);
757  xorb->data[jdx] = pow(0.5*w22,2./3);
758  }
759 
760  }
761 
762  else // If default reconstruction, only one inspiral region with extended TaylorT3 (with tt0=0) is employed up to the inspiral-merger boundary time
763  {
764  for(UINT4 jdx = 0; jdx < length_insp; jdx++)
765  {
766  t = pPhase->tmin + jdx*pWF->dtM;
767 
768  thetabar = pow(-pWF->eta*t,-1./8);
769 
770  theta = factheta*thetabar;
771 
772  w22 = IMRPhenomTomega22(t, theta, pWF, pPhase);
773  xorb->data[jdx] = pow(0.5*w22,2./3);
774  }
775 
776  }
777 
778  /* During the 22 phase merger region, phase and x are also computed because the higher modes end the inspiral at a fixed time,
779  which can occur later than the end of the 22 inspiral. */
780  for(UINT4 jdx = length_insp; jdx < length; jdx++)
781  {
782  t = pPhase->tmin + jdx*pWF->dtM;
783 
784  w22 = IMRPhenomTomega22(t, 0.0, pWF, pPhase);
785  xorb->data[jdx] = pow(0.5*w22,2./3);
786  }
787 
788  /* Compute evolution of LNhat and individual spins */
789  status = IMRPhenomTPHM_EvolveOrbit(V,S1x,S1y,S1z,S2x,S2y,S2z,LNhatx,LNhaty,LNhatz,E1x,E1y,E1z,xorb,m1_SI,m2_SI,chi1x,chi1y,chi1z,chi2x,chi2y,chi2z,pWF,pPhase);
790  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: Internal function IMRPhenomTPHM_EvolveOrbit has failed.");
791 
792  LALFree(pWF);
793  LALFree(pPhase);
795 
796  return status;
797 }
798 /** @} */
799 /** @} */
800 
801 /* Evolve the spin precessing PN evolution equations using the precomputed v(t) as a driver for the evolution. */
803  REAL8TimeSeries **V, /**< post-Newtonian parameter [returned]*/
804  REAL8TimeSeries **S1x, /**< Spin1 vector x component [returned]*/
805  REAL8TimeSeries **S1y, /**< " " " y component [returned]*/
806  REAL8TimeSeries **S1z, /**< " " " z component [returned]*/
807  REAL8TimeSeries **S2x, /**< Spin2 vector x component [returned]*/
808  REAL8TimeSeries **S2y, /**< " " " y component [returned]*/
809  REAL8TimeSeries **S2z, /**< " " " z component [returned]*/
810  REAL8TimeSeries **LNhatx, /**< unit orbital ang. mom. x [returned]*/
811  REAL8TimeSeries **LNhaty, /**< " " " y component [returned]*/
812  REAL8TimeSeries **LNhatz, /**< " " " z component [returned]*/
813  REAL8TimeSeries **E1x, /**< orb. plane basis vector x[returned]*/
814  REAL8TimeSeries **E1y, /**< " " " y component [returned]*/
815  REAL8TimeSeries **E1z, /**< " " " z component [returned]*/
816  REAL8Sequence *xorb, /**< squared velocity from IMRPhenomT phase evolution*/
817  REAL8 m1_SI, /**< Mass of companion 1 (kg) */
818  REAL8 m2_SI, /**< Mass of companion 2 (kg) */
819  REAL8 s1x, /**< x component of primary spin at reference frequency*/
820  REAL8 s1y, /**< y component of primary spin at reference frequency*/
821  REAL8 s1z, /**< z component of primary spin at reference frequency*/
822  REAL8 s2x, /**< x component of secondary spin at reference frequency*/
823  REAL8 s2y, /**< y component of secondary spin at reference frequency*/
824  REAL8 s2z, /**< z component of secondary spin at reference frequency*/
825  IMRPhenomTWaveformStruct *pWF, /**< PhenomTHM waveform struct*/
826  IMRPhenomTPhase22Struct *pPhase /**< PhenomT phase struct*/
827  )
828 {
829  /* Initialize objects and variables needed for the RK integrator */
830 
831  LALAdaptiveRungeKuttaIntegrator *integrator = NULL; /* GSL integrator object */
832  INT4 intreturn; /* Variable for storing status returned by the integrator */
833  REAL8 yinit[12]; /* initial values of parameters */
834  REAL8Array *yout; /* time series of variables returned from integrator */
835 
836  /* intermediate variables */
837  int i, len, len2;
838  REAL8 norm1, norm2, m1sec, m2sec, Msec;
839 
840  /* Standard tests for the input/ouput time series */
841  if ( !V || !S1x || !S1y || !S1z || !S2x || !S2y || !S2z
842  || !LNhatx || !LNhaty || !LNhatz || !E1x || !E1y || !E1z )
843  {
844  XLALPrintError("XLAL Error - %s: NULL(s) in output parameters\n",
845  __func__);
847  }
848 
849  /* Set integration length and interpolation length */
850  //size_t lengthAll = floor((0.0 - pPhase->tmin)/pWF->dtM); // Waveform length up to the peak time: Integration length
851  size_t lengthAll2 = xorb->length; // Full waveform length
852 
853  /* Interpolation of "orbital" v */
854  REAL8Sequence *timesPN = NULL;
855  REAL8Sequence *vorb = NULL;
856  timesPN = XLALCreateREAL8Sequence(lengthAll2);
857  vorb = XLALCreateREAL8Sequence(lengthAll2);
858 
859  for(UINT4 idx=0; idx<lengthAll2; idx++)
860  {
861  timesPN->data[idx] = idx*pWF->dtM;
862  vorb->data[idx] = sqrt(xorb->data[idx]);
863  }
864 
865  // Initialization of gsl_spline objects
866  gsl_interp_accel *accel_v;
867  gsl_spline *spline_v;
868  accel_v = gsl_interp_accel_alloc();
869  spline_v = gsl_spline_alloc(gsl_interp_cspline, vorb->length);
870 
871  // Interpolation of v(t)
872  gsl_spline_init(spline_v, timesPN->data, vorb->data, vorb->length);
873 
874  /* Fill needed structs.
875  PhenomTPHMEvolution is a struct for the SpinDerivatives function that contains the SpinTaylor struct
876  (spin derivatives are universal for all SpinTaylor approximants), the interpolated v(t) and other needed parameters. */
877 
879 
880  XLALSimInspiralSpinTaylorTxCoeffs *Tparams=NULL; // PN coefficients struct
881 
882  /* We select fixed values for the different PN orders: No tidal interactions (it is a BBH model), highest PN order for
883  spins but perpendicular corrections to Lhat disabled, since it was not clear the effect on this on the derived Euler angles from Lhat. */
884 
885  REAL8 lambda1 = 0.0; REAL8 lambda2 = 0.0; REAL8 quadparam1 = 0.0; REAL8 quadparam2 = 0.0;
886  LALSimInspiralSpinOrder spinO = -1;
887  LALSimInspiralTidalOrder tideO = 0;
888  INT4 phaseO = -1;
889  INT4 lscorr = 0;
890 
891  XLALSimInspiralSpinTaylorT4Setup(&Tparams, m1_SI, m2_SI, pWF->fRef, 0.0, lambda1, lambda2, quadparam1, quadparam2, spinO, tideO, phaseO, lscorr, 1);
892 
893  params.v_spline = spline_v;
894  params.v_acc = accel_v;
895  params.Tparams = Tparams;
896  params.ToffSign = 0.0;
897 
898  /* Initial quantities for evolution */
899 
900  // Needed for getting dimensionful spins
901  m1sec = m1_SI / LAL_MSUN_SI * LAL_MTSUN_SI;
902  m2sec = m2_SI / LAL_MSUN_SI * LAL_MTSUN_SI;
903  Msec = m1sec + m2sec;
904 
905  /* Put initial values into a single array for the integrator */
906 
907  /* LNh(x,y,z) */
908  yinit[0] = 0.0;
909  yinit[1] = 0.0;
910  yinit[2] = 1.0;
911  /* S1(x,y,z) */
912  norm1 = m1sec * m1sec / Msec / Msec;
913  yinit[3] = norm1 * s1x;
914  yinit[4] = norm1 * s1y;
915  yinit[5] = norm1 * s1z;
916  /* S2(x,y,z) */
917  norm2 = m2sec * m2sec / Msec / Msec;
918  yinit[6] = norm2 * s2x;
919  yinit[7] = norm2 * s2y;
920  yinit[8]= norm2 * s2z;
921  /* E1(x,y,z) */
922  yinit[9] = 1.0;
923  yinit[10] = 0.0;
924  yinit[11] = 0.0;
925 
926  /* initialize the integrator */
927  integrator = XLALAdaptiveRungeKutta4Init(12,
929  StoppingTest,
931 
932  if( !integrator )
933  {
934  XLALPrintError("XLAL Error - %s: Cannot allocate integrator\n",
935  __func__);
937  }
938 
939  /* stop the integration only when ending time is achieved. */
940  integrator->stopontestonly = 0;
941 
942  /********* Integration from tref to t=0 ******/
943  /* Formally integration will perform forwards with a positive time array,
944  then ending time is select as -tmin and initial time is selected as -tmin + tref */
945 
946  REAL8 tend = -pPhase->tmin;
947  REAL8 tint1 = fabs(tend + pPhase->tRef);
948  size_t length1 = floor(tint1/pWF->dtM);
949 
950  /* run the integration; note: time is measured in \hat{t} = t / M.
951  Integration is indeed perform until tend - dtM to avoid an extra final point. */
952  len = XLALAdaptiveRungeKutta4Hermite(integrator, &params, yinit,
953  tint1, tend - pWF->dtM, pWF->dtM, &yout);
954 
955  // Check if integration failed.
956  if (!yout)
957  {
958  XLALPrintError("XLAL Error - %s: integration failed (yout == NULL)\n",
959  __func__);
961  }
962 
963  intreturn = integrator->returncode;
964  if (!len)
965  {
966  XLALPrintError("XLAL Error - %s: integration failed with errorcode %d.\n", __func__, intreturn);
968  }
969 
970  /* epoch of evolved time series same as waveform epoch */
971  LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO;
972  XLALGPSAdd(&ligotimegps_zero, pPhase->tminSec);
973 
974  //size_t lengthAll = floor((tend - tint1)/pWF->dtM) + length1;
975  size_t lengthAll = len + length1;
976 
977  /* allocate memory for output vectors */
978  *V = XLALCreateREAL8TimeSeries( "PN_EXPANSION_PARAMETER", &ligotimegps_zero, 0.,
979  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
980  *S1x = XLALCreateREAL8TimeSeries( "SPIN1_X_COMPONENT", &ligotimegps_zero, 0.,
981  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
982  *S1y = XLALCreateREAL8TimeSeries( "SPIN1_Y_COMPONENT", &ligotimegps_zero, 0.,
983  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
984  *S1z = XLALCreateREAL8TimeSeries( "SPIN1_Z_COMPONENT", &ligotimegps_zero, 0.,
985  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
986  *S2x = XLALCreateREAL8TimeSeries( "SPIN2_X_COMPONENT", &ligotimegps_zero, 0.,
987  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
988  *S2y = XLALCreateREAL8TimeSeries( "SPIN2_Y_COMPONENT", &ligotimegps_zero, 0.,
989  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
990  *S2z = XLALCreateREAL8TimeSeries( "SPIN2_Z_COMPONENT", &ligotimegps_zero, 0.,
991  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
992  *LNhatx = XLALCreateREAL8TimeSeries( "LNHAT_X_COMPONENT", &ligotimegps_zero, 0.,
993  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
994  *LNhaty = XLALCreateREAL8TimeSeries( "LNHAT_Y_COMPONENT", &ligotimegps_zero, 0.,
995  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
996  *LNhatz = XLALCreateREAL8TimeSeries( "LNHAT_Z_COMPONENT", &ligotimegps_zero, 0.,
997  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
998  *E1x = XLALCreateREAL8TimeSeries( "E1_BASIS_X_COMPONENT", &ligotimegps_zero, 0.,
999  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
1000  *E1y = XLALCreateREAL8TimeSeries( "E1_BASIS_Y_COMPONENT", &ligotimegps_zero, 0.,
1001  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
1002  *E1z = XLALCreateREAL8TimeSeries( "E1_BASIS_Z_COMPONENT", &ligotimegps_zero, 0.,
1003  pWF->deltaT, &lalDimensionlessUnit, lengthAll);
1004  if ( !*V || !*S1x || !*S1y || !*S1z || !*S2x || !*S2y || !*S2z
1005  || !*LNhatx || !*LNhaty || !*LNhatz || !*E1x || !*E1y || !*E1z )
1006  {
1007  XLALDestroyREAL8Array(yout);
1008  LALFree(Tparams);
1009  XLALAdaptiveRungeKuttaFree(integrator);
1011  }
1012 
1013  /* Copy dynamical variables from yout array to output time series.
1014  * Note the first 'len' members of yout are the time steps.
1015  */
1016  for( i = 0; i < len; i++ )
1017  {
1018  int j = i + length1;
1019  (*V)->data->data[j] = vorb->data[j];
1020  (*LNhatx)->data->data[j] = yout->data[len+i];
1021  (*LNhaty)->data->data[j] = yout->data[2*len+i];
1022  (*LNhatz)->data->data[j] = yout->data[3*len+i];
1023  (*S1x)->data->data[j] = yout->data[4*len+i]/norm1;
1024  (*S1y)->data->data[j] = yout->data[5*len+i]/norm1;
1025  (*S1z)->data->data[j] = yout->data[6*len+i]/norm1;
1026  (*S2x)->data->data[j] = yout->data[7*len+i]/norm2;
1027  (*S2y)->data->data[j] = yout->data[8*len+i]/norm2;
1028  (*S2z)->data->data[j] = yout->data[9*len+i]/norm2;
1029  (*E1x)->data->data[j] = yout->data[10*len+i];
1030  (*E1y)->data->data[j] = yout->data[11*len+i];
1031  (*E1z)->data->data[j] = yout->data[12*len+i];
1032  }
1033 
1034  // Free PN Taylor struct and integrator. If tmin!=tref and a second integration step is needed, will be defined again, to avoid possible overwritting.
1035  LALFree(Tparams);
1036  XLALAdaptiveRungeKuttaFree(integrator);
1037  XLALDestroyREAL8Array(yout);
1038 
1039  /********* Integration from tref to tmin ******/
1040  /* If tref!=tmin, quantities have to be evolved backwards from tref to tmin.
1041  Formally this is done by a forward integration, but physically it corresponds to a backward integration. */
1042 
1043  if(fabs(pPhase->tRef-pPhase->tmin)>=pWF->dtM)
1044  {
1045 
1046  /* Initialize objects and variables needed for the RK integrator */
1047 
1048  LALAdaptiveRungeKuttaIntegrator *integrator2 = NULL;
1049  REAL8Array *yout2; // time series of variables returned from integrator
1050  REAL8 yinit2[12];
1051 
1052  /* Put initial values into a single array for the integrator */
1053 
1054  // LNh(x,y,z)
1055  yinit2[0] = 0.0;
1056  yinit2[1] = 0.0;
1057  yinit2[2] = 1.0;
1058  // S1(x,y,z)
1059  norm1 = m1sec * m1sec / Msec / Msec;
1060  yinit2[3] = norm1 * s1x;
1061  yinit2[4] = norm1 * s1y;
1062  yinit2[5] = norm1 * s1z;
1063  // S2(x,y,z)
1064  norm2 = m2sec * m2sec / Msec / Msec;
1065  yinit2[6] = norm2 * s2x;
1066  yinit2[7] = norm2 * s2y;
1067  yinit2[8]= norm2 * s2z;
1068  // E1(x,y,z)
1069  yinit2[9] = 1.0;
1070  yinit2[10] = 0.0;
1071  yinit2[11] = 0.0;
1072 
1073 
1074  /* Set up PN coefficients struct */
1075  XLALSimInspiralSpinTaylorT4Setup(&Tparams, m1_SI, m2_SI, pWF->fmin, 0.0, lambda1, lambda2, quadparam1, quadparam2, spinO, tideO, phaseO, lscorr, 1);
1076  params.Tparams = Tparams;
1077  // Passed to actually performed backward integration. For t' in the integration, v(t) will be evaluated at t = tint1 - t'
1078  params.ToffSign = -tint1;
1079 
1080  /* Set up integrator */
1081  integrator2 = XLALAdaptiveRungeKutta4Init(12,
1083  StoppingTest,
1085 
1086  /* Stop integration only when ending time is achieved */
1087  integrator2->stopontestonly = 0;
1088 
1089  /* Perform integration. Formally is done from dtM to tint1 = -tmin + tref, but physically is done from tref to tmin. */
1090  len2 = XLALAdaptiveRungeKutta4Hermite(integrator2, &params, yinit2,
1091  pWF->dtM, tint1, pWF->dtM, &yout2);
1092 
1093  /* Check if integration failed */
1094  if (!yout2)
1095  {
1096  XLALPrintError("XLAL Error - %s: integration failed (yout == NULL)\n",
1097  __func__);
1099  }
1100  intreturn = integrator->returncode;
1101  if (!len2)
1102  {
1103  XLALPrintError("XLAL Error - %s: integration failed with errorcode %d.\n", __func__, intreturn);
1105  }
1106 
1107  /* Copy dynamical variables from yout array to output time series.
1108  * Note the first 'len' members of yout are the time steps. */
1109  for( i = 0; i < len2; i++ )
1110  {
1111  int j = len2 - 1 - i; // Time series are filled backwards from the index corresponding to tref to fill all the elements.
1112  (*V)->data->data[j] = vorb->data[j];
1113  (*LNhatx)->data->data[j] = yout2->data[len2+i];
1114  (*LNhaty)->data->data[j] = yout2->data[2*len2+i];
1115  (*LNhatz)->data->data[j] = yout2->data[3*len2+i];
1116  (*S1x)->data->data[j] = yout2->data[4*len2+i]/norm1;
1117  (*S1y)->data->data[j] = yout2->data[5*len2+i]/norm1;
1118  (*S1z)->data->data[j] = yout2->data[6*len2+i]/norm1;
1119  (*S2x)->data->data[j] = yout2->data[7*len2+i]/norm2;
1120  (*S2y)->data->data[j] = yout2->data[8*len2+i]/norm2;
1121  (*S2z)->data->data[j] = yout2->data[9*len2+i]/norm2;
1122  (*E1x)->data->data[j] = yout2->data[10*len2+i];
1123  (*E1y)->data->data[j] = yout2->data[11*len2+i];
1124  (*E1z)->data->data[j] = yout2->data[12*len2+i];
1125  }
1126 
1127  // Free integrator, PN coef struct and ouput array.
1128  XLALAdaptiveRungeKuttaFree(integrator2);
1129  XLALDestroyREAL8Array(yout2);
1130  LALFree(Tparams);
1131  }
1132 
1133  // Free gsl spline objects
1134  gsl_spline_free(spline_v);
1135  gsl_interp_accel_free(accel_v);
1136 
1137  XLALDestroyREAL8Sequence( timesPN);
1138  XLALDestroyREAL8Sequence( vorb);
1139 
1140  return XLAL_SUCCESS;
1141 }
1142 
1143 
1144 /* Function to compute Euler angles from the evolution of LNhat:
1145  By definition:
1146  - alpha = arctan(LNhat_y / LNhat_x)
1147  - cosbeta = LNhat*Jhat = LNhat_z (in J-frame)
1148  - gamma = - \int \dot{alpha}*cosbeta (minimal rotation condition)
1149  */
1150 
1152  REAL8TimeSeries **alphaInt, /* Alpha angle time series [out] */
1153  REAL8TimeSeries **cosbetaInt, /* cos(Beta) angle time series [out] */
1154  REAL8TimeSeries **gammaInt, /* Gamma angle time series [out] */
1155  REAL8 *af_evolved, /* Final spin predicted by the evolved spin values at the peak time */
1156  REAL8Sequence *xorb, /* x(t)=v(t)^2 time series [in] */
1157  REAL8 deltaT, /* Sampling interval (s) */
1158  REAL8 m1_SI, /* Mass of companion 1 (kg) */
1159  REAL8 m2_SI, /* Mass of companion 2 (kg) */
1160  REAL8 s1x, /* x component of primary spin */
1161  REAL8 s1y, /* y component of primary spin */
1162  REAL8 s1z, /* z component of primary spin */
1163  REAL8 s2x, /* x component of secondary spin */
1164  REAL8 s2y, /* y component of secondary spin */
1165  REAL8 s2z, /* z component of secondary spin */
1166  REAL8 epochT, /* initial time in mass units */
1167  IMRPhenomTWaveformStruct *pWF, /* PhenomTHM waveform struct */
1168  IMRPhenomTPhase22Struct *pPhase, /* PhenomTHM phase struct */
1169  UNUSED IMRPhenomXPrecessionStruct *pPrec /* PhenomX precessing struct */
1170  );
1171 
1173  REAL8TimeSeries **alphaTS, /* Alpha angle time series [out] */
1174  REAL8TimeSeries **cosbetaTS, /* cos(Beta) angle time series [out] */
1175  REAL8TimeSeries **gammaTS, /* Gamma angle time series [out] */
1176  REAL8 *af_evolved, /* Final spin predicted by the evolved spin values at the peak time */
1177  REAL8Sequence *xorb, /* x(t)=v(t)^2 time series [in] */
1178  REAL8 deltaT, /* Sampling interval (s) */
1179  REAL8 m1_SI, /* Mass of companion 1 (kg) */
1180  REAL8 m2_SI, /* Mass of companion 2 (kg) */
1181  REAL8 s1x, /* x component of primary spin */
1182  REAL8 s1y, /* y component of primary spin */
1183  REAL8 s1z, /* z component of primary spin */
1184  REAL8 s2x, /* x component of secondary spin */
1185  REAL8 s2y, /* y component of secondary spin */
1186  REAL8 s2z, /* z component of secondary spin */
1187  REAL8 epochT, /* initial time in mass units */
1188  IMRPhenomTWaveformStruct *pWF, /* PhenomTHM waveform struct */
1189  IMRPhenomTPhase22Struct *pPhase, /* PhenomTHM phase struct */
1190  UNUSED IMRPhenomXPrecessionStruct *pPrec /* PhenomX precessing struct */
1191  )
1192 {
1193  int status = XLAL_SUCCESS;
1194 
1195  /* Initialize needed time series for calling IMRPhenomTPHM_EvolveOrbit */
1196  REAL8TimeSeries *V = NULL;
1197  REAL8TimeSeries *S1x = NULL;
1198  REAL8TimeSeries *S1y = NULL;
1199  REAL8TimeSeries *S1z = NULL;
1200  REAL8TimeSeries *S2x = NULL;
1201  REAL8TimeSeries *S2y = NULL;
1202  REAL8TimeSeries *S2z = NULL;
1203  REAL8TimeSeries *LNhatx = NULL;
1204  REAL8TimeSeries *LNhaty = NULL;
1205  REAL8TimeSeries *LNhatz = NULL;
1206  REAL8TimeSeries *E1x = NULL;
1207  REAL8TimeSeries *E1y = NULL;
1208  REAL8TimeSeries *E1z = NULL;
1209 
1210  /* Evolve LNhat and spins */
1211  status = IMRPhenomTPHM_EvolveOrbit(&V, &S1x, &S1y, &S1z, &S2x, &S2y, &S2z, &LNhatx, &LNhaty, &LNhatz, &E1x, &E1y, &E1z,\
1212  xorb, m1_SI, m2_SI, s1x, s1y, s1z, s2x, s2y, s2z, pWF, pPhase);
1213 
1214  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: Internal function IMRPhenomTPHM_EvolveOrbit has failed.");
1215 
1216  size_t lenPN = LNhatx->data->length;
1217 
1218  // Compute final spin as predicted by spins at tpeak
1219 
1220  // Needed for getting dimensionful spins
1221  REAL8 m1sec = m1_SI / LAL_MSUN_SI * LAL_MTSUN_SI;
1222  REAL8 m2sec = m2_SI / LAL_MSUN_SI * LAL_MTSUN_SI;
1223  REAL8 Msec = m1sec + m2sec;
1224  REAL8 norm1 = m1sec * m1sec / Msec / Msec;
1225  REAL8 norm2 = m2sec * m2sec / Msec / Msec;
1226 
1227  REAL8 lnxpeak, lnypeak, lnzpeak;
1228  REAL8 s1xpeak,s1ypeak,s1zpeak,s2xpeak,s2ypeak,s2zpeak;
1229 
1230  // Values of spins and Lhat at the peak time
1231 
1232  s1xpeak = norm1*(S1x)->data->data[lenPN-1];
1233  s1ypeak = norm1*(S1y)->data->data[lenPN-1];
1234  s1zpeak = norm1*(S1z)->data->data[lenPN-1];
1235 
1236  s2xpeak = norm2*(S2x)->data->data[lenPN-1];
1237  s2ypeak = norm2*(S2y)->data->data[lenPN-1];
1238  s2zpeak = norm2*(S2z)->data->data[lenPN-1];
1239 
1240  lnxpeak = (LNhatx)->data->data[lenPN-1];
1241  lnypeak = (LNhaty)->data->data[lenPN-1];
1242  lnzpeak = (LNhatz)->data->data[lenPN-1];
1243 
1244  // Projection of the spins in the Lhat direction and perpendicular direction, following the right hand rule.
1245 
1246  REAL8 s1Lpeak = s1xpeak*lnxpeak + s1ypeak*lnypeak + s1zpeak*lnzpeak;
1247  REAL8 s2Lpeak = s2xpeak*lnxpeak + s2ypeak*lnypeak + s2zpeak*lnzpeak;
1248 
1249  REAL8 s1xparallel = s1Lpeak*lnxpeak;
1250  REAL8 s1yparallel = s1Lpeak*lnypeak;
1251  REAL8 s1zparallel = s1Lpeak*lnzpeak;
1252 
1253  REAL8 s1xperp = s1xpeak - s1xparallel;
1254  REAL8 s1yperp = s1ypeak - s1yparallel;
1255  REAL8 s1zperp = s1zpeak - s1zparallel;
1256 
1257  REAL8 s2xparallel = s2Lpeak*lnxpeak;
1258  REAL8 s2yparallel = s2Lpeak*lnypeak;
1259  REAL8 s2zparallel = s2Lpeak*lnzpeak;
1260 
1261  REAL8 s2xperp = s2xpeak - s2xparallel;
1262  REAL8 s2yperp = s2ypeak - s2yparallel;
1263  REAL8 s2zperp = s2zpeak - s2zparallel;
1264 
1265  // Final spìn from common geometrical formula employing the evolved spins at merger.
1266 
1267  REAL8 Sperp = sqrt( pow(s1xperp + s2xperp, 2) + pow(s1yperp + s2yperp, 2) + pow(s1zperp + s2zperp, 2) );
1268 
1269  REAL8 af_nonprec = XLALSimIMRPhenomXFinalSpin2017(pWF->eta, s1Lpeak/norm1, s2Lpeak/norm2);
1270 
1271  REAL8 Sf = copysign(1.0,af_nonprec)*sqrt(Sperp*Sperp + pow(af_nonprec,2));
1272 
1273  if(Sf>1.0){Sf = 1.0;};
1274  if(Sf<-1.0){Sf = -1.0;};
1275 
1276  (*af_evolved) = Sf;
1277 
1278  /* Initialize needed sequences for storing times and angles */
1279 
1280  REAL8Sequence *alpha = NULL;
1281  REAL8Sequence *alphaaux = NULL;
1282  REAL8Sequence *cosbeta = NULL;
1283  REAL8Sequence *gamma = NULL;
1284  REAL8Sequence *times = NULL;
1285 
1286  // Offset for alpha angle, corresponding to the angle of the perpendicular to J projection of L in the plane perpendicular to J at tref.
1287  REAL8 alphaOff = atan2(pPrec->S1y + pPrec->S2y, pPrec->S1x + pPrec->S2x) - LAL_PI;
1288 
1289  // Waveform lenght and index of tref
1290  size_t lengthAll = pPhase->wflength;
1291  REAL8 tend = -pPhase->tmin;
1292  REAL8 tint1 = fabs(tend + pPhase->tRef);
1293  size_t length1 = floor(tint1/pWF->dtM); // This corresponds to the index of tref/fref.
1294 
1295  /* Setup sequences for angles and time */
1296  alpha = XLALCreateREAL8Sequence(lengthAll);
1297  alphaaux = XLALCreateREAL8Sequence(lengthAll);
1298  cosbeta = XLALCreateREAL8Sequence(lengthAll);
1299  gamma = XLALCreateREAL8Sequence(lengthAll);
1300  times = XLALCreateREAL8Sequence(lengthAll);
1301 
1302 
1303  /**** ROTATION OF LNHAT FROM L0 frame to J frame ****/
1304  /* Rotation described in Appendix C eq. C13-14 of IMRPhenomXPHM paper: https://arxiv.org/pdf/2004.06503.pdf
1305  Applied to the whole time evolution of LNhat */
1306 
1307  REAL8 cosPhiJ = cos(-pPrec->phiJ_Sf);
1308  REAL8 sinPhiJ = sin(-pPrec->phiJ_Sf);
1309 
1310  REAL8 cosThetaJ = cos(-pPrec->thetaJ_Sf);
1311  REAL8 sinThetaJ = sin(-pPrec->thetaJ_Sf);
1312 
1313  REAL8 cosKappa = cos(-pPrec->kappa);
1314  REAL8 sinKappa = sin(-pPrec->kappa);
1315 
1316  for(UINT8 i=0; i < lenPN; i++)
1317  {
1318 
1319  IMRPhenomT_rotate_z(cosPhiJ, sinPhiJ, &(LNhatx->data->data[i]), &(LNhaty->data->data[i]), &(LNhatz->data->data[i]));
1320  IMRPhenomT_rotate_y(cosThetaJ, sinThetaJ, &(LNhatx->data->data[i]), &(LNhaty->data->data[i]), &(LNhatz->data->data[i]));
1321  IMRPhenomT_rotate_z(cosKappa, sinKappa, &(LNhatx->data->data[i]), &(LNhaty->data->data[i]), &(LNhatz->data->data[i]));
1322 
1323  /* Compute Euler angles in the J frame */
1324  alphaaux->data[i] = atan2(LNhaty->data->data[i], LNhatx->data->data[i]);
1325  cosbeta->data[i] = LNhatz->data->data[i];
1326  times->data[i] = i*deltaT + epochT;
1327  }
1328 
1329  // Substract alpha value at tref. Added to alphaOff for comodity.
1330  alphaOff -= atan2(LNhaty->data->data[length1], LNhatx->data->data[length1]);
1331  for(UINT8 i=0; i < lenPN; i++)
1332  {
1333  alphaaux->data[i] += alphaOff;
1334  }
1335 
1336  // Unwrap alpha array to have a continuous time series.
1337  unwrap_array(alphaaux->data, alpha->data, lenPN);
1338 
1339  REAL8 EulerRDslope = GetEulerSlope(Sf, pWF->Mfinal);
1340 
1341  // Compute RD angles approximation. Same as in PNAnalyticalInspiralEulerAngles.
1342  for(UINT8 jdx = lenPN-1; jdx < lengthAll; jdx++)
1343  {
1344  times->data[jdx] = jdx*deltaT + epochT;
1345  alpha->data[jdx] = alpha->data[lenPN-2] + EulerRDslope*times->data[jdx];
1346  cosbeta->data[jdx] = cosbeta->data[lenPN-2];
1347  }
1348 
1349  /**** COMPUTE GAMMA FROM MINIMAL ROTATION CONDITION ****/
1350  /* alpha and cosbeta are interpolated to compute the RHS of the minimal rotation condition.
1351  Then gamma is integrated using Boole's rule. */
1352 
1353  // Interpolate alpha
1354  gsl_interp_accel *accel_alpha;
1355  gsl_spline *spline_alpha;
1356 
1357  accel_alpha = gsl_interp_accel_alloc();
1358  spline_alpha = gsl_spline_alloc(gsl_interp_cspline, lengthAll);
1359 
1360  gsl_spline_init(spline_alpha, times->data, alpha->data, lengthAll);
1361 
1362  // Interpolate cosbeta
1363  gsl_interp_accel *accel_cosb;
1364  gsl_spline *spline_cosb;
1365 
1366  accel_cosb = gsl_interp_accel_alloc();
1367  spline_cosb = gsl_spline_alloc(gsl_interp_cspline, lengthAll);
1368 
1369  gsl_spline_init(spline_cosb, times->data, cosbeta->data, lengthAll);
1370 
1371  // Setup helper struct for gamma integration
1372  gammaIntegration gammaStruct;
1373 
1374  gammaStruct.alpha_spline = spline_alpha;
1375  gammaStruct.alpha_acc = accel_alpha;
1376  gammaStruct.cosbeta_spline = spline_cosb;
1377  gammaStruct.cosbeta_acc = accel_cosb;
1378 
1379  // Offset for gamma (this step is also employed to fill the output time series for alpha and cosbeta from the sequences)
1380  gamma->data[0] = -alpha->data[0];
1381  (*gammaTS)->data->data[0] = gamma->data[0];
1382  (*alphaTS)->data->data[0] = alpha->data[0];
1383  (*cosbetaTS)->data->data[0] = cosbeta->data[0];
1384 
1385  // Integrate gamma
1386  for( UINT4 i = 1; i < lengthAll; i++ ){
1387  double t1 = times->data[i-1];
1388  double t2 = times->data[i];
1389  gamma->data[i] = gamma->data[i-1] + (1.0/90.0)*(t2 - t1)*(7.0*f_alphadotcosi(t1,&gammaStruct)
1390  + 32.0*f_alphadotcosi((t1+3.0*t2)/4.0,&gammaStruct)
1391  + 12.0*f_alphadotcosi((t1+t2)/2.0,&gammaStruct)
1392  + 32.0*f_alphadotcosi((3.0*t1+t2)/4.0,&gammaStruct)
1393  + 7.0*f_alphadotcosi(t2,&gammaStruct));/* Boole's Rule */
1394 
1395  // Fill output time series
1396  (*gammaTS)->data->data[i] = gamma->data[i];
1397  (*alphaTS)->data->data[i] = alpha->data[i];
1398  (*cosbetaTS)->data->data[i] = cosbeta->data[i];
1399  }
1400 
1401  // Substract gamma value at tref and apply corresponding offset.
1402  for( UINT4 i = 1; i < lengthAll; i++ ){
1403  (*gammaTS)->data->data[i] += - gamma->data[length1] - alpha->data[length1];
1404  }
1405 
1406 
1407  // Destroy time series and sequences
1415  XLALDestroyREAL8TimeSeries( LNhatx );
1416  XLALDestroyREAL8TimeSeries( LNhaty );
1417  XLALDestroyREAL8TimeSeries( LNhatz );
1421 
1423  XLALDestroyREAL8Sequence( alphaaux);
1424  XLALDestroyREAL8Sequence( cosbeta);
1426  XLALDestroyREAL8Sequence( times);
1427 
1428  // Free gsl spline objects
1429  gsl_spline_free(spline_alpha);
1430  gsl_spline_free(spline_cosb);
1431  gsl_interp_accel_free(accel_alpha);
1432  gsl_interp_accel_free(accel_cosb);
1433 
1434 return status;
1435 }
#define LALFree(p)
static double double delta
double IMRPhenomTomega22(REAL8 t, REAL8 theta, IMRPhenomTWaveformStruct *pWF, IMRPhenomTPhase22Struct *pPhase)
REAL8 GetEulerSlope(REAL8 af, REAL8 mf)
int IMRPhenomTSetPhase22Coefficients(IMRPhenomTPhase22Struct *pPhase, IMRPhenomTWaveformStruct *wf)
int IMRPhenomTSetWaveformVariables(IMRPhenomTWaveformStruct *wf, const REAL8 m1_SI, const REAL8 m2_SI, const REAL8 chi1L_In, const REAL8 chi2L_In, const REAL8 distance, const REAL8 deltaT, const REAL8 fmin, const REAL8 fRef, const REAL8 phiRef, LALDict *lalParams)
#define LAL_ST4_RELATIVE_TOLERANCE
void IMRPhenomT_rotate_z(REAL8 cosangle, REAL8 sinangle, REAL8 *vx, REAL8 *vy, REAL8 *vz)
int PNAnalyticalInspiralEulerAngles(REAL8TimeSeries **alphaTS, REAL8TimeSeries **cosbetaTS, REAL8TimeSeries **gammaTS, REAL8Sequence *xorb, IMRPhenomTWaveformStruct *pWF, IMRPhenomTPhase22Struct *pPhase, IMRPhenomXWaveformStruct *pWFX, IMRPhenomXPrecessionStruct *pPrec, INT4 EulerRDVersion, INT4 GammaVersion)
This function provides a wrapper to the analytical PN angle descriptions computed by the IMRPhenomXP(...
int IMRPhenomTPHM_EvolveOrbit(REAL8TimeSeries **V, REAL8TimeSeries **S1x, REAL8TimeSeries **S1y, REAL8TimeSeries **S1z, REAL8TimeSeries **S2x, REAL8TimeSeries **S2y, REAL8TimeSeries **S2z, REAL8TimeSeries **LNhatx, REAL8TimeSeries **LNhaty, REAL8TimeSeries **LNhatz, REAL8TimeSeries **E1x, REAL8TimeSeries **E1y, REAL8TimeSeries **E1z, REAL8Sequence *xorb, REAL8 m1_SI, REAL8 m2_SI, REAL8 s1x, REAL8 s1y, REAL8 s1z, REAL8 s2x, REAL8 s2y, REAL8 s2z, IMRPhenomTWaveformStruct *pWF, IMRPhenomTPhase22Struct *pPhase)
static double f_alphadotcosi(double x, void *inparams)
void IMRPhenomT_rotate_y(REAL8 cosangle, REAL8 sinangle, REAL8 *vx, REAL8 *vy, REAL8 *vz)
int IMRPhenomTPHM_NumericalEulerAngles(REAL8TimeSeries **alphaInt, REAL8TimeSeries **cosbetaInt, REAL8TimeSeries **gammaInt, REAL8 *af_evolved, REAL8Sequence *xorb, REAL8 deltaT, REAL8 m1_SI, REAL8 m2_SI, REAL8 s1x, REAL8 s1y, REAL8 s1z, REAL8 s2x, REAL8 s2y, REAL8 s2z, REAL8 epochT, IMRPhenomTWaveformStruct *pWF, IMRPhenomTPhase22Struct *pPhase, UNUSED IMRPhenomXPrecessionStruct *pPrec)
void unwrap_array(double *in, double *out, int len)
INT4 XLALSimIMRPhenomTPHMSpinDerivatives(REAL8 UNUSED t, const REAL8 values[], REAL8 dvalues[], void *mparams)
#define LAL_ST4_ABSOLUTE_TOLERANCE
static double IMRPhenomT_MECOTime(double eta, double S, double dchi, double delta)
int StoppingTest(double UNUSED t, const double UNUSED values[], double UNUSED dvalues[], void UNUSED *mparams)
double IMRPhenomX_PN_Euler_epsilon_NNLO(IMRPhenomXPrecessionStruct *pPrec, const double omega, const double omega_cbrt2, const double omega_cbrt, const double logomega)
Internal function to calculate epsilon using pre-cached NNLO PN expressions.
REAL8 XLALSimIMRPhenomXLPNAnsatz(REAL8 v, REAL8 LNorm, REAL8 L0, REAL8 L1, REAL8 L2, REAL8 L3, REAL8 L4, REAL8 L5, REAL8 L6, REAL8 L7, REAL8 L8, REAL8 L8L)
This is a convenient wrapper function for PN orbital angular momentum.
double IMRPhenomX_PN_Euler_alpha_NNLO(IMRPhenomXPrecessionStruct *pPrec, const double omega, const double omega_cbrt2, const double omega_cbrt, const double logomega)
Internal function to calculate alpha using pre-cached NNLO PN expressions.
vector IMRPhenomX_Return_phi_zeta_costhetaL_MSA(const double v, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
Wrapper to generate , and at a given frequency.
REAL8 XLALSimIMRPhenomXFinalSpin2017(REAL8 eta, REAL8 chi1L, REAL8 chi2L)
Final Dimensionless Spin, X Jimenez-Forteza et al, PRD, 95, 064024, (2017), arXiv:1611....
INT4 XLALSimInspiralSpinTaylorT4Setup(XLALSimInspiralSpinTaylorTxCoeffs **params, REAL8 m1, REAL8 m2, REAL8 fStart, REAL8 fEnd, REAL8 lambda1, REAL8 lambda2, REAL8 quadparam1, REAL8 quadparam2, LALSimInspiralSpinOrder spinO, LALSimInspiralTidalOrder tideO, INT4 phaseO, INT4 lscorr, INT4 phenomtp)
See arXiv:0907.0700 for TaylorT4 definition.
INT4 XLALSimInspiralSpinDerivativesAvg(REAL8 *dLNhx, REAL8 *dLNhy, REAL8 *dLNhz, REAL8 *dE1x, REAL8 *dE1y, REAL8 *dE1z, REAL8 *dS1x, REAL8 *dS1y, REAL8 *dS1z, REAL8 *dS2x, REAL8 *dS2y, REAL8 *dS2z, const REAL8 v, const REAL8 LNhx, const REAL8 LNhy, const REAL8 LNhz, const REAL8 E1x, const REAL8 E1y, const REAL8 E1z, const REAL8 S1x, const REAL8 S1y, const REAL8 S1z, const REAL8 S2x, const REAL8 S2y, const REAL8 S2z, const REAL8 LNhdotS1, const REAL8 LNhdotS2, XLALSimInspiralSpinTaylorTxCoeffs *params)
Function computing spin precession equations, common to all member of the SpinTaylor family.
static vector d(const double L_norm, const double J_norm, const vector roots)
Internal function that returns the coefficients "d_0", "d_2" and "d_4" from 1703.03967 corresponding ...
REAL8 tmp1
int s
Definition: bh_qnmode.c:137
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
double theta
Definition: bh_sphwf.c:118
const double vy
sigmaKerr data[0]
const double vz
const double vx
void XLALDestroyREAL8Array(REAL8Array *)
void XLALAdaptiveRungeKuttaFree(LALAdaptiveRungeKuttaIntegrator *integrator)
int XLALAdaptiveRungeKutta4Hermite(LALAdaptiveRungeKuttaIntegrator *integrator, void *params, REAL8 *yinit, REAL8 tinit, REAL8 tend_in, REAL8 deltat, REAL8Array **yout)
LALAdaptiveRungeKuttaIntegrator * XLALAdaptiveRungeKutta4Init(int dim, int(*dydt)(double t, const double y[], double dydt[], void *params), int(*stop)(double t, const double y[], double dydt[], void *params), double eps_abs, double eps_rel)
#define LAL_MSUN_SI
#define LAL_PI
#define LAL_MTSUN_SI
uint64_t UINT8
#define LIGOTIMEGPSZERO
double REAL8
uint32_t UINT4
int32_t INT4
void * XLALMalloc(size_t n)
int XLALSimIMRPhenomTPHM_EvolveOrbit(REAL8TimeSeries **V, REAL8TimeSeries **S1x, REAL8TimeSeries **S1y, REAL8TimeSeries **S1z, REAL8TimeSeries **S2x, REAL8TimeSeries **S2y, REAL8TimeSeries **S2z, REAL8TimeSeries **LNhatx, REAL8TimeSeries **LNhaty, REAL8TimeSeries **LNhatz, REAL8TimeSeries **E1x, REAL8TimeSeries **E1y, REAL8TimeSeries **E1z, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, REAL8 deltaT, REAL8 fmin, REAL8 fRef, REAL8 phiRef, LALDict *lalParams)
Function to return the time series for the evolution of the individual spins, the Newtonian angular m...
LALSimInspiralSpinOrder
Enumeration of allowed PN orders of spin effects.
LALSimInspiralTidalOrder
Enumeration of allowed PN orders of tidal effects.
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
const LALUnit lalDimensionlessUnit
#define XLAL_PRINT_INFO(...)
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
#define XLAL_PRINT_WARNING(...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
XLAL_SUCCESS
XLAL_EFUNC
XLAL_EDOM
XLAL_EINVAL
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
list x
string status
double alpha
Definition: sgwb.c:106
REAL8 thetaJ_Sf
Angle between and (z-direction)
INT4 IMRPhenomXPrecVersion
Flag to set version of Euler angles used.
XLALSimInspiralSpinTaylorTxCoeffs * Tparams
REAL8 * data
REAL8Sequence * data
REAL8 * data
Definition: burst.c:245
double V
Definition: unicorn.c:25
double deltaT
Definition: unicorn.c:24