Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
LALSimIMRPhenomX_precession.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2018/2019 Geraint Pratten
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 #ifdef __cplusplus
21 extern "C" {
22 #endif
23
24/**
25 * \author Geraint Pratten, Marta Colleoni, Eleanor Hamilton
26 *
27 */
28
29#include <gsl/gsl_sf_elljac.h>
30#include <gsl/gsl_sf_ellint.h>
31
32#include <lal/SphericalHarmonics.h>
33#include "LALSimIMR.h"
34
35#include "LALSimIMRPhenomX.h"
41
42#include <lal/LALAdaptiveRungeKuttaIntegrator.h>
43
44#define MIN(a,b) (((a)<(b))?(a):(b))
45#define MAX(a,b) (((a)>(b))?(a):(b))
46
47
48#ifndef _OPENMP
49#define omp ignore
50#endif
51
52#ifndef PHENOMXHMDEBUG
53#define DEBUG 0
54#else
55#define DEBUG 1
56#endif
57
58/* ~~~~~~~~~~ Functions to Perform Frame Transformations and Populate Structs ~~~~~~~~~~ */
59 /**
60 Function to populate the IMRPhenomXPrecessionStruct:
61 - Calculates frame transformation
62 - PN Euler angles
63 - Frame transformations
64 - Orbital angular momenta etc
65 */
69 REAL8 m1_SI,
70 REAL8 m2_SI,
71 REAL8 chi1x,
72 REAL8 chi1y,
73 REAL8 chi1z,
74 REAL8 chi2x,
75 REAL8 chi2y,
76 REAL8 chi2z,
77 LALDict *lalParams,
78 INT4 debug_flag
79)
80{
81 /*
82 Here we assume m1 > m2, q > 1, dm = m1 - m2 = delta = sqrt(1-4eta) > 0
83 */
84 pWF->LALparams = lalParams;
85
86 /* Pre-cache useful powers here */
87 pPrec->sqrt2 = 1.4142135623730951;
88 pPrec->sqrt5 = 2.23606797749978981;
89 pPrec->sqrt6 = 2.44948974278317788;
90 pPrec->sqrt7 = 2.64575131106459072;
91 pPrec->sqrt10 = 3.16227766016838;
92 pPrec->sqrt14 = 3.74165738677394133;
93 pPrec->sqrt15 = 3.87298334620741702;
94 pPrec->sqrt70 = 8.36660026534075563;
95 pPrec->sqrt30 = 5.477225575051661;
96 pPrec->sqrt2p5 = 1.58113883008419;
97
98 pPrec->debug_prec = debug_flag;
99
100 /* Sort out version-specific flags */
101
102 // Get IMRPhenomX precession version from LAL dictionary
104 if (pPrec->IMRPhenomXPrecVersion == 300) pPrec->IMRPhenomXPrecVersion = 223;
105
106 // default to NNLO angles if in-plane spins are negligible and one of the SpinTaylor options has been selected. The solutions would be dominated by numerical noise.
107 REAL8 chi_in_plane = sqrt(chi1x*chi1x+chi1y*chi1y+chi2x*chi2x+chi2y*chi2y);
108
109 if(chi_in_plane<1e-6 && pPrec->IMRPhenomXPrecVersion==330)
110 {
111 pPrec->IMRPhenomXPrecVersion=102;
112 }
113
114 if(chi_in_plane<1e-7 && (pPrec->IMRPhenomXPrecVersion==320||pPrec->IMRPhenomXPrecVersion==321||pPrec->IMRPhenomXPrecVersion==310||pPrec->IMRPhenomXPrecVersion==311))
115 {
116 pPrec->IMRPhenomXPrecVersion=102;
117 }
118
119 // Get expansion order for MSA system of equations. Default is taken to be 5.
121
122 // Get toggle for PNR angles
124 pPrec->IMRPhenomXPNRUseTunedAngles = PNRUseTunedAngles;
125
126 // Get PNR angle interpolation tolerance
128
129 // Get toggle for symmetric waveform
131 pPrec->IMRPhenomXAntisymmetricWaveform = AntisymmetricWaveform;
132
133 // Set toggle for polarization calculation: +1 for symmetric waveform (default), -1 for antisymmetric waveform; refer to XXXX.YYYYY for details
134 pPrec->PolarizationSymmetry = 1.0;
135
136 /* allow for conditional disabling of precession multibanding given mass ratio and opening angle */
137 pPrec->conditionalPrecMBand = 0;
139 if(pPrec->MBandPrecVersion == 2){
140 pPrec->MBandPrecVersion = 0; /* current default value is 0 */
141 pPrec->conditionalPrecMBand = 1;
142 }
143
144
145
146 /* Define a number of convenient local parameters */
147 const REAL8 m1 = m1_SI / pWF->Mtot_SI; /* Normalized mass of larger companion: m1_SI / Mtot_SI */
148 const REAL8 m2 = m2_SI / pWF->Mtot_SI; /* Normalized mass of smaller companion: m2_SI / Mtot_SI */
149 const REAL8 M = (m1 + m2); /* Total mass in solar units */
150
151 // Useful powers of mass
152 const REAL8 m1_2 = m1 * m1;
153 const REAL8 m1_3 = m1 * m1_2;
154 const REAL8 m1_4 = m1 * m1_3;
155 const REAL8 m1_5 = m1 * m1_4;
156 const REAL8 m1_6 = m1 * m1_5;
157 const REAL8 m1_7 = m1 * m1_6;
158 const REAL8 m1_8 = m1 * m1_7;
159
160 const REAL8 m2_2 = m2 * m2;
161
162 pWF->M = M;
163 pWF->m1_2 = m1_2;
164 pWF->m2_2 = m2_2;
165
166 const REAL8 q = m1/m2; // q = m1 / m2 > 1.0
167
168 // Powers of eta
169 const REAL8 eta = pWF->eta;
170 const REAL8 eta2 = eta*eta;
171 const REAL8 eta3 = eta*eta2;
172 const REAL8 eta4 = eta*eta3;
173 const REAL8 eta5 = eta*eta4;
174 const REAL8 eta6 = eta*eta5;
175
176 // \delta in terms of q > 1
177 const REAL8 delta = pWF->delta;
178 const REAL8 delta2 = delta*delta;
179 const REAL8 delta3 = delta*delta2;
180
181 // Cache these powers, as we use them regularly
182 pPrec->eta = eta;
183 pPrec->eta2 = eta2;
184 pPrec->eta3 = eta3;
185 pPrec->eta4 = eta4;
186
187 pPrec->inveta = 1.0 / eta;
188 pPrec->inveta2 = 1.0 / eta2;
189 pPrec->inveta3 = 1.0 / eta3;
190 pPrec->inveta4 = 1.0 / eta4;
191 pPrec->sqrt_inveta = 1.0 / sqrt(eta);
192
193 const REAL8 chi_eff = pWF->chiEff;
194
195 pPrec->twopiGM = LAL_TWOPI * LAL_G_SI * (m1_SI + m2_SI) / LAL_C_SI / LAL_C_SI / LAL_C_SI;
196 pPrec->piGM = LAL_PI * (m1_SI + m2_SI) * (LAL_G_SI / LAL_C_SI) / (LAL_C_SI * LAL_C_SI);
197
198 /* Set spin variables in pPrec struct */
199 pPrec->chi1x = chi1x;
200 pPrec->chi1y = chi1y;
201 pPrec->chi1z = chi1z;
202 pPrec->chi1_norm = sqrt(chi1x*chi1x + chi1y*chi1y + chi1z*chi1z);
203
204 pPrec->chi2x = chi2x;
205 pPrec->chi2y = chi2y;
206 pPrec->chi2z = chi2z;
207 pPrec->chi2_norm = sqrt(chi2x*chi2x + chi2y*chi2y + chi2z*chi2z);
208
209 /* Check that spins obey Kerr bound */
210 if((!PNRUseTunedAngles)||(pWF->PNR_SINGLE_SPIN != 1)){ /*Allow the single-spin mapping for PNR to break the Kerr limit*/
211 XLAL_CHECK(fabs(pPrec->chi1_norm) <= 1.0, XLAL_EDOM, "Error in IMRPhenomXSetPrecessionVariables: |S1/m1^2| must be <= 1.\n");
212 XLAL_CHECK(fabs(pPrec->chi2_norm) <= 1.0, XLAL_EDOM, "Error in IMRPhenomXSetPrecessionVariables: |S2/m2^2| must be <= 1.\n");
213 }
214
215 /* Calculate dimensionful spins */
216 pPrec->S1x = chi1x * m1_2;
217 pPrec->S1y = chi1y * m1_2;
218 pPrec->S1z = chi1z * m1_2;
219 pPrec->S1_norm = fabs(pPrec->chi1_norm) * m1_2;
220
221 pPrec->S2x = chi2x * m2_2;
222 pPrec->S2y = chi2y * m2_2;
223 pPrec->S2z = chi2z * m2_2;
224 pPrec->S2_norm = fabs(pPrec->chi2_norm) * m2_2;
225
226 // Useful powers
227 pPrec->S1_norm_2 = pPrec->S1_norm * pPrec->S1_norm;
228 pPrec->S2_norm_2 = pPrec->S2_norm * pPrec->S2_norm;
229
230 pPrec->chi1_perp = sqrt(chi1x*chi1x + chi1y*chi1y);
231 pPrec->chi2_perp = sqrt(chi2x*chi2x + chi2y*chi2y);
232
233 /* Get spin projections */
234 pPrec->S1_perp = (m1_2) * sqrt(chi1x*chi1x + chi1y*chi1y);
235 pPrec->S2_perp = (m2_2) * sqrt(chi2x*chi2x + chi2y*chi2y);
236
237 /* Norm of in-plane vector sum: Norm[ S1perp + S2perp ] */
238 pPrec->STot_perp = sqrt( (pPrec->S1x+pPrec->S2x)*(pPrec->S1x+pPrec->S2x) + (pPrec->S1y+pPrec->S2y)*(pPrec->S1y+pPrec->S2y) );
239
240 /* This is called chiTot_perp to distinguish from Sperp used in contrusction of chi_p. For normalization, see Sec. IV D of arXiv:2004.06503 */
241 pPrec->chiTot_perp = pPrec->STot_perp * (M*M) / m1_2;
242 /* Store pPrec->chiTot_perp to pWF so that it can be used in XCP modifications (PNRUseTunedCoprec) */
243 pWF->chiTot_perp = pPrec->chiTot_perp;
244
245 /* disable tuned PNR angles, tuned coprec and mode asymmetries in low in-plane spin limit */
246 if((chi_in_plane < 1e-7)&&(pPrec->IMRPhenomXPNRUseTunedAngles == 1)&&(pWF->PNR_SINGLE_SPIN != 1)){
248 PNRUseTunedAngles = 0;
251 AntisymmetricWaveform = 0;
254 }
255
256 /*
257 Calculate the effective precessing spin parameter (Schmidt et al, PRD 91, 024043, 2015):
258 - m1 > m2, so body 1 is the larger black hole
259 */
260 pPrec->A1 = 2.0 + (3.0 * m2) / (2.0 * m1);
261 pPrec->A2 = 2.0 + (3.0 * m1) / (2.0 * m2);
262 pPrec->ASp1 = pPrec->A1 * pPrec->S1_perp;
263 pPrec->ASp2 = pPrec->A2 * pPrec->S2_perp;
264
265 /* S_p = max(A1 S1_perp, A2 S2_perp) */
266 const REAL8 num = (pPrec->ASp2 > pPrec->ASp1) ? pPrec->ASp2 : pPrec->ASp1;
267 const REAL8 den = (m2 > m1) ? pPrec->A2*(m2_2) : pPrec->A1*(m1_2);
268
269 /* chi_p = max(A1 * Sp1 , A2 * Sp2) / (A_i * m_i^2) where i is the index of the larger BH */
270 const REAL8 chip = num / den;
271 const REAL8 chi1L = chi1z;
272 const REAL8 chi2L = chi2z;
273
274
275 pPrec->chi_p = chip;
276 // (PNRUseTunedCoprec)
277 pWF->chi_p = pPrec->chi_p;
278 pPrec->phi0_aligned = pWF->phi0;
279
280 /* Effective (dimensionful) aligned spin */
281 pPrec->SL = chi1L*m1_2 + chi2L*m2_2;
282
283 /* Effective (dimensionful) in-plane spin */
284 pPrec->Sperp = chip * m1_2; /* m1 > m2 */
285
286 pPrec->MSA_ERROR = 0;
287
288 pPrec->pWF22AS = NULL;
289
290 // get first digit of precessing version: this tags the method employed to compute the Euler angles
291 // 1: NNLO; 2: MSA; 3: SpinTaylor (numerical)
292 int precversionTag=(pPrec->IMRPhenomXPrecVersion-(pPrec->IMRPhenomXPrecVersion%100))/100;
293
294 /* start of SpinTaylor code */
295 if(precversionTag==3)
296 {
297 // allocate memory to store arrays with results of the PN precession equations
300 pPrec->L_MAX_PNR=pPrec->M_MAX;
301
302 // check mode array to estimate frequency range over which splines will need to be evaluated
303 LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(lalParams);
304 INT4 LMAX_PNR = 2;
305 if (ModeArray != NULL)
306 {
307 if(XLALSimInspiralModeArrayIsModeActive(ModeArray, 4, 4)){
308 LMAX_PNR = 4;
309 }
310 else if((XLALSimInspiralModeArrayIsModeActive(ModeArray, 3, 3))||(XLALSimInspiralModeArrayIsModeActive(ModeArray, 3, 2))){
311 LMAX_PNR = 3;
312 }
313 pPrec->L_MAX_PNR = LMAX_PNR;
314 IMRPhenomX_GetandSetModes(ModeArray,pPrec);
315 XLALDestroyValue(ModeArray);
316 }
317 // buffer for GSL interpolation to succeed
318 // set first to fMin
319 REAL8 flow = pWF->fMin;
320
321 if(pWF->deltaF==0.) pWF->deltaMF = get_deltaF_from_wfstruct(pWF);
322
323 // if PNR angles are disabled, step back accordingly to the waveform's frequency grid step
324 if(PNRUseTunedAngles==false)
325 {
326
327 pPrec->integration_buffer = (pWF->deltaF>0.)? 3.*pWF->deltaF: 0.5;
328 flow = (pWF->fMin-pPrec->integration_buffer)*2./pPrec->M_MAX;
329
330 }
331 // if PNR angles are enabled, adjust buffer to the requirements of IMRPhenomX_PNR_GeneratePNRAngleInterpolants
332 else{
333
334 size_t iStart_here;
335
336 if (pWF->deltaF == 0.) iStart_here = 0;
337 else{
338 iStart_here= (size_t)(pWF->fMin / pWF->deltaF);
339 flow = iStart_here * pWF->deltaF;
340 }
341
342 REAL8 fmin_HM_inspiral = flow * 2.0 / pPrec->M_MAX;
343
344 INT4 precVersion = pPrec->IMRPhenomXPrecVersion;
345 // fill in a fake value to allow the next code to work
346 pPrec->IMRPhenomXPrecVersion = 223;
348 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_GetAndSetPNRVariables failed in IMRPhenomXGetAndSetPrecessionVariables.\n");
349
350 /* generate alpha parameters to catch edge cases */
353 status = IMRPhenomX_PNR_precompute_alpha_coefficients(alphaParams, pWF, pPrec);
357 "Error: IMRPhenomX_PNR_precompute_alpha_coefficients failed.\n");
358 status = IMRPhenomX_PNR_precompute_beta_coefficients(betaParams, pWF, pPrec);
362 "Error: IMRPhenomX_PNR_precompute_beta_coefficients failed.\n");
367 "Error: IMRPhenomX_PNR_BetaConnectionFrequencies failed.\n");
368 pPrec->IMRPhenomXPrecVersion = precVersion;
369 REAL8 Mf_alpha_upper = alphaParams->A4 / 3.0;
370 REAL8 Mf_low_cut = (3.0 / 3.5) * Mf_alpha_upper;
371 REAL8 MF_high_cut = betaParams->Mf_beta_lower;
372 LALFree(alphaParams);
373 LALFree(betaParams);
374
375 if((MF_high_cut > pWF->fCutDef) || (MF_high_cut < 0.1 * pWF->fRING)){
376 MF_high_cut = pWF->fRING;
377 }
378 if((Mf_low_cut > pWF->fCutDef) || (MF_high_cut < Mf_low_cut)){
379 Mf_low_cut = MF_high_cut / 2.0;
380 }
381
382 REAL8 flow_alpha = XLALSimIMRPhenomXUtilsMftoHz(Mf_low_cut * 0.65 * pPrec->M_MAX / 2.0, pWF->Mtot);
383
384 if(flow_alpha < flow){
385 // flow is approximately in the intermediate region of the frequency map
386 // conservatively reduce flow to account for potential problems in this region
387 flow = fmin_HM_inspiral / 1.5;
388 }
389 else{
390 REAL8 Mf_RD_22 = pWF->fRING;
391 REAL8 Mf_RD_lm = IMRPhenomXHM_GenerateRingdownFrequency(pPrec->L_MAX_PNR, pPrec->M_MAX, pWF);
392 REAL8 fmin_HM_ringdowm = XLALSimIMRPhenomXUtilsMftoHz(XLALSimIMRPhenomXUtilsHztoMf(flow, pWF->Mtot) - (Mf_RD_lm - Mf_RD_22), pWF->Mtot);
393 flow = ((fmin_HM_ringdowm < fmin_HM_inspiral)&&(fmin_HM_ringdowm > 0.0)) ? fmin_HM_ringdowm : fmin_HM_inspiral;
394 }
395
396
397 double pnr_interpolation_deltaf = IMRPhenomX_PNR_HMInterpolationDeltaF(flow, pWF, pPrec);
398 pPrec->integration_buffer = 1.4*pnr_interpolation_deltaf;
399 flow = (flow - 2.0 * pnr_interpolation_deltaf < 0) ? flow / 2.0 : flow - 2.0 * pnr_interpolation_deltaf;
400
401 iStart_here = (size_t)(flow / pnr_interpolation_deltaf);
402 flow = iStart_here * pnr_interpolation_deltaf;
403 }
404
405 XLAL_CHECK(flow>0.,XLAL_EDOM,"Error in %s: starting frequency for SpinTaylor angles must be positive!",__func__);
406 status = IMRPhenomX_InspiralAngles_SpinTaylor(pPrec->PNarrays,&pPrec->fmin_integration,chi1x,chi1y,chi1z,chi2x,chi2y,chi2z,flow,pPrec->IMRPhenomXPrecVersion,pWF,lalParams);
407 // convert the min frequency of integration to geometric units for later convenience
409
410 if (pPrec->IMRPhenomXPrecVersion == 330)
411 {
412
413 REAL8 chi1x_evolved = chi1x;
414 REAL8 chi1y_evolved = chi1y;
415 REAL8 chi1z_evolved = chi1z;
416 REAL8 chi2x_evolved = chi2x;
417 REAL8 chi2y_evolved = chi2y;
418 REAL8 chi2z_evolved = chi2z;
419
420 // in case that SpinTaylor angles generate, overwrite variables with evolved spins
421 if(status!=XLAL_FAILURE) {
422 size_t lenPN = pPrec->PNarrays->V_PN->data->length;
423
424 REAL8 chi1x_temp = pPrec->PNarrays->S1x_PN->data->data[lenPN-1];
425 REAL8 chi1y_temp = pPrec->PNarrays->S1y_PN->data->data[lenPN-1];
426 REAL8 chi1z_temp = pPrec->PNarrays->S1z_PN->data->data[lenPN-1];
427
428 REAL8 chi2x_temp = pPrec->PNarrays->S2x_PN->data->data[lenPN-1];
429 REAL8 chi2y_temp = pPrec->PNarrays->S2y_PN->data->data[lenPN-1];
430 REAL8 chi2z_temp = pPrec->PNarrays->S2z_PN->data->data[lenPN-1];
431
432 REAL8 Lx = pPrec->PNarrays->LNhatx_PN->data->data[lenPN-1];
433 REAL8 Ly = pPrec->PNarrays->LNhaty_PN->data->data[lenPN-1];
434 REAL8 Lz = pPrec->PNarrays->LNhatz_PN->data->data[lenPN-1];
435
436 // orbital separation vector not stored in PN arrays
437 //REAL8 nx = pPrec->PNarrays->E1x->data->data[lenPN-1];
438 //REAL8 ny = pPrec->PNarrays->E1y->data->data[lenPN-1];
439
440 // rotate to get x,y,z components in L||z frame
441 REAL8 phi = atan2( Ly, Lx );
442 REAL8 theta = acos( Lz / sqrt(Lx*Lx + Ly*Ly + Lz*Lz) );
443 //REAL8 kappa = atan( ny/nx );
444
445 IMRPhenomX_rotate_z(-phi, &chi1x_temp, &chi1y_temp, &chi1z_temp);
446 IMRPhenomX_rotate_y(-theta, &chi1x_temp, &chi1y_temp, &chi1z_temp);
447 //IMRPhenomX_rotate_z(-kappa, &chi1x_temp, &chi1y_temp, &chi1z_temp);
448
449 IMRPhenomX_rotate_z(-phi, &chi2x_temp, &chi2y_temp, &chi2z_temp);
450 IMRPhenomX_rotate_y(-theta, &chi2x_temp, &chi2y_temp, &chi2z_temp);
451 //IMRPhenomX_rotate_z(-kappa, &chi2x_temp, &chi2y_temp, &chi2z_temp);
452
453 chi1x_evolved = chi1x_temp;
454 chi1y_evolved = chi1y_temp;
455 chi1z_evolved = chi1z_temp;
456
457 chi2x_evolved = chi2x_temp;
458 chi2y_evolved = chi2y_temp;
459 chi2z_evolved = chi2z_temp;
460 }
461
462 pPrec->chi1x_evolved = chi1x_evolved;
463 pPrec->chi1y_evolved = chi1y_evolved;
464 pPrec->chi1z_evolved = chi1z_evolved;
465 pPrec->chi2x_evolved = chi2x_evolved;
466 pPrec->chi2y_evolved = chi2y_evolved;
467 pPrec->chi2z_evolved = chi2z_evolved;
468
469 //printf("%f, %f, %f, %f, %f, %f\n", chi1x, chi1y, chi1z, chi2x, chi2y, chi2z);
470 //printf("%f, %f, %f, %f, %f, %f\n", chi1x_evolved, chi1y_evolved, chi1z_evolved, chi2x_evolved, chi2y_evolved, chi2z_evolved);
471 //printf("----\n");
472 }
473
474 // if PN numerical integration fails, default to MSA+fallback to NNLO
475 if(status==XLAL_FAILURE) {
476 LALFree(pPrec->PNarrays);
477 XLAL_PRINT_WARNING("Warning: due to a failure in the SpinTaylor routines, the model will default to MSA angles.");
478 pPrec->IMRPhenomXPrecVersion=223;
479 }
480 // end of SpinTaylor code
481
482 }
483
484
485
486 /* update precessing version to catch possible fallbacks of SpinTaylor angles */
487 precversionTag=(pPrec->IMRPhenomXPrecVersion-(pPrec->IMRPhenomXPrecVersion%100))/100;
488 int pflag = pPrec->IMRPhenomXPrecVersion;
489
490
491 if(pflag != 101 && pflag != 102 && pflag != 103 && pflag != 104 && pflag != 220 && pflag != 221 && pflag != 222 && pflag != 223 && pflag != 224 && pflag!=310 && pflag!=311 && pflag!=320 && pflag!=321 && pflag!=330)
492 {
493 XLAL_ERROR(XLAL_EINVAL, "Error in IMRPhenomXGetAndSetPrecessionVariables: Invalid precession flag. Allowed versions are 101, 102, 103, 104, 220, 221, 222, 223, 224, 310, 311, 320, 321 or 330.\n");
494 }
495
496 switch( pflag )
497 {
498 case 101: // NNLO single spin PNEuler angles + 2PN non-spinning L
499 case 102: // NNLO single spin PNEuler angles + 3PN spinning L
500 case 103: // NNLO single spin PNEuler angles + 4PN spinning L
501 case 104: // NNLO single spin PNEuler angles + 4PN spinning L + LOS terms in L
502 {
503 break;
504 }
505 case 220: // MSA using expressions as detailed in arXiv:1703.03967. Defaults to NNLO v102 if MSA fails.
506 case 221: // MSA using expressions as detailed in arXiv:1703.03967. Terminal failure if MSA fails.
507 case 222: // MSA using expressions as implemented in LALSimInspiralFDPrecAngles. Terminal failure if MSA fails.
508 case 223: // MSA using expressions as implemented in LALSimInspiralFDPrecAngles. Defaults to NNLO v102 if MSA fails.
509 case 224: // MSA using expressions as detailed in arXiv:1703.03967, with \zeta_0 and \phi_{z,0} as in LALSimInspiralFDPrecAngles. Defaults to NNLO v102 if MSA fails.
510 {
511 /*
512 Double-spin model using angles from Chatziioannou et al, PRD, 95, 104004, (2017), arXiv:1703.03967
513 Uses 3PN L
514 */
515 #if DEBUG == 1
516 printf("Initializing MSA system...\n");
517 #endif
518
519 if(pPrec->ExpansionOrder < -1 || pPrec->ExpansionOrder > 5)
520 {
521 XLAL_ERROR(XLAL_EINVAL, "Error in IMRPhenomXGetAndSetPrecessionVariables: Invalid expansion order for MSA corrections. Default is 5, allowed values are [-1,0,1,2,3,4,5].\n");
522 }
523 break;
524
525 }
526
527 case 310: // Numerical integration of SpinTaylor equations, constant angles in MRD
528 case 311: // Numerical integration of SpinTaylor equations, constant angles in MRD, BBH precession
529 case 320: // Numerical integration of SpinTaylor equations, analytical continuation in MRD
530 case 321: // Numerical integration of SpinTaylor equations, analytical continuation in MRD, BBH precession
531 case 330: // Numerical integration of SpinTaylor equations, PNR angles, analytic joining
532 {
533 break;
534 }
535
536
537 default:
538 {
539 XLAL_ERROR(XLAL_EINVAL, "Error in IMRPhenomXGetAndSetPrecessionVariables: IMRPhenomXPrecessionVersion not recognized.\n");
540 break;
541 }
542 }
543
544
545 pPrec->precessing_tag=precversionTag;
546
547
548 /* Calculate parameter for two-spin to single-spin map used in PNR and XCP */
549 /* Initialize PNR variables */
550 pPrec->chi_singleSpin = 0.0;
551 pPrec->costheta_singleSpin = 0.0;
552 pPrec->costheta_final_singleSpin = 0.0;
553 pPrec->chi_singleSpin_antisymmetric = 0.0;
554 pPrec->theta_antisymmetric = 0.0;
555 pPrec->PNR_HM_Mflow = 0.0;
556 pPrec->PNR_HM_Mfhigh = 0.0;
557
558 pPrec->PNR_q_window_lower = 0.0;
559 pPrec->PNR_q_window_upper = 0.0;
560 pPrec->PNR_chi_window_lower = 0.0;
561 pPrec->PNR_chi_window_upper = 0.0;
562 // pPrec->PNRInspiralScaling = 0;
563
565 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_GetAndSetPNRVariables failed in IMRPhenomXGetAndSetPrecessionVariables.\n");
566
567 pPrec->alphaPNR = 0.0;
568 pPrec->betaPNR = 0.0;
569 pPrec->gammaPNR = 0.0;
570
571 /*...#...#...#...#...#...#...#...#...#...#...#...#...#...#.../
572 / Get and/or store CoPrec params into pWF and pPrec /
573 /...#...#...#...#...#...#...#...#...#...#...#...#...#...#...*/
574
575 status = IMRPhenomX_PNR_GetAndSetCoPrecParams(pWF,pPrec,lalParams);
577 "Error: IMRPhenomX_PNR_GetAndSetCoPrecParams failed \
578 in IMRPhenomXGetAndSetPrecessionVariables.\n");
579
580 /*..#...#...#...#...#...#...#...#...#...#...#...#...#...#...*/
581
582
583
584 //
585 if( pflag == 220 || pflag == 221 || pflag == 222 || pflag == 223 || pflag == 224 )
586 {
587 #if DEBUG == 1
588 printf("Evaluating MSA system.\n");
589 printf("Expansion Order : %d\n",pPrec->ExpansionOrder);
590 #endif
591
593
594 if(pPrec->MSA_ERROR == 1)
595 {
596 // In version 220, 223 and 224 if the MSA system fails to initialize we default to the NNLO PN angles using the 3PN aligned-spin orbital angular momentum
597 if(pflag == 220 || pflag == 223 || pflag == 224)
598 {
599 XLAL_PRINT_WARNING("Warning: Initialization of MSA system failed. Defaulting to NNLO angles using 3PN aligned-spin approximation.");
600 pPrec->IMRPhenomXPrecVersion = 102;
601 pflag = pPrec->IMRPhenomXPrecVersion;
602 }
603 else // Otherwise, if the MSA system fails to initialize we trigger a terminal error
604 {
605 XLAL_ERROR(XLAL_EDOM,"Error: IMRPhenomX_Initialize_MSA_System failed to initialize. Terminating.\n");
606 }
607 }
608 }
609
610 #if DEBUG == 1
611 printf("In IMRPhenomXSetPrecessionVariables... \n\n");
612 printf("chi_p : %e\n",pPrec->chi_p);
613 printf("phic : %e\n",pPrec->phi0_aligned);
614 printf("SL : %e\n",pPrec->SL);
615 printf("Sperp : %e\n\n",pPrec->Sperp);
616 #endif
617
618 /*...#...#...#...#...#...#...#...#...#...#...#...#...#...#.../
619 / Compute and set final spin and RD frequency /
620 /...#...#...#...#...#...#...#...#...#...#...#...#...#...#...*/
621 IMRPhenomX_SetPrecessingRemnantParams(pWF,pPrec,lalParams);
622 /*..#...#...#...#...#...#...#...#...#...#...#...#...#...#...*/
623
624 /* Useful powers of \chi_p */
625 const REAL8 chip2 = chip * chip;
626
627 /* Useful powers of spins aligned with L */
628 const REAL8 chi1L2 = chi1L * chi1L;
629 const REAL8 chi2L2 = chi2L * chi2L;
630
631 const REAL8 log16 = 2.772588722239781;
632
633 /* Cache the orbital angular momentum coefficients for future use.
634
635 References:
636 - Kidder, PRD, 52, 821-847, (1995), arXiv:gr-qc/9506022
637 - Blanchet, LRR, 17, 2, (2014), arXiv:1310.1528
638 - Bohe et al, 1212.5520v2
639 - Marsat, CQG, 32, 085008, (2015), arXiv:1411.4118
640 */
641 switch( pflag )
642 {
643 /* 2PN non-spinning orbital angular momentum (as per IMRPhenomPv2) */
644 case 101:
645 {
646 pPrec->L0 = 1.0;
647 pPrec->L1 = 0.0;
648 pPrec->L2 = ((3.0/2.0) + (eta/6.0));
649 pPrec->L3 = 0.0;
650 pPrec->L4 = (81.0 + (-57.0 + eta)*eta)/24.;
651 pPrec->L5 = 0.0;
652 pPrec->L6 = 0.0;
653 pPrec->L7 = 0.0;
654 pPrec->L8 = 0.0;
655 pPrec->L8L = 0.0;
656 break;
657 }
658 /* 3PN orbital angular momentum */
659 case 102:
660 case 220:
661 case 221:
662 case 224:
663 case 310:
664 case 311:
665 case 320:
666 case 321:
667 case 330:
668 {
669 pPrec->L0 = 1.0;
670 pPrec->L1 = 0.0;
671 pPrec->L2 = 3.0/2. + eta/6.0;
672 pPrec->L3 = (5*(chi1L*(-2 - 2*delta + eta) + chi2L*(-2 + 2*delta + eta)))/6.;
673 pPrec->L4 = (81 + (-57 + eta)*eta)/24.;
674 pPrec->L5 = (-7*(chi1L*(72 + delta*(72 - 31*eta) + eta*(-121 + 2*eta)) + chi2L*(72 + eta*(-121 + 2*eta) + delta*(-72 + 31*eta))))/144.;
675 pPrec->L6 = (10935 + eta*(-62001 + eta*(1674 + 7*eta) + 2214*powers_of_lalpi.two))/1296.;
676 pPrec->L7 = 0.0;
677 pPrec->L8 = 0.0;
678
679 // This is the log(x) term
680 pPrec->L8L = 0.0;
681 break;
682
683 }
684 /* 3PN orbital angular momentum using non-conserved spin norms as per LALSimInspiralFDPrecAngles.c */
685 case 222:
686 case 223:
687 {
688 pPrec->L0 = 1.0;
689 pPrec->L1 = 0.0;
690 pPrec->L2 = 3.0/2. + eta/6.0;
691 pPrec->L3 = (-7*(chi1L + chi2L + chi1L*delta - chi2L*delta) + 5*(chi1L + chi2L)*eta)/6.;
692 pPrec->L4 = (81 + (-57 + eta)*eta)/24.;
693 pPrec->L5 = (-1650*(chi1L + chi2L + chi1L*delta - chi2L*delta) + 1336*(chi1L + chi2L)*eta + 511*(chi1L - chi2L)*delta*eta + 28*(chi1L + chi2L)*eta2)/600.;
694 pPrec->L6 = (10935 + eta*(-62001 + 1674*eta + 7*eta2 + 2214*powers_of_lalpi.two))/1296.;
695 pPrec->L7 = 0.0;
696 pPrec->L8 = 0.0;
697
698 // This is the log(x) term
699 pPrec->L8L = 0.0;
700 break;
701 }
702 /* 4PN orbital angular momentum */
703 case 103:
704 {
705 pPrec->L0 = 1.0;
706 pPrec->L1 = 0.0;
707 pPrec->L2 = 3.0/2. + eta/6.0;
708 pPrec->L3 = (5*(chi1L*(-2 - 2*delta + eta) + chi2L*(-2 + 2*delta + eta)))/6.;
709 pPrec->L4 = (81 + (-57 + eta)*eta)/24.;
710 pPrec->L5 = (-7*(chi1L*(72 + delta*(72 - 31*eta) + eta*(-121 + 2*eta)) + chi2L*(72 + eta*(-121 + 2*eta) + delta*(-72 + 31*eta))))/144.;
711 pPrec->L6 = (10935 + eta*(-62001 + eta*(1674 + 7*eta) + 2214*powers_of_lalpi.two))/1296.;
712 pPrec->L7 = (chi2L*(-324 + eta*(1119 - 2*eta*(172 + eta)) + delta*(324 + eta*(-633 + 14*eta)))
713 - chi1L*(324 + eta*(-1119 + 2*eta*(172 + eta)) + delta*(324 + eta*(-633 + 14*eta))))/32.;
714 pPrec->L8 = 2835/128. - (eta*(-10677852 + 100*eta*(-640863 + eta*(774 + 11*eta))
715 + 26542080*LAL_GAMMA + 675*(3873 + 3608*eta)*powers_of_lalpi.two))/622080. - (64*eta*log16)/3.;
716
717 pPrec->L8L = -(64.0/3.0) * eta;
718 break;
719 }
720 /*
721 4PN orbital angular momentum + leading order in spin at all PN orders terms.
722 - Marsat, CQG, 32, 085008, (2015), arXiv:1411.4118
723 - Siemonsen et al, PRD, 97, 064010, (2018), arXiv:1606.08832
724 */
725 case 104:
726 {
727 pPrec->L0 = 1.0;
728 pPrec->L1 = 0.0;
729 pPrec->L2 = 3.0/2. + eta/6.0;
730 pPrec->L3 = (5*(chi1L*(-2 - 2*delta + eta) + chi2L*(-2 + 2*delta + eta)))/6.;
731 pPrec->L4 = (81 + (-57 + eta)*eta)/24.;
732 pPrec->L5 = (-7*(chi1L*(72 + delta*(72 - 31*eta) + eta*(-121 + 2*eta)) + chi2L*(72 + eta*(-121 + 2*eta) + delta*(-72 + 31*eta))))/144.;
733 pPrec->L6 = (10935 + eta*(-62001 + eta*(1674 + 7*eta) + 2214*powers_of_lalpi.two))/1296.;
734 pPrec->L7 = (chi2L*(-324 + eta*(1119 - 2*eta*(172 + eta)) + delta*(324 + eta*(-633 + 14*eta)))
735 - chi1L*(324 + eta*(-1119 + 2*eta*(172 + eta)) + delta*(324 + eta*(-633 + 14*eta))))/32.;
736 pPrec->L8 = 2835/128. - (eta*(-10677852 + 100*eta*(-640863 + eta*(774 + 11*eta))
737 + 26542080*LAL_GAMMA + 675*(3873 + 3608*eta)*powers_of_lalpi.two))/622080. - (64*eta*log16)/3.;
738
739 // This is the log(x) term at 4PN, x^4/2 * log(x)
740 pPrec->L8L = -(64.0/3.0) * eta;
741
742 // Leading order in spin at all PN orders, note that the 1.5PN terms are already included. Here we have additional 2PN and 3.5PN corrections.
743 pPrec->L4 += (chi1L2*(1 + delta - 2*eta) + 4*chi1L*chi2L*eta - chi2L2*(-1 + delta + 2*eta))/2.;
744 pPrec->L7 += (3*(chi1L + chi2L)*eta*(chi1L2*(1 + delta - 2*eta) + 4*chi1L*chi2L*eta - chi2L2*(-1 + delta + 2*eta)))/4.;
745
746 break;
747 }
748
749 default:
750 {
751 XLAL_ERROR(XLAL_EINVAL,"Error: IMRPhenomXPrecVersion not recognized. Requires version 101, 102, 103, 104, 220, 221, 222, 223, 224, 310, 311, 320, 321 or 330.\n");
752 break;
753 }
754 }
755
756 /* Reference orbital angular momentum */
757 pPrec->LRef = M * M * XLALSimIMRPhenomXLPNAnsatz(pWF->v_ref, pWF->eta / pWF->v_ref, pPrec->L0, pPrec->L1, pPrec->L2, pPrec->L3, pPrec->L4, pPrec->L5, pPrec->L6, pPrec->L7, pPrec->L8, pPrec->L8L);
758
759 /*
760 In the following code block we construct the convetions that relate the source frame and the LAL frame.
761
762 A detailed discussion of the conventions can be found in Appendix C and D of arXiv:2004.06503 and https://dcc.ligo.org/LIGO-T1500602
763 */
764
765 /* Get source frame (*_Sf) J = L + S1 + S2. This is an instantaneous frame in which L is aligned with z */
766 pPrec->J0x_Sf = (m1_2)*chi1x + (m2_2)*chi2x;
767 pPrec->J0y_Sf = (m1_2)*chi1y + (m2_2)*chi2y;
768 pPrec->J0z_Sf = (m1_2)*chi1z + (m2_2)*chi2z + pPrec->LRef;
769
770 pPrec->J0 = sqrt(pPrec->J0x_Sf*pPrec->J0x_Sf + pPrec->J0y_Sf*pPrec->J0y_Sf + pPrec->J0z_Sf*pPrec->J0z_Sf);
771
772 /* Get angle between J0 and LN (z-direction) */
773 if(pPrec->J0 < 1e-10)
774 {
775 XLAL_PRINT_WARNING("Warning: |J0| < 1e-10. Setting thetaJ = 0.\n");
776 pPrec->thetaJ_Sf = 0.0;
777 }
778 else
779 {
780 pPrec->thetaJ_Sf = acos(pPrec->J0z_Sf / pPrec->J0);
781 }
782
783 const double phiRef = pWF->phiRef_In;
784
786
787 if ( !(convention == 0 || convention == 1 || convention == 5 || convention == 6 || convention == 7))
788 {
789 XLAL_ERROR(XLAL_EINVAL,"Error: IMRPhenomXPConvention not recognized. Requires version 0, 1, 5, 6 or 7.\n");
790 }
791
792 #if DEBUG == 1
793 printf("\n*** Convention = %i\n", convention);
794 #endif
795
796 /* Get azimuthal angle of J0 in the source frame */
797 if(fabs(pPrec->J0x_Sf) < MAX_TOL_ATAN && fabs(pPrec->J0y_Sf) < MAX_TOL_ATAN)
798 {
799 #if DEBUG == 1
800 printf("\nAligned spin limit!\n");
801 #endif
802
803 /* Impose the aligned spin limit */
804 switch(convention)
805 {
806 case 0:
807 case 5:
808 {
809 pPrec->phiJ_Sf = LAL_PI/2.0 - phiRef;
810 break;
811 }
812 case 1:
813 case 6:
814 case 7:
815 {
816 pPrec->phiJ_Sf = 0;
817 break;
818 }
819
820 }
821 }
822 else
823 {
824 pPrec->phiJ_Sf = atan2(pPrec->J0y_Sf, pPrec->J0x_Sf); /* azimuthal angle of J0 in the source frame */
825 }
826 pPrec->phi0_aligned = - pPrec->phiJ_Sf;
827
828 switch(convention)
829 {
830 case 0:
831 {
832 pWF->phi0 = pPrec->phi0_aligned;
833 break;
834 }
835 case 1:
836 {
837 pWF->phi0 = 0;
838 break;
839 }
840 case 5:
841 case 6:
842 case 7:
843 {
844 break;
845 }
846 }
847
848 /*
849 Here we follow the same prescription as in IMRPhenomPv2:
850
851 Now rotate from SF to J frame to compute alpha0, the azimuthal angle of LN, as well as
852 thetaJ, the angle between J and N.
853
854 The J frame is defined by imposing that J points in the z-direction and the line of sight N is in the xz-plane
855 (with positive projection along x).
856
857 The components of any vector in the (new) J-frame can be obtained by rotation from the (old) source frame (SF).
858 This is done by multiplying by: RZ[-kappa].RY[-thetaJ].RZ[-phiJ]
859
860 Note that kappa is determined by rotating N with RY[-thetaJ].RZ[-phiJ], which brings J to the z-axis, and
861 taking the opposite of the azimuthal angle of the rotated N.
862 */
863
864 /* Determine kappa via rotations, as above */
865 pPrec->Nx_Sf = sin(pWF->inclination)*cos((LAL_PI / 2.0) - phiRef);
866 pPrec->Ny_Sf = sin(pWF->inclination)*sin((LAL_PI / 2.0) - phiRef);
867 pPrec->Nz_Sf = cos(pWF->inclination);
868
869 REAL8 tmp_x = pPrec->Nx_Sf;
870 REAL8 tmp_y = pPrec->Ny_Sf;
871 REAL8 tmp_z = pPrec->Nz_Sf;
872
873 IMRPhenomX_rotate_z(-pPrec->phiJ_Sf, &tmp_x, &tmp_y, &tmp_z);
874 IMRPhenomX_rotate_y(-pPrec->thetaJ_Sf, &tmp_x, &tmp_y, &tmp_z);
875
876 /* Note difference in overall - sign w.r.t PhenomPv2 code */
877 pPrec->kappa = XLALSimIMRPhenomXatan2tol(tmp_y,tmp_x, MAX_TOL_ATAN);
878
879 /* Now determine alpha0 by rotating LN. In the source frame, LN = {0,0,1} */
880 tmp_x = 0.0;
881 tmp_y = 0.0;
882 tmp_z = 1.0;
883 IMRPhenomX_rotate_z(-pPrec->phiJ_Sf, &tmp_x, &tmp_y, &tmp_z);
884 IMRPhenomX_rotate_y(-pPrec->thetaJ_Sf, &tmp_x, &tmp_y, &tmp_z);
885 IMRPhenomX_rotate_z(-pPrec->kappa, &tmp_x, &tmp_y, &tmp_z);
886
887 if (fabs(tmp_x) < MAX_TOL_ATAN && fabs(tmp_y) < MAX_TOL_ATAN)
888 {
889 /* This is the aligned spin case */
890 #if DEBUG == 1
891 printf("\nAligned-spin case.\n");
892 #endif
893
894 switch(convention)
895 {
896 case 0:
897 case 5:
898 {
899 pPrec->alpha0 = LAL_PI;
900 break;
901 }
902 case 1:
903 case 6:
904 case 7:
905 {
906 pPrec->alpha0 = LAL_PI - pPrec->kappa;
907 break;
908 }
909 }
910 }
911 else
912 {
913 switch(convention)
914 {
915 case 0:
916 case 5:
917 {
918 pPrec->alpha0 = atan2(tmp_y,tmp_x);
919 break;
920 }
921 case 1:
922 case 6:
923 case 7:
924 {
925 pPrec->alpha0 = LAL_PI - pPrec->kappa;
926 break;
927 }
928 }
929 }
930
931
932 switch(convention)
933 {
934 case 0:
935 case 5:
936 {
937 /* Now determine thetaJN by rotating N */
938 tmp_x = pPrec->Nx_Sf;
939 tmp_y = pPrec->Ny_Sf;
940 tmp_z = pPrec->Nz_Sf;
941 IMRPhenomX_rotate_z(-pPrec->phiJ_Sf, &tmp_x, &tmp_y, &tmp_z);
942 IMRPhenomX_rotate_y(-pPrec->thetaJ_Sf, &tmp_x, &tmp_y, &tmp_z);
943 IMRPhenomX_rotate_z(-pPrec->kappa, &tmp_x, &tmp_y, &tmp_z);
944
945 /* We don't need the y-component but we will store it anyway */
946 pPrec->Nx_Jf = tmp_x;
947 pPrec->Ny_Jf = tmp_y;
948 pPrec->Nz_Jf = tmp_z;
949
950 /* This is a unit vector, so no normalization */
951 pPrec->thetaJN = acos(pPrec->Nz_Jf);
952 break;
953 }
954 case 1:
955 case 6:
956 case 7:
957 {
958 REAL8 J0dotN = (pPrec->J0x_Sf * pPrec->Nx_Sf) + (pPrec->J0y_Sf * pPrec->Ny_Sf) + (pPrec->J0z_Sf * pPrec->Nz_Sf);
959 pPrec->thetaJN = acos( J0dotN / pPrec->J0 );
960 pPrec->Nz_Jf = cos(pPrec->thetaJN);
961 pPrec->Nx_Jf = sin(pPrec->thetaJN);
962 break;
963 }
964 }
965
966
967 /*
968 Define the polarizations used. This follows the conventions adopted for IMRPhenomPv2.
969
970 The IMRPhenomP polarizations are defined following the conventions in Arun et al (arXiv:0810.5336),
971 i.e. projecting the metric onto the P, Q, N triad defining where: P = (N x J) / |N x J|.
972
973 However, the triad X,Y,N used in LAL (the "waveframe") follows the definition in the
974 NR Injection Infrastructure (Schmidt et al, arXiv:1703.01076).
975
976 The triads differ from each other by a rotation around N by an angle \zeta. We therefore need to rotate
977 the polarizations by an angle 2 \zeta.
978 */
979 pPrec->Xx_Sf = -cos(pWF->inclination) * sin(phiRef);
980 pPrec->Xy_Sf = -cos(pWF->inclination) * cos(phiRef);
981 pPrec->Xz_Sf = +sin(pWF->inclination);
982
983 tmp_x = pPrec->Xx_Sf;
984 tmp_y = pPrec->Xy_Sf;
985 tmp_z = pPrec->Xz_Sf;
986
987 IMRPhenomX_rotate_z(-pPrec->phiJ_Sf, &tmp_x, &tmp_y, &tmp_z);
988 IMRPhenomX_rotate_y(-pPrec->thetaJ_Sf, &tmp_x, &tmp_y, &tmp_z);
989 IMRPhenomX_rotate_z(-pPrec->kappa, &tmp_x, &tmp_y, &tmp_z);
990
991
992 /*
993 The components tmp_i are now the components of X in the J frame.
994
995 We now need the polar angle of this vector in the P, Q basis of Arun et al:
996
997 P = (N x J) / |NxJ|
998
999 Note, that we put N in the (pos x)z half plane of the J frame
1000 */
1001
1002 switch(convention)
1003 {
1004 case 0:
1005 case 5:
1006 {
1007 /* Get polar angle of X vector in J frame in the P,Q basis of Arun et al */
1008 pPrec->PArunx_Jf = +0.0;
1009 pPrec->PAruny_Jf = -1.0;
1010 pPrec->PArunz_Jf = +0.0;
1011
1012 /* Q = (N x P) by construction */
1013 pPrec->QArunx_Jf = pPrec->Nz_Jf;
1014 pPrec->QAruny_Jf = 0.0;
1015 pPrec->QArunz_Jf = -pPrec->Nx_Jf;
1016 break;
1017 }
1018 case 1:
1019 case 6:
1020 case 7:
1021 {
1022 /* Get polar angle of X vector in J frame in the P,Q basis of Arun et al */
1023 pPrec->PArunx_Jf = pPrec->Nz_Jf;
1024 pPrec->PAruny_Jf = 0;
1025 pPrec->PArunz_Jf = -pPrec->Nx_Jf;
1026
1027 /* Q = (N x P) by construction */
1028 pPrec->QArunx_Jf = 0;
1029 pPrec->QAruny_Jf = 1;
1030 pPrec->QArunz_Jf = 0;
1031 break;
1032 }
1033 }
1034
1035 // (X . P)
1036 pPrec->XdotPArun = (tmp_x * pPrec->PArunx_Jf) + (tmp_y * pPrec->PAruny_Jf) + (tmp_z * pPrec->PArunz_Jf);
1037
1038 // (X . Q)
1039 pPrec->XdotQArun = (tmp_x * pPrec->QArunx_Jf) + (tmp_y * pPrec->QAruny_Jf) + (tmp_z * pPrec->QArunz_Jf);
1040
1041 /* Now get the angle zeta */
1042 pPrec->zeta_polarization = atan2(pPrec->XdotQArun, pPrec->XdotPArun);
1043
1044 /* ********** PN Euler Angle Coefficients ********** */
1045 /*
1046 This uses the single spin PN Euler angles as per IMRPhenomPv2
1047 */
1048
1049 /* ********** PN Euler Angle Coefficients ********** */
1050 switch( pflag )
1051 {
1052 case 101:
1053 case 102:
1054 case 103:
1055 case 104:
1056 {
1057 /*
1058 This uses the single spin PN Euler angles as per IMRPhenomPv2
1059 */
1060
1061 /* Post-Newtonian Euler Angles: alpha */
1062 REAL8 chiL = (1.0 + q) * (chi_eff / q);
1063 REAL8 chiL2 = chiL * chiL;
1064
1065 pPrec->alpha1 = -35/192. + (5*delta)/(64.*m1);
1066
1067 pPrec->alpha2 = ((15*chiL*delta*m1)/128. - (35*chiL*m1_2)/128.)/eta;
1068
1069 pPrec->alpha3 = -5515/3072. + eta*(-515/384. - (15*delta2)/(256.*m1_2)
1070 + (175*delta)/(256.*m1)) + (4555*delta)/(7168.*m1)
1071 + ((15*chip2*delta*m1_3)/128. - (35*chip2*m1_4)/128.)/eta2;
1072
1073 /* This is the term proportional to log(w) */
1074 pPrec->alpha4L = ((5*chiL*delta2)/16. - (5*chiL*delta*m1)/3. + (2545*chiL*m1_2)/1152.
1075 + ((-2035*chiL*delta*m1)/21504.
1076 + (2995*chiL*m1_2)/9216.)/eta + ((5*chiL*chip2*delta*m1_5)/128.
1077 - (35*chiL*chip2*m1_6)/384.)/eta3
1078 - (35*LAL_PI)/48. + (5*delta*LAL_PI)/(16.*m1));
1079
1080 pPrec->alpha5 = (5*(-190512*delta3*eta6 + 2268*delta2*eta3*m1*(eta2*(323 + 784*eta)
1081 + 336*(25*chiL2 + chip2)*m1_4) + 7*m1_3*(8024297*eta4 + 857412*eta5
1082 + 3080448*eta6 + 143640*chip2*eta2*m1_4
1083 - 127008*chip2*(-4*chiL2 + chip2)*m1_8
1084 + 6048*eta3*((2632*chiL2 + 115*chip2)*m1_4 - 672*chiL*m1_2*LAL_PI))
1085 + 3*delta*m1_2*(-5579177*eta4 + 80136*eta5 - 3845520*eta6
1086 + 146664*chip2*eta2*m1_4 + 127008*chip2*(-4*chiL2 + chip2)*m1_8
1087 - 42336*eta3*((726*chiL2 + 29*chip2)*m1_4
1088 - 96*chiL*m1_2*LAL_PI))))/(6.5028096e7*eta4*m1_3);
1089
1090 /* Post-Newtonian Euler Angles: epsilon */
1091 pPrec->epsilon1 = -35/192. + (5*delta)/(64.*m1);
1092
1093 pPrec->epsilon2 = ((15*chiL*delta*m1)/128. - (35*chiL*m1_2)/128.)/eta;
1094
1095 pPrec->epsilon3 = -5515/3072. + eta*(-515/384. - (15*delta2)/(256.*m1_2)
1096 + (175*delta)/(256.*m1)) + (4555*delta)/(7168.*m1);
1097
1098 /* This term is proportional to log(w) */
1099 pPrec->epsilon4L = (5*chiL*delta2)/16. - (5*chiL*delta*m1)/3. + (2545*chiL*m1_2)/1152.
1100 + ((-2035*chiL*delta*m1)/21504. + (2995*chiL*m1_2)/9216.)/eta - (35*LAL_PI)/48.
1101 + (5*delta*LAL_PI)/(16.*m1);
1102
1103 pPrec->epsilon5 = (5*(-190512*delta3*eta3 + 2268*delta2*m1*(eta2*(323 + 784*eta)
1104 + 8400*chiL2*m1_4)
1105 - 3*delta*m1_2*(eta*(5579177 + 504*eta*(-159 + 7630*eta))
1106 + 254016*chiL*m1_2*(121*chiL*m1_2 - 16*LAL_PI))
1107 + 7*m1_3*(eta*(8024297 + 36*eta*(23817 + 85568*eta))
1108 + 338688*chiL*m1_2*(47*chiL*m1_2 - 12*LAL_PI))))/(6.5028096e7*eta*m1_3);
1109
1110 break;
1111 }
1112 case 220:
1113 case 221:
1114 case 222:
1115 case 223:
1116 case 224:
1117 case 310:
1118 case 311:
1119 case 320:
1120 case 321:
1121 case 330:
1122 {
1123 pPrec->alpha1 = 0;
1124 pPrec->alpha2 = 0;
1125 pPrec->alpha3 = 0;
1126 pPrec->alpha4L = 0;
1127 pPrec->alpha5 = 0;
1128 pPrec->epsilon1 = 0;
1129 pPrec->epsilon2 = 0;
1130 pPrec->epsilon3 = 0;
1131 pPrec->epsilon4L = 0;
1132 pPrec->epsilon5 = 0;
1133 break;
1134 }
1135 default:
1136 {
1137 XLAL_ERROR(XLAL_EINVAL,"Error: IMRPhenomXPrecVersion not recognized. Requires version 101, 102, 103, 104, 220, 221, 222, 223, 224, 310, 311, 320, 321 or 330.\n");
1138 break;
1139 }
1140 }
1141
1142 REAL8 alpha_offset = 0, epsilon_offset = 0;
1143
1144 #if DEBUG == 1
1145 printf("thetaJN : %e\n", pPrec->thetaJN);
1146 printf("phiJ_Sf : %e\n", pPrec->phiJ_Sf);
1147 printf("alpha0 : %e\n", pPrec->alpha0);
1148 printf("pi-kappa : %e\n", LAL_PI-pPrec->kappa);
1149 printf("kappa : %e\n", pPrec->kappa);
1150 printf("pi/2 - phiRef : %e\n", LAL_PI_2 - phiRef);
1151 printf("zeta_polarization : %.16e\n", pPrec->zeta_polarization);
1152 printf("zeta_polarization : %.16e\n", acos(pPrec->XdotPArun));
1153 printf("zeta_polarization : %.16e\n", asin(pPrec->XdotQArun));
1154 printf("zeta_polarization : %.16e\n\n", LAL_PI_2 - acos(pPrec->XdotQArun));
1155 printf("alpha1 : %e\n", pPrec->alpha1);
1156 printf("alpha2 : %e\n", pPrec->alpha2);
1157 printf("alpha3 : %e\n", pPrec->alpha3);
1158 printf("alpha4L : %e\n", pPrec->alpha4L);
1159 printf("alpha5 : %e\n\n", pPrec->alpha5);
1160 #endif
1161
1162
1163 switch(convention)
1164 {
1165 case 0:
1166 pPrec->epsilon0 = 0;
1167 break;
1168 case 1:
1169 case 6:
1170 pPrec->epsilon0 = pPrec->phiJ_Sf - LAL_PI;
1171 break;
1172 case 5:
1173 case 7:
1174 pPrec->epsilon0 = 0;
1175 break;
1176 }
1177
1178 if(convention == 5 || convention == 7)
1179 {
1180 pPrec->alpha_offset = -pPrec->alpha0;
1181 pPrec->epsilon_offset = 0;
1182 pPrec->alpha_offset_1 = -pPrec->alpha0;
1183 pPrec->epsilon_offset_1 = 0;
1184 pPrec->alpha_offset_3 = -pPrec->alpha0;
1185 pPrec->epsilon_offset_3 = 0;
1186 pPrec->alpha_offset_4 = -pPrec->alpha0;
1187 pPrec->epsilon_offset_4 = 0;
1188 }
1189 else
1190 {
1191 /* Get initial Get \alpha and \epsilon offsets at \omega = pi * M * f_{Ref} */
1192 Get_alphaepsilon_atfref(&alpha_offset, &epsilon_offset, 2, pPrec, pWF);
1193 pPrec->alpha_offset = alpha_offset;
1194 pPrec->epsilon_offset = epsilon_offset;
1195 pPrec->alpha_offset_1 = alpha_offset;
1196 pPrec->epsilon_offset_1 = epsilon_offset;
1197 pPrec->alpha_offset_3 = alpha_offset;
1198 pPrec->epsilon_offset_3 = epsilon_offset;
1199 pPrec->alpha_offset_4 = alpha_offset;
1200 pPrec->epsilon_offset_4 = epsilon_offset;
1201 }
1202
1203 pPrec->cexp_i_alpha = 0.;
1204 pPrec->cexp_i_epsilon = 0.;
1205 pPrec->cexp_i_betah = 0.;
1206
1207 /*
1208 Check whether maximum opening angle becomes larger than \pi/2 or \pi/4.
1209
1210 If (L + S_L) < 0, then Wigner-d Coefficients will not track the angle between J and L, meaning
1211 that the model may become pathological as one moves away from the aligned-spin limit.
1212
1213 If this does not happen, then max_beta will be the actual maximum opening angle.
1214
1215 This function uses a 2PN non-spinning approximation to the orbital angular momentum L, as
1216 the roots can be analytically derived.
1217
1218 Returns XLAL_PRINT_WARNING if model is in a pathological regime.
1219 */
1220
1221
1222 // When L + SL < 0 and q>7, we disable multibanding
1223 IMRPhenomXPCheckMaxOpeningAngle(pWF,pPrec,lalParams);
1224
1225 /* Activate multibanding for Euler angles it threshold !=0. Only for PhenomXPHM. */
1227 {
1228 /* User switched off multibanding */
1229 pPrec->MBandPrecVersion = 0;
1230 }
1231 else
1232 {
1233 /* User requested multibanding */
1234 pPrec->MBandPrecVersion = 1;
1235
1236 /* Switch off multiband for very high mass as in IMRPhenomXHM. */
1237 if(pWF->Mtot > 500)
1238 {
1239 XLAL_PRINT_WARNING("Very high mass, only merger in frequency band, multibanding not efficient, switching off for non-precessing modes and Euler angles.");
1240 pPrec->MBandPrecVersion = 0;
1242 }
1243 if(pPrec->IMRPhenomXPrecVersion == 330 && pWF->q > 7){
1244 /* this is here as a safety catch in case */
1245 XLAL_PRINT_WARNING("Multibanding may lead to pathological behaviour in this case. Disabling multibanding .\n");
1247 pPrec->MBandPrecVersion = 0;
1248 }
1249
1250 else if(pPrec->IMRPhenomXPrecVersion < 200)
1251 {
1252 /* The NNLO angles can have a worse, even pathological, behaviour for high mass ratio and double spin cases.
1253 The waveform will look noisy, we switch off the multibanding for mass ratio above 8 to avoid worsen even more the waveform. */
1254 if(pWF->q > 8)
1255 {
1256 XLAL_PRINT_WARNING("Very high mass ratio, NNLO angles may become pathological, switching off multibanding for angles.\n");
1258 pPrec->MBandPrecVersion = 0;
1259 }
1260 }
1261 /* The MSA angles give quite 'noisy' waveforms in this corner of parameter space so we switch off multibanding to avoid worsen the waveform. */
1262 else if ( pWF->q > 50 && pWF->Mtot > 100 )
1263 {
1265 pPrec->MBandPrecVersion = 0;
1266 }
1267
1268 }
1269
1270 /* At high mass ratios, we find there can be numerical instabilities in the model, although the waveforms continue to be well behaved.
1271 * We warn to user of the possibility of these instabilities.
1272 */
1273 //printf(pWF->q);
1274 if( pWF->q > 80 )
1275 {
1276 XLAL_PRINT_WARNING("Very high mass ratio, possibility of numerical instabilities. Waveforms remain well behaved.\n");
1277 }
1278
1279
1280 const REAL8 ytheta = pPrec->thetaJN;
1281 const REAL8 yphi = 0.0;
1282 pPrec->Y2m2 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 2, -2);
1283 pPrec->Y2m1 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 2, -1);
1284 pPrec->Y20 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 2, 0);
1285 pPrec->Y21 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 2, 1);
1286 pPrec->Y22 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 2, 2);
1287 pPrec->Y3m3 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 3, -3);
1288 pPrec->Y3m2 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 3, -2);
1289 pPrec->Y3m1 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 3, -1);
1290 pPrec->Y30 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 3, 0);
1291 pPrec->Y31 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 3, 1);
1292 pPrec->Y32 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 3, 2);
1293 pPrec->Y33 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 3, 3);
1294 pPrec->Y4m4 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 4, -4);
1295 pPrec->Y4m3 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 4, -3);
1296 pPrec->Y4m2 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 4, -2);
1297 pPrec->Y4m1 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 4, -1);
1298 pPrec->Y40 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 4, 0);
1299 pPrec->Y41 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 4, 1);
1300 pPrec->Y42 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 4, 2);
1301 pPrec->Y43 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 4, 3);
1302 pPrec->Y44 = XLALSpinWeightedSphericalHarmonic(ytheta, yphi, -2, 4, 4);
1303
1304 pPrec->LALparams = lalParams;
1305
1306 return XLAL_SUCCESS;
1307}
1308/* Function to set remnant quantities related to final
1309spin within IMRPhenomXGetAndSetPrecessionVariables */
1313 LALDict *lalParams
1314){
1315
1316 /*
1317
1318 The strategy for setting the precessing remnant spin in PhenomX
1319 is multifaceted becuase there are not only the various options
1320 implemented for the original PhenomXP, but there are also the
1321 options needed for PNR's construction, and its turning off of
1322 its CoPrecessing model outside of the PNR calibration region.
1323 In that latter case, the final spin transitions from the
1324 non-precessing final spin, where PNR's CoPrecessing model is
1325 tuned, to the precessing final spin, as is needed for the EZH
1326 effective ringdown result.
1327
1328 A layer on top of this, is the need for the l=m=2 fundamental
1329 QNM frequency to be computed, in some way, amid all of these
1330 scenarios.
1331
1332 This function exists to draw a conceptual circle around all of
1333 this mess.
1334
1335 Most comments below are cogent, but some have been left for
1336 historical record?
1337
1338 */
1339
1340 /* Define shorthand variables for PNR CoPrec options */
1341 INT4 status = 0;
1342
1343 // Toggle for PNR coprecessing tuning
1344 INT4 PNRUseInputCoprecDeviations = pPrec->IMRPhenomXPNRUseInputCoprecDeviations;
1345
1346 // Toggle for enforced use of non-precessing spin as is required during tuning of PNR's coprecessing model
1347 INT4 PNRUseTunedCoprec = pPrec->IMRPhenomXPNRUseTunedCoprec;
1348
1349 // High-level toggle for whether to apply deviations
1350 INT4 APPLY_PNR_DEVIATIONS = pWF->APPLY_PNR_DEVIATIONS;
1351
1352 /*
1353 HISTOICAL COMMENT
1354 Update the final spin in pWF to account for precessing spin effects:
1355
1356 XLALSimIMRPhenomXPrecessingFinalSpin2017(eta,chi1L,chi2L,chi_perp);
1357
1358 Note that chi_perp gets weighted by an appropriate mass factor
1359
1360 q_factor = m1/M (when m1 > m2)
1361 REAL8 Sperp = chip * q_factor * q_factor;
1362 REAL8 af = copysign(1.0, af_parallel) * sqrt(Sperp*Sperp + af_parallel*af_parallel);
1363 */
1364 REAL8 M = pWF->M;
1365 REAL8 af_parallel = XLALSimIMRPhenomXFinalSpin2017(pWF->eta,pPrec->chi1z,pPrec->chi2z);
1366 double Lfinal = M*M*af_parallel - pWF->m1_2*pPrec->chi1z - pWF->m2_2*pPrec->chi2z;
1367
1369 if (fsflag == 4 && pPrec->precessing_tag!=3) fsflag = 3;
1370
1371 /* For PhenomPNR, we wil use the PhenomPv2 final spin function's result, modified such that its sign is given by sign( cos(betaRD) ). See the related fsflag case below. */
1372 if (PNRUseTunedCoprec && fsflag < 6) fsflag = 5;
1373
1374 /* When tuning the coprecessing model, we wish to enforce use of the non-precessing final spin. See the related fsflag case below. */
1375 if (PNRUseInputCoprecDeviations) fsflag = 6;
1376
1377 /* Generate and store ringdown value of precession angle beta. This is to be used for e.g. setting the sign of the final spin, and calculating the effective ringdown frequency */
1378 if( PNRUseTunedCoprec ){
1380 }
1381
1382 //
1383 double chi1L = pPrec->chi1z;
1384 double chi2L = pPrec->chi2z;
1385
1386 //
1387 int pflag = pPrec->IMRPhenomXPrecVersion;
1388
1389 //
1390 switch(fsflag)
1391 {
1392 case 0:
1393 pWF->afinal_prec = XLALSimIMRPhenomXPrecessingFinalSpin2017(pWF->eta,chi1L,chi2L,pPrec->chi_p);
1394 break;
1395 case 1:
1396 pWF->afinal_prec = XLALSimIMRPhenomXPrecessingFinalSpin2017(pWF->eta,chi1L,chi2L,pPrec->chi1x);
1397 break;
1398 case 2:
1399 case 4:
1401 break;
1402 case 3:
1403 if( pflag == 220 || pflag == 221 || pflag == 222 || pflag == 223 || pflag == 224 )
1404 {
1405 if(pPrec->MSA_ERROR == 1 )
1406 {
1407 XLAL_PRINT_WARNING("Initialization of MSA system failed. Defaulting to final spin version 0.\n");
1408 pWF->afinal_prec = XLALSimIMRPhenomXPrecessingFinalSpin2017(pWF->eta,chi1L,chi2L,pPrec->chi_p);
1409 }
1410 else
1411 {
1412
1413 INT2 sign = 1;
1415 sign = copysign(1, af_parallel);
1416 }
1417 pWF->afinal_prec = sign * sqrt( pPrec->SAv2 + Lfinal*Lfinal + 2.0*Lfinal*(pPrec->S1L_pav + pPrec->S2L_pav) ) / (M*M);
1418
1419 }
1420 }
1421 else
1422 {
1423 XLAL_PRINT_WARNING("Error: XLALSimInspiralWaveformParamsLookupPhenomXPFinalSpinMod version 3 requires PrecVersion 220, 221, 222, 223 or 224. Defaulting to version 0.\n");
1424 pWF->afinal_prec = XLALSimIMRPhenomXPrecessingFinalSpin2017(pWF->eta,chi1L,chi2L,pPrec->chi_p);
1425 }
1426 break;
1427
1428 case 5:
1429 {
1430 /*-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~*
1431 Implement Pv2 final spin but with sign derived from EZH's model for ringdown beta.
1432 *-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~*/
1433
1434 // Use these as input into method for effective RD frequency
1435 // * get the value of ringdown beta
1436 INT2 sign = 1;
1437 sign = copysign(1, cos(pWF->betaRD) );
1438
1439 /* Calculate Pv2 final spin without alteration. Below we alter it. NOTE that XLALSimIMRPhenomXPrecessingFinalSpin2017 appears to be an explicit copy of the actual Pv2 final spin function, FinalSpinIMRPhenomD_all_in_plane_spin_on_larger_BH. The original PhenomX have not referenced this code duplication. */
1440 double afinal_prec_Pv2 = XLALSimIMRPhenomXPrecessingFinalSpin2017(pWF->eta,chi1L,chi2L,pPrec->chi_p);
1441
1442 // The eqiuvalent PhenomPv2 code reference would look like the commented line below.
1443 // double afinal_prec_Pv2 = FinalSpinIMRPhenomD_all_in_plane_spin_on_larger_BH( m1, m2, chi1L, chi2L, chi_p );
1444
1445 // Define the PNR final spin to be the Pv2 final spin magnitude, with direction given by sign of cos betaRD
1446 pWF->afinal_prec = sign * fabs(afinal_prec_Pv2);
1447
1448 // // Experimental version of final spin
1449 // pWF->afinal_prec = cos(pWF->betaRD) * fabs(afinal_prec_Pv2);
1450 }
1451 break;
1452
1453 case 6:
1454 {
1455 /*-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~*
1456 During PNR tuning, we wish to evaluate the coprecessing model with the same final spin that would be used in PhenomXHM. We implement this here.
1457 *-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~*/
1458 pWF->afinal_prec = pWF->afinal_nonprec;
1459 }
1460 break;
1461
1462 case 7:
1463 {
1464
1465 INT2 sign = 1;
1466 sign = copysign(1, cos(pWF->betaRD) );
1467
1468 /* Calculate final spin using the same spin version adopted for XPHM-SpinTaylor */
1469 double afinal_prec = XLALSimIMRPhenomXPrecessingFinalSpin2017(pWF->eta,chi1L,chi2L,pPrec->chiTot_perp);
1470
1471 // Define the PNR final spin to be the above final spin magnitude, with direction given by sign of cos betaRD
1472 pWF->afinal_prec = sign * fabs(afinal_prec);
1473 }
1474 break;
1475
1476 default:
1477 {
1478 XLAL_ERROR(XLAL_EDOM,"Error: XLALSimInspiralWaveformParamsLookupPhenomXPFinalSpinMod version not recognized. Requires PhenomXPFinalSpinMod of 0, 1, 2, 3, or 5.\n");
1479 }
1480 }
1481
1482 /* (PNRUseTunedCoprec) When not generating PNR make NO destinction between afinal and afinal_prec */
1483 if( !PNRUseTunedCoprec ){
1484 pWF->afinal = pWF->afinal_prec;
1485 } else {
1486 /* ELSE, use the non-precessing final spin defined in IMRPhenomXSetWaveformVariables. XCP uses the non-precessing parameters as a base upon which to add precessing deviations. The line below is added only for clarity: pWF->afinal is already equal to pWF->afinal_nonprec as is set in IMRPhenomXSetWaveformVariables */
1487
1488 /* pWF->afinal = pWF->afinal_nonprec */
1489 // ABOVE but commented out, we see what the final spin assignment WOULD BE if NO WINDOWING of coprec tuning were used
1490
1491 pWF->afinal = (pWF->pnr_window)*pWF->afinal_nonprec + (1.0-pWF->pnr_window)*pWF->afinal_prec;
1492 // Above: NOTE that as PNR is turned off outside of its calibration window, we want to turn on use of the precessing final spin as defined in the code section above
1493
1494 #if DEBUG == 1
1495 printf("*** PNR Co-precessing model in use (l=m=2) ***\n\n");
1496 printf("PNR window : %e\n", pWF->pnr_window);
1497 printf("pWF->afinal : %e\n",pWF->afinal);
1498 printf("pWF->afinal_prec : %e\n",pWF->afinal_prec);
1499 printf("pWF->afinal_nonprec : %e\n",pWF->afinal_nonprec);
1500 #endif
1501 }
1502
1503
1504 if( fabs(pWF->afinal) > 1.0 )
1505 {
1506 XLAL_PRINT_WARNING("Warning: Final spin magnitude %g > 1. Setting final spin magnitude = 1.", pWF->afinal);
1507 pWF->afinal = copysign(1.0, pWF->afinal);
1508 }
1509
1510 if( fabs(pWF->afinal_prec) > 1.0 )
1511 {
1512 XLAL_PRINT_WARNING("Warning: Final spin magnitude %g > 1. Setting final spin magnitude = 1.", pWF->afinal_prec);
1513 pWF->afinal_prec = copysign(1.0, pWF->afinal_prec);
1514 }
1515
1516 /* Update ringdown and damping frequency: no precession; to be used for PNR tuned deviations */
1517 pWF->fRING = evaluate_QNMfit_fring22(pWF->afinal) / (pWF->Mfinal);
1518 pWF->fDAMP = evaluate_QNMfit_fdamp22(pWF->afinal) / (pWF->Mfinal);
1519 //pWF->fISCO = XLALSimIMRPhenomXfISCO(pWF->afinal);
1520
1521 #if DEBUG == 1
1522 printf("afinal (prec) : %e\n",pWF->afinal);
1523 printf("fring (prec) : %e\n",pWF->fRING);
1524 printf("fdamp (prec) : %e\n\n",pWF->fDAMP);
1525 #endif
1526
1527 // Copy IMRPhenomXReturnCoPrec to pWF
1529 #if DEBUG == 1
1530 printf("pPrec->IMRPhenomXReturnCoPrec : %i\n",pPrec->IMRPhenomXReturnCoPrec);
1531 printf("pWF->IMRPhenomXReturnCoPrec : %i\n",pWF->IMRPhenomXReturnCoPrec);
1532 #endif
1533
1534 //
1535 if( APPLY_PNR_DEVIATIONS )
1536 {
1537 /* Add an overall deviation to the high-level ringdown frequency (PNRUseTunedCoprec) */
1538 pWF->fRING = pWF->fRING - (pWF->PNR_DEV_PARAMETER * pWF->NU5);
1539 pWF->fDAMP = pWF->fDAMP + (pWF->PNR_DEV_PARAMETER * pWF->NU6);
1540 }
1541
1542 // we want to define the quantities below if PNR is used (PNRUseTunedCoprec). In particular, pWF->fRINGEffShiftDividedByEmm is used by the HMs
1543 // Define identifiers for perturbation theory frequencies
1544 if( PNRUseTunedCoprec && (pWF->PNR_SINGLE_SPIN != 1))
1545 {
1546 const REAL8 fRING22_prec = evaluate_QNMfit_fring22(pWF->afinal_prec) / (pWF->Mfinal);
1547 const REAL8 fRING21_prec = evaluate_QNMfit_fring21(pWF->afinal_prec) / (pWF->Mfinal);
1548 pWF->fRING22_prec = fRING22_prec;
1549
1550 // * Calculate and store single quantity needed to determine effective ringdown frequencies for all QNMs
1551 pWF->fRINGEffShiftDividedByEmm = (1.0-fabs(cos(pWF->betaRD))) * ( fRING22_prec - fRING21_prec );
1552
1553
1554 // As we turn off PNR tuning, we want to turn on use of the effective ringdown frequency
1555 // NOTE that when pWF->pnr_window=0, this should reduce to the def of pWF->fRINGCP below
1556
1557 const INT4 emm = 2;
1558 // NOTE that we use pWF->fRING and not fRING22_prec below because of how pWF->afinal is defined using (1-pWF->pnr_window)
1559 pWF->fRING = pWF->fRING - (1.0-pWF->pnr_window) * emm * pWF->fRINGEffShiftDividedByEmm;
1560 // pWF->fRING = (pWF->pnr_window)*pWF->fRING - (1-pWF->pnr_window) * emm * pWF->fRINGEffShiftDividedByEmm;
1561
1562 #if DEBUG == 1
1563 printf("pflag : %i\n",pflag);
1564 printf("pWF->betaRD : %e\n",pWF->betaRD);
1565 printf("pWF->fRINGEffShiftDividedByEm : %e\n", pWF->fRINGEffShiftDividedByEmm);
1566 printf("fring22 (prec) : %e\n",fRING22_prec);
1567 printf("fRING : %e\n",pWF->fRING);
1568 #endif
1569
1570 }
1571
1572 //
1573 return status;
1574
1575};
1576
1577/** Get alpha and epsilon offset depending of the mprime (second index of the non-precessing mode) */
1578void Get_alphaepsilon_atfref(REAL8 *alpha_offset, REAL8 *epsilon_offset, UINT4 mprime, IMRPhenomXPrecessionStruct *pPrec, IMRPhenomXWaveformStruct *pWF)
1579{
1580 /* Compute the offsets due to the choice of integration constant in alpha and epsilon PN formula */
1581 double omega_ref = pWF->piM * pWF->fRef * 2./mprime;
1582
1583 int pflag = pPrec->IMRPhenomXPrecVersion;
1584
1585 /* Explicitly enumerate MSA flags */
1586 if(pflag == 220 || pflag == 221 || pflag == 222 || pflag == 223 || pflag == 224)
1587 {
1588 const double v = cbrt ( omega_ref );
1589 const vector vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWF,pPrec);
1590
1591 *alpha_offset = vangles.x - pPrec->alpha0;
1592 *epsilon_offset = vangles.y - pPrec->epsilon0;
1593 }
1594
1595 else
1596 {
1597 double logomega_ref = log(omega_ref);
1598 double omega_ref_cbrt = cbrt(omega_ref);
1599 double omega_ref_cbrt2 = omega_ref_cbrt * omega_ref_cbrt;
1600
1601 *alpha_offset = (
1602 pPrec->alpha1 / omega_ref
1603 + pPrec->alpha2 / omega_ref_cbrt2
1604 + pPrec->alpha3 / omega_ref_cbrt
1605 + pPrec->alpha4L * logomega_ref
1606 + pPrec->alpha5 * omega_ref_cbrt - pPrec->alpha0
1607 );
1608
1609 *epsilon_offset = (
1610 pPrec->epsilon1 / omega_ref
1611 + pPrec->epsilon2 / omega_ref_cbrt2
1612 + pPrec->epsilon3 / omega_ref_cbrt
1613 + pPrec->epsilon4L * logomega_ref
1614 + pPrec->epsilon5 * omega_ref_cbrt - pPrec->epsilon0
1615 );
1616 }
1617
1618 return;
1619};
1620
1621
1622/**
1623 This is a convenient wrapper function for PN orbital angular momentum.
1624*/
1626 REAL8 v, /**< Input velocity */
1627 REAL8 LNorm, /**< Orbital angular momentum normalization */
1628 REAL8 L0, /**< Newtonian orbital angular momentum (i.e. LN = 1.0*LNorm) */
1629 REAL8 L1, /**< 0.5PN Orbital angular momentum */
1630 REAL8 L2, /**< 1.0PN Orbital angular momentum */
1631 REAL8 L3, /**< 1.5PN Orbital angular momentum */
1632 REAL8 L4, /**< 2.0PN Orbital angular momentum */
1633 REAL8 L5, /**< 2.5PN Orbital angular momentum */
1634 REAL8 L6, /**< 3.0PN Orbital angular momentum */
1635 REAL8 L7, /**< 3.5PN Orbital angular momentum */
1636 REAL8 L8, /**< 4.0PN Orbital angular momentum */
1637 REAL8 L8L /**< 4.0PN logarithmic orbital angular momentum term */
1638)
1639{
1640 const REAL8 x = v*v;
1641 const REAL8 x2 = x*x;
1642 const REAL8 x3 = x*x2;
1643 const REAL8 x4 = x*x3;
1644 const REAL8 sqx = sqrt(x);
1645
1646 /*
1647 Here LN is the Newtonian pre-factor: LN = \eta / \sqrt{x} :
1648
1649 L = L_N \sum_a L_a x^{a/2}
1650 = L_N [ L0 + L1 x^{1/2} + L2 x^{2/2} + L3 x^{3/2} + ... ]
1651
1652 */
1653 return LNorm * (L0 + L1*sqx + L2*x + L3*(x*sqx) + L4*x2 + L5*(x2*sqx) + L6*x3 + L7*(x3*sqx) + L8*x4 + L8L*x4*log(x));
1654}
1655
1656
1657/**
1658 2PN non-spinning orbital angular momentum as a function of x = v^2 = (Pi M f)^{2/3}
1659
1660 - Bohe et al, 1212.5520v2, Eq 4.7
1661*/
1663{
1664 const REAL8 eta2 = eta*eta;
1665 const REAL8 x = v*v;
1666 const REAL8 x2 = x*x;
1667 const REAL8 sqx = v;
1668
1669 return (eta / sqx) * ( 1.0 + x * (3/2. + eta/6.) + x2 * (27/8. - (19*eta)/8. + eta2/24.) );
1670}
1671
1672/**
1673 3PN orbital angular momentum as a function of x = v^2 = (Pi M f)^{2/3}
1674
1675 Includes linear in spin corrections up to 3.5PN
1676
1677 - Bohe et al, 1212.5520v2, Eq 4.7
1678*/
1679REAL8 XLALSimIMRPhenomXL3PNAS(const REAL8 v, const REAL8 eta, const REAL8 chi1L, const REAL8 chi2L, const REAL8 delta)
1680{
1681 const REAL8 eta2 = eta*eta;
1682
1683 const REAL8 x = v*v;
1684 const REAL8 x2 = x*x;
1685 const REAL8 x3 = x2*x;
1686 const REAL8 sqx = v;
1687
1688 return (eta/sqx) * (
1689 1.0 // Newtonian
1690 + (3/2. + eta/6.) * x // 1PN
1691 + (chi1L*(-5/3. - (5*delta)/3. + (5*eta)/6.) + chi2L*(-5/3. + (5*delta)/3. + (5*eta)/6.)) * x * sqx // 1.5PN
1692 + (27/8. - (19*eta)/8. + eta2/24.) * x2 // 2PN
1693 + ((-7*(chi1L*(72 + delta*(72 - 31*eta) + eta*(-121 + 2*eta)) + chi2L*(72 + eta*(-121 + 2*eta) + delta*(-72 + 31*eta))))/144.) * x2 * sqx // 2.5PN
1694 + ((10935 + eta*(-62001 + eta*(1674 + 7*eta) + 2214*LAL_TWOPI))/1296.) * x3 // 3PN
1695 );
1696}
1697
1698/**
1699 4PN orbital angular momentum as a function of x = v^2 = (Pi M f)^{2/3}
1700
1701 - Bohe et al, 1212.5520v2, Eq 4.7
1702 - Marsat, CQG, 32, 085008, (2015), arXiv:1411.4118
1703 - Siemonsen et al, PRD, 97, 064010, (2018), arXiv:1606.08832
1704*/
1705REAL8 XLALSimIMRPhenomXL4PNAS(const REAL8 v, const REAL8 eta, const REAL8 chi1L, const REAL8 chi2L, const REAL8 delta)
1706{
1707 const REAL8 x = v*v;
1708 const REAL8 x2 = x*x;
1709 const REAL8 x3 = x2*x;
1710 const REAL8 x4 = x3*x;
1711 const REAL8 sqx = sqrt(x);
1712 const REAL8 logx = log(x);
1713
1714 return (eta/sqx) * (
1715 1.0 //Newtonian
1716 + ((9 + eta)/6.) * x // 1PN
1717 + ((5*chi1L*(-2 - 2*delta + eta) + 5*chi2L*(-2 + 2*delta + eta))/6.) * x * sqx // 1.5PN
1718 + ((81 + (-57 + eta)*eta)/24.) * x2 // 2PN
1719 + ((-7*(chi1L*(72 + delta*(72 - 31*eta) + eta*(-121 + 2*eta)) + chi2L*(72 + eta*(-121 + 2*eta) + delta*(-72 + 31*eta))))/144.) * x2 * sqx //2.5PN
1720 + ((10935 + eta*(-62001 + eta*(1674 + 7*eta) + 2214*LAL_TWOPI))/1296.) * x3 //3PN
1721 + ((chi2L*(-324 + eta*(1119 - 2*eta*(172 + eta)) + delta*(324 + eta*(-633 + 14*eta)))
1722 - chi1L*(324 + eta*(-1119 + 2*eta*(172 + eta)) + delta*(324 + eta*(-633 + 14*eta))))/32.) * x3 * sqx //3.5PN
1723 + (2835/128. - (eta*(-10677852 + 100*eta*(-640863 + eta*(774 + 11*eta)) + 26542080*LAL_GAMMA
1724 + 675*(3873 + 3608*eta)*LAL_TWOPI))/622080. - (64*eta*log(16.))/3.) * x4 //4PN
1725 + (-64*eta/3.) * x4 * logx // 4PN log[x] term
1726 );
1727}
1728
1729/**
1730 4PN orbital angular momentum as a function of x = v^2 = (Pi M f)^{2/3}
1731
1732 - Bohe et al, 1212.5520v2, Eq 4.7
1733 - Marsat, CQG, 32, 085008, (2015), arXiv:1411.4118
1734 - Siemonsen et al, PRD, 97, 064010, (2018), arXiv:1606.08832
1735*/
1736REAL8 XLALSimIMRPhenomXL4PNLOSIAS(const REAL8 v, const REAL8 eta, const REAL8 chi1L, const REAL8 chi2L, const REAL8 delta)
1737{
1738 const REAL8 chi1L2 = chi1L * chi1L;
1739 const REAL8 chi2L2 = chi2L * chi2L;
1740
1741 const REAL8 x = v*v;
1742 const REAL8 x2 = x*x;
1743 const REAL8 x3 = x2*x;
1744 const REAL8 x4 = x3*x;
1745 const REAL8 sqx = sqrt(x);
1746 const REAL8 logx = log(x);
1747
1748 return (eta/sqx) * (
1749 1.0 //Newtonian
1750 + ((9 + eta)/6.) * x // 1PN
1751 + ((5*chi1L*(-2 - 2*delta + eta) + 5*chi2L*(-2 + 2*delta + eta))/6.) * x * sqx // 1.5PN
1752 + ((81 + (-57 + eta)*eta)/24.) * x2 // 2PN
1753 + ((-7*(chi1L*(72 + delta*(72 - 31*eta) + eta*(-121 + 2*eta)) + chi2L*(72 + eta*(-121 + 2*eta) + delta*(-72 + 31*eta))))/144.) * x2 * sqx //2.5PN
1754 + ((10935 + eta*(-62001 + eta*(1674 + 7*eta) + 2214*LAL_TWOPI))/1296.) * x3 //3PN
1755 + ((chi2L*(-324 + eta*(1119 - 2*eta*(172 + eta)) + delta*(324 + eta*(-633 + 14*eta)))
1756 - chi1L*(324 + eta*(-1119 + 2*eta*(172 + eta)) + delta*(324 + eta*(-633 + 14*eta))))/32.) * x3 * sqx //3.5PN
1757 + (2835/128. - (eta*(-10677852 + 100*eta*(-640863 + eta*(774 + 11*eta)) + 26542080*LAL_GAMMA
1758 + 675*(3873 + 3608*eta)*LAL_TWOPI))/622080. - (64*eta*log(16.))/3.) * x4 //4PN
1759 + (-64*eta/3.) * x4 * logx // 4PN log[x] term
1760 + ((chi1L2*(1 + delta - 2*eta) + 4*chi1L*chi2L*eta - chi2L2*(-1 + delta + 2*eta))/2.) * x2 // 2PN LOS term, quadratic in spin
1761 + ((3*(chi1L + chi2L)*eta*(chi1L2*(1 + delta - 2*eta) + 4*chi1L*chi2L*eta - chi2L2*(-1 + delta + 2*eta)))/4.) * x3 // 3.5PN LOS term, cubic in spin
1762 );
1763}
1764
1765/**
1766 External wrapper function to next-to-next-to-leading (NNLO) in spin-orbit
1767 expression for the PN Euler angle alpha. This expression is derived by PN
1768 re-expanding and averaging over the orientation of the spin in the orbital plane.
1769
1770 - P Schmidt, 2014, http://orca.cf.ac.uk/64062/
1771 - A Bohé et al, https://dcc.ligo.org/LIGO-T1500602
1772 - Discussion in arXiv:2004.06503 Sec IV A
1773*/
1774/* Wrapper to NNLO PN alpha angle */
1776 const REAL8 f, /**< Geometric frequency */
1777 const REAL8 eta, /**< Symmetric mass rato */
1778 const REAL8 chi1L, /**< Dimensionless aligned spin of larger BH */
1779 const REAL8 chi2L, /**< Dimensionless aligned spin of smaller BH */
1780 const REAL8 chip, /**< Effective precession parameter: Schmidt, Ohme, Hannam, PRD, 91,024043 (2015) */
1781 const REAL8 alpha0 /**< Euler angle at reference Frequency, defines a constant offset */
1782)
1783{
1784 const REAL8 omega = LAL_PI * f;
1785 const REAL8 logomega = log(omega);
1786 const REAL8 omega_cbrt = cbrt(omega);
1787 const REAL8 omega_cbrt2 = omega_cbrt*omega_cbrt;
1788
1789 /* Note that we assume: m1 > m2 and dm = m1 - m2 > 0 */
1790 const REAL8 delta = sqrt(1.0 - 4.0*eta);
1791 const REAL8 delta2 = delta*delta;
1792 const REAL8 delta3 = delta*delta2;
1793
1794 const REAL8 m1 = 0.5*(1.0 + delta);
1795 const REAL8 m1_2 = m1*m1;
1796 const REAL8 m1_3 = m1*m1_2;
1797 const REAL8 m1_4 = m1*m1_3;
1798 const REAL8 m1_5 = m1*m1_4;
1799 const REAL8 m1_6 = m1_3*m1_3;
1800 const REAL8 m1_8 = m1_4*m1_4;
1801
1802 const REAL8 m2 = 0.5*(1.0 - delta);
1803 const REAL8 q = m1/m2;
1804
1805 const REAL8 eta2 = eta*eta;
1806 const REAL8 eta3 = eta*eta2;
1807 const REAL8 eta4 = eta*eta3;
1808 const REAL8 eta5 = eta*eta4;
1809 const REAL8 eta6 = eta*eta5;
1810
1811 const REAL8 chi_eff = m1*chi1L + m2*chi2L;
1812 const REAL8 chiL = (1.0 + q) * chi_eff / q;
1813 const REAL8 chiL2 = chiL*chiL;
1814 const REAL8 chip2 = chip*chip;
1815
1816 const REAL8 alpha1 = (-35/192. - (5*delta)/(64.*m1));
1817 const REAL8 alpha2 = (((-15*chiL*delta*m1)/128. - (35*chiL*m1_2)/128.)/eta);
1818 const REAL8 alpha3 = (-5515/3072. + eta*(-515/384. - (15*delta2)/(256.*m1_2) - (175*delta)/(256.*m1)) - (4555*delta)/(7168.*m1)
1819 + ((-15*chip2*delta*m1_3)/128. - (35*chip2*m1_4)/128.)/eta2);
1820 const REAL8 alpha4 = (((5*chiL*delta2)/16. + (5*chiL*delta*m1)/3. + (2545*chiL*m1_2)/1152. + ((2035*chiL*delta*m1)/21504.
1821 + (2995*chiL*m1_2)/9216.)/eta + ((-5*chiL*chip2*delta*m1_5)/128.
1822 - (35*chiL*chip2*m1_6)/384.)/eta3 - (35*LAL_PI)/48. - (5*delta*LAL_PI)/(16.*m1)));
1823 const REAL8 alpha5 = ((5*(190512*delta3*eta6 + 2268*delta2*eta3*m1*(eta2*(323 + 784*eta) + 336*(25*chiL2 + chip2)*m1_4)
1824 + 7*m1_3*(8024297*eta4 + 857412*eta5 + 3080448*eta6 + 143640*chip2*eta2*m1_4 - 127008*chip2*(-4*chiL2 + chip2)*m1_8
1825 + 6048*eta3*((2632*chiL2 + 115*chip2)*m1_4 - 672*chiL*m1_2*LAL_PI)) + 3*delta*m1_2*(5579177*eta4
1826 - 80136*eta5 + 3845520*eta6 - 146664*chip2*eta2*m1_4 - 127008*chip2*(-4*chiL2 + chip2)*m1_8
1827 + 42336*eta3*((726*chiL2 + 29*chip2)*m1_4 - 96*chiL*m1_2*LAL_PI))))/(6.5028096e7*eta4*m1_3));
1828
1829 return (alpha1/omega + alpha2/omega_cbrt2 + alpha3/omega_cbrt + alpha4*logomega + alpha5*omega_cbrt + alpha0);
1830}
1831
1832
1833/** External wrapper to NNLO PN epsilon angle. See documentation above XLALSimIMRPhenomXPNEuleralphaNNLO. */
1835 REAL8 f, /**< Geometric frequency */
1836 REAL8 eta, /**< Symmetric mass rato */
1837 REAL8 chi1L, /**< Dimensionless aligned spin of larger BH */
1838 REAL8 chi2L, /**< Dimensionless aligned spin of smaller BH */
1839 REAL8 chip, /**< Effective precession parameter: Schmidt, Ohme, Hannam, PRD, 91,024043 (2015) */
1840 REAL8 epsilon0 /**< Euler angle at reference Frequency, defines a constant offset */
1841)
1842{
1843 const REAL8 omega = LAL_PI * f;
1844 const REAL8 logomega = log(omega);
1845 const REAL8 omega_cbrt = cbrt(omega);
1846 const REAL8 omega_cbrt2 = omega_cbrt*omega_cbrt;
1847
1848 /* Note that we assume: m1 > m2 and dm = m1 - m2 > 0 */
1849 const REAL8 delta = sqrt(1.0 - 4.0*eta);
1850 const REAL8 delta2 = delta*delta;
1851 const REAL8 delta3 = delta*delta2;
1852
1853 const REAL8 m1 = 0.5*(1.0 + delta);
1854 const REAL8 m1_2 = m1*m1;
1855 const REAL8 m1_3 = m1*m1_2;
1856 const REAL8 m1_4 = m1*m1_3;
1857 const REAL8 m1_5 = m1*m1_4;
1858 const REAL8 m1_6 = m1_3*m1_3;
1859 const REAL8 m1_8 = m1_4*m1_4;
1860
1861 const REAL8 m2 = 0.5*(1.0 - delta);
1862 const REAL8 q = m1/m2;
1863
1864 const REAL8 eta2 = eta*eta;
1865 const REAL8 eta3 = eta*eta2;
1866 const REAL8 eta4 = eta*eta3;
1867 const REAL8 eta5 = eta*eta4;
1868 const REAL8 eta6 = eta*eta5;
1869
1870 const REAL8 chi_eff = m1*chi1L + m2*chi2L;
1871 const REAL8 chiL = (1.0 + q) * chi_eff / q;
1872 const REAL8 chiL2 = chiL*chiL;
1873 const REAL8 chip2 = chip*chip;
1874
1875 const REAL8 epsilon1 = (-35/192. - (5*delta)/(64.*m1));
1876 const REAL8 epsilon2 = (((-15*chiL*delta*m1)/128. - (35*chiL*m1_2)/128.)/eta);
1877 const REAL8 epsilon3 = (-5515/3072. + eta*(-515/384. - (15*delta2)/(256.*m1_2) - (175*delta)/(256.*m1)) - (4555*delta)/(7168.*m1)
1878 + ((-15*chip2*delta*m1_3)/128. - (35*chip2*m1_4)/128.)/eta2);
1879 const REAL8 epsilon4L = (((5*chiL*delta2)/16. + (5*chiL*delta*m1)/3. + (2545*chiL*m1_2)/1152. + ((2035*chiL*delta*m1)/21504.
1880 + (2995*chiL*m1_2)/9216.)/eta + ((-5*chiL*chip2*delta*m1_5)/128.
1881 - (35*chiL*chip2*m1_6)/384.)/eta3 - (35*LAL_PI)/48. - (5*delta*LAL_PI)/(16.*m1)));
1882 const REAL8 epsilon5 = ((5*(190512*delta3*eta6 + 2268*delta2*eta3*m1*(eta2*(323 + 784*eta) + 336*(25*chiL2 + chip2)*m1_4)
1883 + 7*m1_3*(8024297*eta4 + 857412*eta5 + 3080448*eta6 + 143640*chip2*eta2*m1_4 - 127008*chip2*(-4*chiL2 + chip2)*m1_8
1884 + 6048*eta3*((2632*chiL2 + 115*chip2)*m1_4 - 672*chiL*m1_2*LAL_PI)) + 3*delta*m1_2*(5579177*eta4
1885 - 80136*eta5 + 3845520*eta6 - 146664*chip2*eta2*m1_4 - 127008*chip2*(-4*chiL2 + chip2)*m1_8
1886 + 42336*eta3*((726*chiL2 + 29*chip2)*m1_4 - 96*chiL*m1_2*LAL_PI))))/(6.5028096e7*eta4*m1_3));
1887
1888 return (epsilon1/omega + epsilon2/omega_cbrt2 + epsilon3/omega_cbrt + epsilon4L*logomega + epsilon5*omega_cbrt + epsilon0);
1889
1890}
1891
1892/** Internal function to calculate alpha using pre-cached NNLO PN expressions */
1894 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomX Precession Struct */
1895 const double omega, /**< Orbital frequency */
1896 const double omega_cbrt2, /**< Orbital frequency */
1897 const double omega_cbrt, /**< Cubic root of orbital frequency */
1898 const double logomega /**< Natural logarithm of orbital frequency */
1899)
1900{
1901 double alpha = 0.0;
1902
1903 /* Note that alpha_offset already includes the term alpha0 */
1904 alpha = (
1905 pPrec->alpha1 / omega
1906 + pPrec->alpha2 / omega_cbrt2
1907 + pPrec->alpha3 / omega_cbrt
1908 + pPrec->alpha4L * logomega
1909 + pPrec->alpha5 * omega_cbrt
1910 - pPrec->alpha_offset
1911 );
1912
1913 return alpha;
1914}
1915
1916/** Internal function to calculate epsilon using pre-cached NNLO PN expressions */
1918 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomX Precession Struct */
1919 const double omega, /**< Orbital frequency */
1920 const double omega_cbrt2, /**< Orbital frequency */
1921 const double omega_cbrt, /**< Cubic root of orbital frequency */
1922 const double logomega /**< Natural logarithm of orbital frequency */
1923)
1924{
1925 double epsilon = 0.0;
1926
1927 /* Note that epsilon_offset already includes the term epsilon0 */
1928 epsilon = (
1929 pPrec->epsilon1 / omega
1930 + pPrec->epsilon2 / omega_cbrt2
1931 + pPrec->epsilon3 / omega_cbrt
1932 + pPrec->epsilon4L * logomega
1933 + pPrec->epsilon5 * omega_cbrt
1934 - pPrec->epsilon_offset
1935 );
1936
1937 return epsilon;
1938}
1939
1940/** Core twisting up routine, see Section III A of arXiv:2004.06503 */
1942 const REAL8 Mf, /**< Frequency (Hz) */
1943 const COMPLEX16 hAS, /**< Underlying aligned-spin IMRPhenomXAS strain */
1944 IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
1945 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
1946 COMPLEX16 *hp, /**< [out] h_+ polarization \f$\tilde h_+\f$ */
1947 COMPLEX16 *hc /**< [out] h_x polarization \f$\tilde h_x\f$ */
1948)
1949{
1950 XLAL_CHECK(hp != NULL, XLAL_EFAULT);
1951 XLAL_CHECK(hc != NULL, XLAL_EFAULT);
1952
1953 /* Euler angles */
1954 double alpha = 0.0;
1955 double epsilon = 0.0;
1956
1957 double cBetah = 0.0;
1958 double sBetah = 0.0;
1959
1960 const double omega = LAL_PI * Mf;
1961 const double logomega = log(omega);
1962 const double omega_cbrt = cbrt(omega);
1963 const double omega_cbrt2 = omega_cbrt * omega_cbrt;
1964
1965 const double v = omega_cbrt;
1966
1967 double s, s2, cos_beta;
1968
1970 {
1971 alpha = pPrec->alphaPNR - pPrec->alpha_offset;
1972 epsilon = -1.0 * pPrec->gammaPNR - pPrec->epsilon_offset;
1973 cos_beta = cos(pPrec->betaPNR);
1974 }
1975 else
1976 {
1977 switch(pPrec->IMRPhenomXPrecVersion)
1978 {
1979 /* ~~~~~ Use NNLO PN Euler Angles - Appendix G of arXiv:2004.06503 and https://dcc.ligo.org/LIGO-T1500602 ~~~~~ */
1980 case 101:
1981 case 102:
1982 case 103:
1983 case 104:
1984 {
1985 alpha = IMRPhenomX_PN_Euler_alpha_NNLO(pPrec,omega,omega_cbrt2,omega_cbrt,logomega);
1986 epsilon = IMRPhenomX_PN_Euler_epsilon_NNLO(pPrec,omega,omega_cbrt2,omega_cbrt,logomega);
1987
1988 const REAL8 L = XLALSimIMRPhenomXLPNAnsatz(v, pWF->eta/v, pPrec->L0, pPrec->L1, pPrec->L2, pPrec->L3, pPrec->L4, pPrec->L5, pPrec->L6, pPrec->L7, pPrec->L8, pPrec->L8L);
1989
1990 /*
1991 We ignore the sign of L + SL below:
1992 s := Sp / (L + SL)
1993 */
1994 s = pPrec->Sperp / (L + pPrec->SL);
1995 s2 = s*s;
1996 cos_beta = copysign(1.0, L + pPrec->SL) / sqrt(1.0 + s2);
1997
1998 break;
1999 }
2000 case 220:
2001 case 221:
2002 case 222:
2003 case 223:
2004 case 224:
2005 {
2006 vector vangles = {0.,0.,0.};
2007
2008 /* ~~~~~ Euler Angles from Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967 ~~~~~ */
2009 vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWF,pPrec);
2010
2011 alpha = vangles.x - pPrec->alpha_offset;
2012 epsilon = vangles.y - pPrec->epsilon_offset;
2013 cos_beta = vangles.z;
2014
2015 break;
2016 }
2017 default:
2018 {
2019 XLAL_ERROR(XLAL_EINVAL,"Error: IMRPhenomXPrecessionVersion not recognized. Recommended default is 223.\n");
2020 break;
2021 }
2022 }
2023 }
2024
2025
2026 INT4 status = 0;
2027 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
2028 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
2029
2030 /* Useful powers of the Wigner coefficients */
2031 const REAL8 cBetah2 = cBetah * cBetah;
2032 const REAL8 cBetah3 = cBetah * cBetah2;
2033 const REAL8 cBetah4 = cBetah * cBetah3;
2034 const REAL8 sBetah2 = sBetah * sBetah;
2035 const REAL8 sBetah3 = sBetah * sBetah2;
2036 const REAL8 sBetah4 = sBetah * sBetah3;
2037
2038
2039 /*
2040 Compute the Wigner d coefficients, see Appendix A of arXiv:2004.06503
2041 d22 = Table[WignerD[{2, mp, 2}, 0, -\[Beta], 0], {mp, -2, 2}]
2042 d2m2 = Table[WignerD[{2, mp, -2}, 0, -\[Beta], 0], {mp, -2, 2}]
2043 */
2044 // d22 = {d^2_{-2,2} , d^2_{-1,2}, d^2_{0,2}, d^2_{1,2}, d^2_{2,2} }
2045 const COMPLEX16 d22[5] = {sBetah4, 2.0*cBetah*sBetah3, pPrec->sqrt6*sBetah2*cBetah2, 2.0*cBetah3*sBetah, cBetah4};
2046
2047 // d2m2 = {d^2_{-2,-2} , d^2_{-1,-2}, d^2_{0,-2}, d^2_{1,-2}, d^2_{2,-2} }
2048 const COMPLEX16 d2m2[5] = {d22[4], -d22[3], d22[2], -d22[1], d22[0]}; /* Exploit symmetry d^2_{-2,m} = (-1)^m d^2_{2,-m}*/
2049
2050 const COMPLEX16 Y2mA[5] = {pPrec->Y2m2, pPrec->Y2m1, pPrec->Y20, pPrec->Y21, pPrec->Y22};
2051
2052 /* Precompute powers of e^{i m alpha} */
2053 COMPLEX16 cexp_i_alpha = cexp(+I*alpha);
2054 COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2055 COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2056 COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2057 COMPLEX16 cexp_im_alpha_l2[5] = {cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha};
2058
2059 COMPLEX16 hp_sum = 0;
2060 COMPLEX16 hc_sum = 0;
2061
2062 REAL8 polarizationSymmetry = pPrec->PolarizationSymmetry;
2063
2064 /* Loop over m' modes and perform the actual twisting up */
2065 for(int m=-2; m<=2; m++)
2066 {
2067 /* Transfer functions, see Eq. 3.5 and 3.6 of arXiv:2004.06503 */
2068 COMPLEX16 A2m2emm = cexp_im_alpha_l2[-m+2] * d2m2[m+2] * Y2mA[m+2]; /* = cexp(I*m*alpha) * d22[m+2] * Y2mA[m+2] */
2069 COMPLEX16 A22emmstar = cexp_im_alpha_l2[m+2] * d22[m+2] * conj(Y2mA[m+2]); /* = cexp(-I*m*alpha) * d2m2[m+2] * conj(Y2mA[m+2]) */
2070 hp_sum += A2m2emm + polarizationSymmetry * A22emmstar;
2071 hc_sum += I*(A2m2emm - polarizationSymmetry * A22emmstar);
2072 }
2073
2074 /* Note that \gamma = - \epsilon */
2075 COMPLEX16 eps_phase_hP = cexp(-2.0*I*epsilon) * hAS / 2.0;
2076
2077 /* Return h_+ and h_x */
2078 *hp = eps_phase_hP * hp_sum;
2079 *hc = eps_phase_hP * hc_sum;
2080
2081 /* When debugging, save angles to output file. */
2082 #if DEBUG == 1
2083 FILE *fileangle;
2084 char fileSpec[40];
2085
2086 sprintf(fileSpec, "angles_XP.dat");
2087
2088 fileangle = fopen(fileSpec,"a");
2089
2090 // COMPLEX16 cexp_i_epsilon = cexp(I*epsilon);
2091 // COMPLEX16 cexp_i_betah = cBetah + I*sBetah;
2092 //fprintf(fileangle, "%.16e %.16e %.16e %.16e %.16e %.16e %.16e\n", XLALSimIMRPhenomXUtilsMftoHz(Mf, pWF->Mtot), creal(cexp_i_alpha), cimag(cexp_i_alpha), creal(cexp_i_epsilon), cimag(cexp_i_epsilon), creal(cexp_i_betah), cimag(cexp_i_betah));
2093 fprintf(fileangle, "%.16e %.16e %.16e %.16e\n", XLALSimIMRPhenomXUtilsMftoHz(Mf, pWF->Mtot), alpha, epsilon, cos_beta);
2094 fclose(fileangle);
2095 #endif
2096
2097 return XLAL_SUCCESS;
2098}
2099
2101 REAL8 *cos_beta_half, /**< [out] cos(beta/2) */
2102 REAL8 *sin_beta_half, /**< [out] sin(beta/2) */
2103 const REAL8 cos_beta /**< cos(beta) */
2104)
2105{
2106 /* Note that the results here are indeed always non-negative */
2107 *cos_beta_half = + sqrt( fabs(1.0 + cos_beta) / 2.0 ); /* cos(beta/2) */
2108 *sin_beta_half = + sqrt( fabs(1.0 - cos_beta) / 2.0 ); /* sin(beta/2) */
2109
2110 return XLAL_SUCCESS;
2111}
2112
2114 REAL8 *cos_beta_half, /**< [out] cos(beta/2) */
2115 REAL8 *sin_beta_half, /**< [out] sin(beta/2) */
2116 const REAL8 v, /**< Cubic root of (Pi * Frequency (geometric)) */
2117 IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX waveform struct */
2118 IMRPhenomXPrecessionStruct *pPrec /**< IMRPhenomX precession struct */
2119)
2120{
2121 /* Orbital angular momentum */
2122 const REAL8 L = XLALSimIMRPhenomXLPNAnsatz(v, pWF->eta/v, pPrec->L0, pPrec->L1, pPrec->L2, pPrec->L3, pPrec->L4, pPrec->L5, pPrec->L6, pPrec->L7, pPrec->L8, pPrec->L8L);
2123
2124 /*
2125 We ignore the sign of L + SL below:
2126 s := Sp / (L + SL)
2127 */
2128 const REAL8 s = pPrec->Sperp / (L + pPrec->SL);
2129 const REAL8 s2 = s*s;
2130 const REAL8 cos_beta = copysign(1.0, L + pPrec->SL) / sqrt(1.0 + s2);
2131
2132 *cos_beta_half = + sqrt( fabs(1.0 + cos_beta) / 2.0 ); /* cos(beta/2) */
2133 *sin_beta_half = + sqrt( fabs(1.0 - cos_beta) / 2.0 ); /* sin(beta/2) */
2134
2135 return XLAL_SUCCESS;
2136}
2137
2138/**
2139 Helper function to check if maximum opening angle > pi/2 or pi/4 and issues a warning. See discussion in https://dcc.ligo.org/LIGO-T1500602
2140*/
2142 IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
2143 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
2144 LALDict *lalParams /**< LAL dictionary */
2145)
2146{
2147 const REAL8 eta = pWF->eta;
2148
2149 /* For now, use the 2PN non-spinning maximum opening angle */
2150 const REAL8 v_at_max_beta = sqrt(2.0 / 3.0) * sqrt( (-9.0 - eta + sqrt(1539.0 - 1008.0*eta + 19.0*eta*eta)) / (81 - 57*eta + eta*eta) );
2151
2152 REAL8 cBetah = 0.0;
2153 REAL8 sBetah = 0.0;
2154
2155 INT4 status;
2156 status = IMRPhenomXWignerdCoefficients(&cBetah, &sBetah, v_at_max_beta, pWF, pPrec);
2157 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients failed.");
2158
2159 const REAL8 L_min = XLALSimIMRPhenomXL2PNNS(v_at_max_beta,eta);
2160 const REAL8 max_beta = 2.0 * acos(cBetah);
2161
2162 /*
2163 If L + SL becomes < 0, WignerdCoefficients does not track the angle between J and L.
2164 The model may become pathological as one moves away from the aligned spin limit.
2165
2166 If this does not happen, then max_beta is the actual maximum opening angle as predicted by the model.
2167 */
2168 if ((L_min + pPrec->SL) < 0. && pPrec->chi_p > 0.)
2169 {
2170 XLAL_PRINT_WARNING("The maximum opening angle exceeds Pi/2.\nThe model may be pathological in this regime.");
2171 if((pWF->q > 7.0) && (pPrec->conditionalPrecMBand == 1)){
2172 XLAL_PRINT_WARNING("Multibanding may lead to pathological behaviour in this case. Disabling multibanding.\n");
2174 pPrec->MBandPrecVersion = 0;
2175 }
2176
2177 }
2178 else if (max_beta > LAL_PI_4)
2179 {
2180 XLAL_PRINT_WARNING("The maximum opening angle %g is larger than Pi/4.\nThe model has not been tested against NR in this regime.", max_beta);
2181 }
2182
2183 return XLAL_SUCCESS;
2184}
2185
2186
2187
2188
2189/* ~~~~~~~~~~ Routines to Calculate PN Angles following the MSA approach of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967 ~~~~~~~~~~*/
2190/** Returns the 3PN accurate orbital angular momentum as implemented in LALSimInspiralFDPrecAngles_internals.c */
2191double IMRPhenomX_L_norm_3PN_of_v(const double v, const double v2, const double L_norm, IMRPhenomXPrecessionStruct *pPrec)
2192{
2193 return L_norm*(1. + v2*(pPrec->constants_L[0] + v*pPrec->constants_L[1] + v2*(pPrec->constants_L[2] + v*pPrec->constants_L[3] + v2*(pPrec->constants_L[4]))));
2194}
2195
2196
2197/** Wrapper to generate \f$\phi_z\f$, \f$\zeta\f$ and \f$\cos \theta_L\f$ at a given frequency */
2199 const double v, /**< Velocity */
2200 IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX waveform struct */
2201 IMRPhenomXPrecessionStruct *pPrec /**< IMRPhenomX precession struct */
2202)
2203{
2204 vector vout = {0.,0.,0.};
2205
2206 /* Change code here to determine PN order passed for L */
2207 const double L_norm = pWF->eta / v;
2208 const double J_norm = IMRPhenomX_JNorm_MSA(L_norm,pPrec);
2209
2210 double L_norm3PN = 0.0;
2211
2212 /* Orbital angular momentum at 3PN, coefficients are pre-cached when initializing precession struct */
2213 if(pPrec->IMRPhenomXPrecVersion == 222 || pPrec->IMRPhenomXPrecVersion == 223)
2214 {
2215 L_norm3PN = IMRPhenomX_L_norm_3PN_of_v(v, v*v, L_norm, pPrec);
2216 }
2217 else
2218 {
2219 L_norm3PN = XLALSimIMRPhenomXLPNAnsatz(v, L_norm, pPrec->L0, pPrec->L1, pPrec->L2, pPrec->L3, pPrec->L4, pPrec->L5, pPrec->L6, pPrec->L7, pPrec->L8, pPrec->L8L);
2220 }
2221 const double J_norm3PN = IMRPhenomX_JNorm_MSA(L_norm3PN,pPrec);
2222
2223 /*
2224 Get roots to S^2 equation :
2225 vroots.x = A1 = S_{3}^2
2226 vroots.y = A2 = S_{-}^2
2227 vroots.z = A3 = S_{+}^2
2228 */
2229 const vector vRoots = IMRPhenomX_Return_Roots_MSA(L_norm,J_norm,pPrec);
2230
2231 pPrec->S32 = vRoots.x;
2232 pPrec->Smi2 = vRoots.y;
2233 pPrec->Spl2 = vRoots.z;
2234
2235 pPrec->Spl2mSmi2 = pPrec->Spl2 - pPrec->Smi2;
2236 pPrec->Spl2pSmi2 = pPrec->Spl2 + pPrec->Smi2;
2237 pPrec->Spl = sqrt(pPrec->Spl2);
2238 pPrec->Smi = sqrt(pPrec->Smi2);
2239
2240 const double SNorm = IMRPhenomX_Return_SNorm_MSA(v,pPrec);
2241 pPrec->S_norm = SNorm;
2242 pPrec->S_norm_2 = SNorm * SNorm;
2243
2244 vector vMSA = {0.,0.,0.};
2245 if(fabs(pPrec->Smi2 - pPrec->Spl2) > 1.e-5)
2246 {
2247 /* Get phiz_0_MSA and zeta_0_MSA */
2248 vMSA = IMRPhenomX_Return_MSA_Corrections_MSA(v, L_norm, J_norm, pPrec);
2249 }
2250
2251 const double phiz_MSA = vMSA.x;
2252 const double zeta_MSA = vMSA.y;
2253
2254 const double phiz = IMRPhenomX_Return_phiz_MSA(v,J_norm,pPrec);
2255 const double zeta = IMRPhenomX_Return_zeta_MSA(v,pPrec);
2256 double cos_theta_L = IMRPhenomX_costhetaLJ(L_norm3PN,J_norm3PN,SNorm);
2257
2258 vout.x = phiz + phiz_MSA;
2259 vout.y = zeta + zeta_MSA;
2260 vout.z = cos_theta_L;
2261
2262 return vout;
2263}
2264
2265/** This function initializes all the core variables required for the MSA system. This will be called first. */
2267{
2268 /*
2269 Sanity check on the precession version
2270 */
2271 int pflag = pPrec->IMRPhenomXPrecVersion;
2272 if(pflag != 220 && pflag != 221 && pflag != 222 && pflag != 223 && pflag != 224)
2273 {
2274 XLAL_ERROR(XLAL_EINVAL,"Error: MSA system requires IMRPhenomXPrecVersion 220, 221, 222, 223 or 224.\n");
2275 }
2276
2277 /*
2278 First initialize the system of variables needed for Chatziioannou et al, PRD, 88, 063011, (2013), arXiv:1307.4418:
2279 - Racine et al, PRD, 80, 044010, (2009), arXiv:0812.4413
2280 - Favata, PRD, 80, 024002, (2009), arXiv:0812.0069
2281 - Blanchet et al, PRD, 84, 064041, (2011), arXiv:1104.5659
2282 - Bohe et al, CQG, 30, 135009, (2013), arXiv:1303.7412
2283 */
2284 const double eta = pPrec->eta;
2285 const double eta2 = pPrec->eta2;
2286 const double eta3 = pPrec->eta3;
2287 const double eta4 = pPrec->eta4;
2288
2289 const double m1 = pWF->m1;
2290 const double m2 = pWF->m2;
2291
2292 /* PN Coefficients for d \omega / d t as per LALSimInspiralFDPrecAngles_internals.c */
2293 const double domegadt_constants_NS[17] = {96./5.,-1486./35.,-264./5.,384.*LAL_PI/5.,34103./945.,13661./105.,944./15.,LAL_PI*(-4159./35.),LAL_PI*(-2268./5.),(16447322263./7276500. + LAL_PI*LAL_PI*512./5. - LAL_LN2*109568./175. -LAL_GAMMA*54784./175.),(-56198689./11340. + LAL_PI*LAL_PI*902./5.),1623./140.,-1121./27.,-54784./525.,-LAL_PI*883./42.,LAL_PI*71735./63.,LAL_PI*73196./63.};
2294 const double domegadt_constants_SO[18] = {-904./5.,-120.,-62638./105.,4636./5.,-6472./35.,3372./5.,-LAL_PI*720.,-LAL_PI*2416./5.,-208520./63.,796069./105.,-100019./45.,-1195759./945.,514046./105.,-8709./5.,-LAL_PI*307708./105.,LAL_PI*44011./7.,-LAL_PI*7992./7.,LAL_PI*151449./35.};
2295 const double domegadt_constants_SS[4] = {-494./5.,-1442./5.,-233./5.,-719./5.};
2296
2297 const double L_csts_nonspin[9] = {3./2.,1./6.,27./8.,-19./8.,1./24.,135./16.,-6889/144.+ 41./24.*LAL_PI*LAL_PI,31./24.,7./1296.};
2298 const double L_csts_spinorbit[6] = {-14./6.,-3./2.,-11./2.,133./72.,-33./8.,7./4.};
2299
2300
2301 /*
2302 Note that Chatziioannou et al use q = m2/m1, where m1 > m2 and therefore q < 1
2303 IMRPhenomX assumes m1 > m2 and q > 1. For the internal MSA code, flip q and
2304 dump this to pPrec->qq, where qq explicitly dentoes that this is 0 < q < 1.
2305 */
2306 const double q = m2 / m1; // m2 / m1, q < 1, m1 > m2
2307 const double invq = 1.0 / q; // m2 / m1, q < 1, m1 > m2
2308 pPrec->qq = q;
2309 pPrec->invqq = invq;
2310
2311 const double mu = (m1 * m2) / (m1 + m2);
2312
2313 #if DEBUG == 1
2314 printf("m1 = %.6f\n\n",pWF->m1);
2315 printf("m2 = %.6f\n\n",pWF->m2);
2316 printf("q (<1) = %.6f\n\n",pPrec->qq);
2317 #endif
2318
2319 /* \delta and powers of \delta in terms of q < 1, should just be m1 - m2 */
2320 pPrec->delta_qq = (1.0 - pPrec->qq) / (1.0 + pPrec->qq);
2321 pPrec->delta2_qq = pPrec->delta_qq * pPrec->delta_qq;
2322 pPrec->delta3_qq = pPrec->delta_qq * pPrec->delta2_qq;
2323 pPrec->delta4_qq = pPrec->delta_qq * pPrec->delta3_qq;
2324
2325 /* Initialize empty vectors */
2326 vector S1v = {0.,0.,0.};
2327 vector S2v = {0.,0.,0.};
2328
2329 /* Define source frame such that \hat{L} = {0,0,1} with L_z pointing along \hat{z}. */
2330 vector Lhat = {0.,0.,1.};
2331
2332 /* Set LHat variables - these are fixed. */
2333 pPrec->Lhat_cos_theta = 1.0; /* Cosine of Polar angle of orbital angular momentum */
2334 pPrec->Lhat_phi = 0.0; /* Azimuthal angle of orbital angular momentum */
2335 pPrec->Lhat_theta = 0.0; /* Polar angle of orbital angular momentum */
2336
2337 /* Dimensionful spin vectors, note eta = m1 * m2 and q = m2/m1 */
2338 S1v.x = pPrec->chi1x * eta/q; /* eta / q = m1^2 */
2339 S1v.y = pPrec->chi1y * eta/q;
2340 S1v.z = pPrec->chi1z * eta/q;
2341
2342 S2v.x = pPrec->chi2x * eta*q; /* eta * q = m2^2 */
2343 S2v.y = pPrec->chi2y * eta*q;
2344 S2v.z = pPrec->chi2z * eta*q;
2345
2346 REAL8 S1_0_norm = IMRPhenomX_vector_L2_norm(S1v);
2347 REAL8 S2_0_norm = IMRPhenomX_vector_L2_norm(S2v);
2348
2349 /* Initial dimensionful spin vectors at reference frequency */
2350 /* S1 = {S1x,S1y,S1z} */
2351 pPrec->S1_0.x = S1v.x;
2352 pPrec->S1_0.y = S1v.y;
2353 pPrec->S1_0.z = S1v.z;
2354
2355 /* S2 = {S2x,S2y,S2z} */
2356 pPrec->S2_0.x = S2v.x;
2357 pPrec->S2_0.y = S2v.y;
2358 pPrec->S2_0.z = S2v.z;
2359
2360 /* Reference velocity v and v^2 */
2361 pPrec->v_0 = cbrt( pPrec->piGM * pWF->fRef );
2362 pPrec->v_0_2 = pPrec->v_0 * pPrec->v_0;
2363
2364 /* Reference orbital angular momenta */
2365 vector L_0 = {0.,0.,0.};
2366 L_0 = IMRPhenomX_vector_scalar(Lhat,pPrec->eta / pPrec->v_0);
2367 pPrec->L_0 = L_0;
2368
2369 #if DEBUG == 1
2370 printf("v_0 = %.6f\n\n",pPrec->v_0);
2371
2372 printf("chi1x = %.6f\n",pPrec->chi1x);
2373 printf("chi1y = %.6f\n",pPrec->chi1y);
2374 printf("chi1z = %.6f\n\n",pPrec->chi1z);
2375
2376 printf("chi2x = %.6f\n",pPrec->chi2x);
2377 printf("chi2y = %.6f\n",pPrec->chi2y);
2378 printf("chi2z = %.6f\n\n",pPrec->chi2z);
2379
2380 printf("S1_0.x = %.6f\n",pPrec->S1_0.x);
2381 printf("S1_0.y = %.6f\n",pPrec->S1_0.y);
2382 printf("S1_0.z = %.6f\n",pPrec->S1_0.z);
2383 printf("S1_0 = %.6f\n\n",S1_0_norm);
2384
2385 printf("S2_0.x = %.6f\n",pPrec->S2_0.x);
2386 printf("S2_0.y = %.6f\n",pPrec->S2_0.y);
2387 printf("S2_0.z = %.6f\n",pPrec->S2_0.z);
2388 printf("S2_0 = %.6f\n\n",S2_0_norm);
2389 #endif
2390
2391 /* Inner products used in MSA system */
2392 double dotS1L, dotS2L, dotS1Ln, dotS2Ln, dotS1S2;
2393
2394 dotS1L = IMRPhenomX_vector_dot_product(S1v,Lhat);
2395 dotS2L = IMRPhenomX_vector_dot_product(S2v,Lhat);
2396 dotS1S2 = IMRPhenomX_vector_dot_product(S1v,S2v);
2397 dotS1Ln = dotS1L / S1_0_norm;
2398 dotS2Ln = dotS2L / S2_0_norm;
2399
2400 /* Add dot products to struct */
2401 pPrec->dotS1L = dotS1L;
2402 pPrec->dotS2L = dotS2L;
2403 pPrec->dotS1S2 = dotS1S2;
2404 pPrec->dotS1Ln = dotS1Ln;
2405 pPrec->dotS2Ln = dotS2Ln;
2406
2407 #if DEBUG == 1
2408 printf("Lhat_0.x = %.6f\n",Lhat.x);
2409 printf("Lhat_0.y = %.6f\n",Lhat.y);
2410 printf("Lhat_0.z = %.6f\n\n",Lhat.z);
2411
2412 printf("dotS1L = %.6f\n",pPrec->dotS1L);
2413 printf("dotS2L = %.6f\n",pPrec->dotS2L);
2414 printf("dotS1Ln = %.6f\n",pPrec->dotS1Ln);
2415 printf("dotS2Ln = %.6f\n",pPrec->dotS2Ln);
2416 printf("dotS1S2 = %.6f\n\n",pPrec->dotS1S2);
2417 #endif
2418
2419 /* Coeffcients for PN orbital angular momentum at 3PN, as per LALSimInspiralFDPrecAngles_internals.c */
2420 pPrec->constants_L[0] = (L_csts_nonspin[0] + eta*L_csts_nonspin[1]);
2421 pPrec->constants_L[1] = IMRPhenomX_Get_PN_beta(L_csts_spinorbit[0], L_csts_spinorbit[1], pPrec);
2422 pPrec->constants_L[2] = (L_csts_nonspin[2] + eta*L_csts_nonspin[3] + eta*eta*L_csts_nonspin[4]);
2423 pPrec->constants_L[3] = IMRPhenomX_Get_PN_beta((L_csts_spinorbit[2]+L_csts_spinorbit[3]*eta), (L_csts_spinorbit[4]+L_csts_spinorbit[5]*eta), pPrec);
2424 pPrec->constants_L[4] = (L_csts_nonspin[5]+L_csts_nonspin[6]*eta +L_csts_nonspin[7]*eta*eta+L_csts_nonspin[8]*eta*eta*eta);
2425
2426 /* Effective total spin */
2427 const double Seff = (1.0 + q) * pPrec->dotS1L + (1 + (1.0/q))*pPrec->dotS2L;
2428 const double Seff2 = Seff * Seff;
2429
2430 pPrec->Seff = Seff;
2431 pPrec->Seff2 = Seff2;
2432
2433 #if DEBUG == 1
2434 printf("Seff = %.6f\n\n",pPrec->Seff);
2435 #endif
2436
2437 /* Initial total spin, S = S1 + S2 */
2438 vector S0 = {0.,0.,0.};
2439 S0 = IMRPhenomX_vector_sum(S1v,S2v);
2440
2441 /* Cache total spin in the precession struct */
2442 pPrec->S_0 = S0;
2443
2444 #if DEBUG == 1
2445 printf("S_0_x = %.6f\n",pPrec->S_0.x);
2446 printf("S_0_y = %.6f\n",pPrec->S_0.y);
2447 printf("S_0_z = %.6f\n\n",pPrec->S_0.z);
2448 #endif
2449
2450 /* Initial total angular momentum, J = L + S1 + S2 */
2451 pPrec->J_0 = IMRPhenomX_vector_sum(pPrec->L_0,pPrec->S_0);
2452
2453 #if DEBUG == 1
2454 printf("J_0_x = %.6f\n",pPrec->J_0.x);
2455 printf("J_0_y = %.6f\n",pPrec->J_0.y);
2456 printf("J_0_z = %.6f\n\n",pPrec->J_0.z);
2457 #endif
2458
2459 /* Norm of total initial spin */
2461 pPrec->S_0_norm_2 = pPrec->S_0_norm * pPrec->S_0_norm;
2462
2463 /* Norm of orbital and total angular momenta */
2464 pPrec->L_0_norm = IMRPhenomX_vector_L2_norm(pPrec->L_0);
2465 pPrec->J_0_norm = IMRPhenomX_vector_L2_norm(pPrec->J_0);
2466
2467 const double L0norm = pPrec->L_0_norm;
2468 const double J0norm = pPrec->J_0_norm;
2469
2470 #if DEBUG == 1
2471 printf("L_0_norm = %.6f\n",pPrec->L_0_norm);
2472 printf("J_0_norm = %.6f\n\n",pPrec->J_0_norm);
2473 #endif
2474
2475 /* Useful powers */
2476 pPrec->S_0_norm_2 = pPrec->S_0_norm * pPrec->S_0_norm;
2477 pPrec->J_0_norm_2 = pPrec->J_0_norm * pPrec->J_0_norm;
2478 pPrec->L_0_norm_2 = pPrec->L_0_norm * pPrec->L_0_norm;
2479
2480 /* Vector for obtaining B, C, D coefficients */
2481 UNUSED vector vBCD;
2483
2484 #if DEBUG == 1
2485 printf("B = %.6f\n",vBCD.x);
2486 printf("C = %.6f\n",vBCD.y);
2487 printf("D = %.6f\n\n",vBCD.z);
2488 #endif
2489
2490 /*
2491 Get roots to S^2 equation : S^2_+, S^2_-, S^2_3
2492 vroots.x = A1 = S_{3}^2
2493 vroots.y = A2 = S_{-}^2
2494 vroots.z = A3 = S_{+}^2
2495 */
2496 vector vRoots = {0.,0.,0.};
2497
2498 vRoots = IMRPhenomX_Return_Roots_MSA(pPrec->L_0_norm,pPrec->J_0_norm,pPrec);
2499
2500 // Set roots
2501 pPrec->Spl2 = vRoots.z;
2502 pPrec->Smi2 = vRoots.y;
2503 pPrec->S32 = vRoots.x;
2504
2505 // S^2_+ + S^2_-
2506 pPrec->Spl2pSmi2 = pPrec->Spl2 + pPrec->Smi2;
2507
2508 // S^2_+ - S^2_-
2509 pPrec->Spl2mSmi2 = pPrec->Spl2 - pPrec->Smi2;
2510
2511 // S_+ and S_-
2512 pPrec->Spl = sqrt(pPrec->Spl2);
2513 pPrec->Smi = sqrt(pPrec->Smi2);
2514
2515 /* Eq. 45 of PRD 95, 104004, (2017), arXiv:1703.03967, set from initial conditions */
2516 pPrec->SAv2 = 0.5 * ( pPrec->Spl2pSmi2 );
2517 pPrec->SAv = sqrt(pPrec->SAv2);
2518 pPrec->invSAv2 = 1.0 / pPrec->SAv2;
2519 pPrec->invSAv = 1.0 / pPrec->SAv;
2520
2521 #if DEBUG == 1
2522 printf("From vRoots... \n");
2523 printf("Spl2 = %.6f\n",pPrec->Spl2);
2524 printf("Smi2 = %.6f\n",pPrec->Smi2);
2525 printf("S32 = %.6f\n",pPrec->S32);
2526 printf("SAv2 = %.6f\n",pPrec->SAv2);
2527 printf("SAv = %.6f\n\n",pPrec->SAv);
2528 #endif
2529
2530 /* c_1 is determined by Eq. 41 of PRD, 95, 104004, (2017), arXiv:1703.03967 */
2531 const double c_1 = 0.5 * (J0norm*J0norm - L0norm*L0norm - pPrec->SAv2) / pPrec->L_0_norm * eta;
2532 const double c1_2 = c_1 * c_1;
2533
2534 /* Useful powers and combinations of c_1 */
2535 pPrec->c1 = c_1;
2536 pPrec->c12 = c_1 * c_1;
2537 pPrec->c1_over_eta = c_1 / eta;
2538
2539 /* Average spin couplings over one precession cycle: A9 - A14 of arXiv:1703.03967 */
2540 const double omqsq = (1.0 - q) * (1.0 - q) + 1e-16;
2541 const double omq2 = (1.0 - q * q) + 1e-16;
2542
2543 /* Precession averaged spin couplings, Eq. A9 - A14 of arXiv:1703.03967, note that we only use the initial values */
2544 pPrec->S1L_pav = (c_1 * (1.0 + q) - q * eta * Seff) / (eta * omq2);
2545 pPrec->S2L_pav = - q * (c_1 * (1.0 + q) - eta * Seff) / (eta * omq2);
2546 pPrec->S1S2_pav = 0.5 * pPrec->SAv2 - 0.5*(pPrec->S1_norm_2 + pPrec->S2_norm_2);
2547 pPrec->S1Lsq_pav = (pPrec->S1L_pav*pPrec->S1L_pav) + ((pPrec->Spl2mSmi2)*(pPrec->Spl2mSmi2) * pPrec->v_0_2) / (32.0 * eta2 * omqsq);
2548 pPrec->S2Lsq_pav = (pPrec->S2L_pav*pPrec->S2L_pav) + (q*q*(pPrec->Spl2mSmi2)*(pPrec->Spl2mSmi2) * pPrec->v_0_2) / (32.0 * eta2 * omqsq);
2549 pPrec->S1LS2L_pav = pPrec->S1L_pav*pPrec->S2L_pav - q * (pPrec->Spl2mSmi2)*(pPrec->Spl2mSmi2)*pPrec->v_0_2 / (32.0 * eta2 * omqsq);
2550
2551 /* Spin couplings in arXiv:1703.03967 */
2552 pPrec->beta3 = ( (113./12.) + (25./4.)*(m2/m1) )*pPrec->S1L_pav + ( (113./12.) + (25./4.)*(m1/m2) )*pPrec->S2L_pav;
2553
2554 pPrec->beta5 = ( ( (31319./1008.) - (1159./24.)*eta) + (m2/m1)*((809./84) - (281./8.)*eta) )*pPrec->S1L_pav
2555 + ( ( (31319./1008.) - (1159./24.)*eta) + (m1/m2)*((809./84) - (281./8.)*eta) )*pPrec->S2L_pav;
2556
2557 pPrec->beta6 = LAL_PI * ( ( (75./2.) + (151./6.)*(m2/m1))*pPrec->S1L_pav + ( (75./2.) + (151./6.)*(m1/m2))*pPrec->S2L_pav );
2558
2559 pPrec->beta7 = (
2560 ( (130325./756) - (796069./2016)*eta + (100019./864.)*eta2 ) + (m2/m1)*( (1195759./18144) - (257023./1008.)*eta + (2903/32.)*eta2 ) * pPrec->S1L_pav
2561 + ( (130325./756) - (796069./2016)*eta + (100019./864.)*eta2 ) + (m1/m2)*( (1195759./18144) - (257023./1008.)*eta + (2903/32.)*eta2 ) * pPrec->S2L_pav
2562 );
2563
2564 pPrec->sigma4 = (1.0/mu) * ( (247./48.)*pPrec->S1S2_pav - (721./48.)*pPrec->S1L_pav*pPrec->S2L_pav )
2565 + (1.0/(m1*m1)) * ( (233./96.)*pPrec->S1_norm_2 - (719./96.)*pPrec->S1Lsq_pav )
2566 + (1.0/(m2*m2)) * ( (233./96.)*pPrec->S2_norm_2 - (719./96.)*pPrec->S2Lsq_pav );
2567
2568 /* Compute PN coefficients using precession-averaged spin couplings */
2569 pPrec->a0 = 96.0 * eta / 5.0;
2570
2571 /* These are all normalized by a factor of a0 */
2572 pPrec->a2 = -(743./336.) - (11.0/4.)*eta;
2573 pPrec->a3 = 4.0 * LAL_PI - pPrec->beta3;
2574 pPrec->a4 = (34103./18144.) + (13661./2016.)*eta + (59./18.)*eta2 - pPrec->sigma4;
2575 pPrec->a5 = -(4159./672.)*LAL_PI - (189./8.)*LAL_PI*eta - pPrec->beta5;
2576 pPrec->a6 = (16447322263./139708800.) + (16./3.)*LAL_PI*LAL_PI - (856./105)*log(16.) - (1712./105.)*LAL_GAMMA - pPrec->beta6
2577 + eta*( (451./48)*LAL_PI*LAL_PI - (56198689./217728.) ) + eta2*(541./896.) - eta3*(5605./2592.);
2578 pPrec->a7 = -(4415./4032.)*LAL_PI + (358675./6048.)*LAL_PI*eta + (91495./1512.)*LAL_PI*eta2 - pPrec->beta7;
2579
2580 // Coefficients are weighted by an additional factor of a_0
2581 pPrec->a2 *= pPrec->a0;
2582 pPrec->a3 *= pPrec->a0;
2583 pPrec->a4 *= pPrec->a0;
2584 pPrec->a5 *= pPrec->a0;
2585 pPrec->a6 *= pPrec->a0;
2586 pPrec->a7 *= pPrec->a0;
2587
2588 #if DEBUG == 1
2589 printf("a0 = %.6f\n",pPrec->a0);
2590 printf("a2 = %.6f\n",pPrec->a2);
2591 printf("a3 = %.6f\n",pPrec->a3);
2592 printf("a4 = %.6f\n",pPrec->a4);
2593 printf("a5 = %.6f\n\n",pPrec->a5);
2594 #endif
2595
2596 /* For versions 222 and 223, we compute PN coefficients using initial spin couplings, as per LALSimInspiralFDPrecAngles_internals.c */
2597 if(pflag == 222 || pflag == 223)
2598 {
2599 pPrec->a0 = eta*domegadt_constants_NS[0];
2600 pPrec->a2 = eta*(domegadt_constants_NS[1] + eta*(domegadt_constants_NS[2]));
2601 pPrec->a3 = eta*(domegadt_constants_NS[3] + IMRPhenomX_Get_PN_beta(domegadt_constants_SO[0], domegadt_constants_SO[1], pPrec));
2602 pPrec->a4 = eta*(domegadt_constants_NS[4] + eta*(domegadt_constants_NS[5] + eta*(domegadt_constants_NS[6])) + IMRPhenomX_Get_PN_sigma(domegadt_constants_SS[0], domegadt_constants_SS[1], pPrec) + IMRPhenomX_Get_PN_tau(domegadt_constants_SS[2], domegadt_constants_SS[3], pPrec));
2603 pPrec->a5 = eta*(domegadt_constants_NS[7] + eta*(domegadt_constants_NS[8]) + IMRPhenomX_Get_PN_beta((domegadt_constants_SO[2] + eta*(domegadt_constants_SO[3])), (domegadt_constants_SO[4] + eta*(domegadt_constants_SO[5])), pPrec));
2604 }
2605
2606 /* Debugging */
2607 #if DEBUG == 1
2608 printf("Using list of coefficients... \n");
2609 printf("a0 = %.6f\n",pPrec->a0);
2610 printf("a2 = %.6f\n",pPrec->a2);
2611 printf("a3 = %.6f\n",pPrec->a3);
2612 printf("a4 = %.6f\n",pPrec->a4);
2613 printf("a5 = %.6f\n\n",pPrec->a5);
2614 #endif
2615
2616 /* Useful powers of a_0 */
2617 pPrec->a0_2 = pPrec->a0*pPrec->a0;
2618 pPrec->a0_3 = pPrec->a0_2*pPrec->a0;
2619 pPrec->a2_2 = pPrec->a2*pPrec->a2;
2620
2621 /*
2622 Calculate g coefficients as in Appendix A of Chatziioannou et al, PRD, 95, 104004, (2017), arXiv:1703.03967.
2623 These constants are used in TaylorT2 where domega/dt is expressed as an inverse polynomial
2624 */
2625 pPrec->g0 = 1. / pPrec->a0;
2626
2627 // Eq. A2 (1703.03967)
2628 pPrec->g2 = -(pPrec->a2 / pPrec->a0_2);
2629
2630 // Eq. A3 (1703.03967)
2631 pPrec->g3 = -(pPrec->a3/pPrec->a0_2);
2632
2633 // Eq.A4 (1703.03967)
2634 pPrec->g4 = -(pPrec->a4*pPrec->a0 - pPrec->a2_2) / pPrec->a0_3;
2635
2636 // Eq. A5 (1703.03967)
2637 pPrec->g5 = -(pPrec->a5*pPrec->a0 - 2.0*pPrec->a3*pPrec->a2) / pPrec->a0_3;
2638
2639 #if DEBUG == 1
2640 printf("g0 = %.6f\n",pPrec->g0);
2641 printf("g2 = %.6f\n",pPrec->g2);
2642 printf("g3 = %.6f\n",pPrec->g3);
2643 printf("g4 = %.6f\n",pPrec->g4);
2644 printf("g5 = %.6f\n\n",pPrec->g5);
2645 #endif
2646
2647 // Useful powers of delta
2648 const double delta = pPrec->delta_qq;
2649 const double delta2 = delta * delta;
2650 const double delta3 = delta * delta2;
2651 const double delta4 = delta * delta3;
2652
2653 // These are the phase coefficients of Eq. 51 of PRD, 95, 104004, (2017), arXiv:1703.03967
2654 pPrec->psi0 = 0.0;
2655 pPrec->psi1 = 0.0;
2656 pPrec->psi2 = 0.0;
2657
2658 /* \psi_1 is defined in Eq. C1 of Appendix C in PRD, 95, 104004, (2017), arXiv:1703.03967 */
2659 pPrec->psi1 = 3.0 * (2.0 * eta2 * Seff - c_1) / (eta * delta2);
2660
2661 double c_1_over_nu = pPrec->c1_over_eta;
2662 double c_1_over_nu_2 = c_1_over_nu * c_1_over_nu;
2663 double one_p_q_sq = (1.+q) * (1.+q);
2664 double Seff_2 = Seff * Seff;
2665 double q_2 = q * q;
2666 double one_m_q_sq = (1.-q)*(1.-q);
2667 double one_m_q2_2 = (1. - q_2) * (1. - q_2);
2668 double one_m_q_4 = one_m_q_sq * one_m_q_sq;
2669
2670 double term1, term2, term3, term4, term5, term6, term7, term8;
2671
2672 /* This implements the Delta term as in LALSimInspiralFDPrecAngles.c
2673 c.f. https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiralFDPrecAngles_internals.c#L145
2674 */
2675 if(pflag == 222 || pflag == 223)
2676 {
2677 const double Del1 = 4. * c_1_over_nu_2 * one_p_q_sq;
2678 const double Del2 = 8. * c_1_over_nu * q * (1. + q) * Seff;
2679 const double Del3 = 4. * (one_m_q2_2 * pPrec->S1_norm_2 - q_2 * Seff_2);
2680 const double Del4 = 4. * c_1_over_nu_2 * q_2 * one_p_q_sq;
2681 const double Del5 = 8. * c_1_over_nu * q_2 * (1. + q) * Seff;
2682 const double Del6 = 4. * (one_m_q2_2 * pPrec->S2_norm_2 - q_2 * Seff_2);
2683 pPrec->Delta = sqrt( fabs( (Del1 - Del2 - Del3) * (Del4 - Del5 - Del6) ));
2684 }
2685 else
2686 {
2687 /* Coefficients of \Delta as defined in Eq. C3 of Appendix C in PRD, 95, 104004, (2017), arXiv:1703.03967. */
2688 term1 = c1_2 * eta / (q * delta4);
2689 term2 = -2.0 * c_1 * eta3 * (1.0 + q) * Seff / (q * delta4);
2690 term3 = -eta2 * (delta2 * pPrec->S1_norm_2 - eta2 * Seff2) / delta4;
2691 /*
2692 Is this 1) (c1_2 * q * eta / delta4) or 2) c1_2*eta2/delta4?
2693
2694 - In paper.pdf, the expression 1) is used.
2695
2696 Using eta^2 leads to higher frequency oscillations, use q * eta
2697 */
2698 term4 = c1_2 * eta * q / delta4;
2699 term5 = -2.0*c_1*eta3*(1.0 + q)*Seff / delta4;
2700 term6 = -eta2 * (delta2*pPrec->S2_norm_2 - eta2*Seff2) / delta4;
2701
2702 /* \Delta as in Eq. C3 of Appendix C in PRD, 95, 104004, (2017) */
2703 pPrec->Delta = sqrt( fabs( (term1 + term2 + term3) * (term4 + term5 + term6) ) );
2704 }
2705
2706 /* This implements the Delta term as in LALSimInspiralFDPrecAngles.c
2707 c.f. https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiralFDPrecAngles_internals.c#L160
2708 */
2709 if(pflag == 222 || pflag == 223)
2710 {
2711 const double u1 = 3. * pPrec->g2 / pPrec->g0;
2712 const double u2 = 0.75 * one_p_q_sq / one_m_q_4;
2713 const double u3 = -20. * c_1_over_nu_2 * q_2 * one_p_q_sq;
2714 const double u4 = 2. * one_m_q2_2 * (q * (2. + q) * pPrec->S1_norm_2 + (1. + 2. * q) * pPrec->S2_norm_2 - 2. * q * pPrec->SAv2);
2715 const double u5 = 2. * q_2 * (7. + 6. * q + 7. * q_2) * 2. * c_1_over_nu * Seff;
2716 const double u6 = 2. * q_2 * (3. + 4. * q + 3. * q_2) * Seff_2;
2717 const double u7 = q * pPrec->Delta;
2718
2719 /* Eq. C2 (1703.03967) */
2720 pPrec->psi2 = u1 + u2*(u3 + u4 + u5 - u6 + u7);
2721 }
2722 else
2723 {
2724 /* \psi_2 is defined in Eq. C2 of Appendix C in PRD, 95, 104004, (2017). Here we implement system of equations as in paper.pdf */
2725 term1 = 3.0 * pPrec->g2 / pPrec->g0;
2726
2727 /* q^2 or no q^2 in term2? Consensus on retaining q^2 term: https://git.ligo.org/waveforms/reviews/phenompv3hm/issues/7 */
2728 term2 = 3.0 * q * q / (2.0 * eta3);
2729 term3 = 2.0 * pPrec->Delta;
2730 term4 = -2.0*eta2*pPrec->SAv2 / delta2;
2731 term5 = -10.*eta*c1_2 / delta4;
2732 term6 = 2.0 * eta2 * (7.0 + 6.0*q + 7.0*q*q) * c_1 * Seff / (omqsq * delta2);
2733 term7 = -eta3 * (3.0 + 4.0*q + 3.0*q*q) * Seff2 / (omqsq * delta2);
2734 term8 = eta * (q * (2.0+q)*pPrec->S1_norm_2 + (1.0 + 2.0*q)*pPrec->S2_norm_2) / ( omqsq );
2735
2736 /* \psi_2, C2 of Appendix C of PRD, 95, 104004, (2017) */
2737 pPrec->psi2 = term1 + term2 * (term3 + term4 + term5 + term6 + term7 + term8);
2738 }
2739
2740 #if DEBUG == 1
2741 printf("psi1 = %.6f\n",pPrec->psi1);
2742 printf("psi2 = %.6f\n\n",pPrec->psi2);
2743 #endif
2744
2745 /* Eq. D1 of PRD, 95, 104004, (2017), arXiv:1703.03967 */
2746 const double Rm = pPrec->Spl2 - pPrec->Smi2;
2747 const double Rm_2 = Rm * Rm;
2748
2749 /* Eq. D2 and D3 Appendix D of PRD, 95, 104004, (2017), arXiv:1703.03967 */
2750 const double cp = pPrec->Spl2 * eta2 - c1_2;
2751 double cm = pPrec->Smi2 * eta2 - c1_2;
2752
2753 /*
2754 Check if cm goes negative, this is likely pathological. If so, set MSA_ERROR to 1, so that waveform generator can handle
2755 the error approriately
2756 */
2757 // if(cm < 0.0)
2758 // {
2759 // pPrec->MSA_ERROR = 1;
2760 // XLAL_PRINT_ERROR("Error, coefficient cm = %.16f, which is negative and likely to be pathological. Triggering MSA failure.\n",cm);
2761 // }
2762
2763 /* fabs is here to help enforce positive definite cpcm */
2764 const double cpcm = fabs( cp * cm );
2765 const double sqrt_cpcm = sqrt(cpcm);
2766
2767 /* Eq. D4 in PRD, 95, 104004, (2017), arXiv:1703.03967 ; Note difference to published version. */
2768 const double a1dD = 0.5 + 0.75/eta;
2769
2770 /* Eq. D5 in PRD, 95, 104004, (2017), arXiv:1703.03967 */
2771 const double a2dD = -0.75*Seff/eta;
2772
2773 /* Eq. E3 in PRD, 95, 104004, (2017), arXiv:1703.03967 ; Note that this is Rm * D2 */
2774 const double D2RmSq = (cp - sqrt_cpcm) / eta2 ;
2775
2776 /* Eq. E4 in PRD, 95, 104004, (2017), arXiv:1703.03967 ; Note that this is Rm^2 * D4 */
2777 const double D4RmSq = -0.5*Rm*sqrt_cpcm/eta2 - cp/eta4*(sqrt_cpcm - cp);
2778
2779 const double S0m = pPrec->S1_norm_2 - pPrec->S2_norm_2;
2780
2781 /* Difference of spin norms squared, as used in Eq. D6 of PRD, 95, 104004, (2017), arXiv:1703.03967 */
2782 double aw = (-3.*(1. + q)/q*(2.*(1. + q)*eta2*Seff*c_1 - (1. + q)*c1_2 + (1. - q)*eta2*S0m));
2783 double cw = 3./32./eta*Rm_2;
2784 double dw = 4.0*cp - 4.0*D2RmSq*eta2;
2785 double hw = -2.0*(2.0*D2RmSq - Rm)*c_1;
2786 double fw = Rm*D2RmSq - D4RmSq - 0.25*Rm_2;
2787
2788 const double adD = aw / dw;
2789 const double hdD = hw / dw;
2790 const double cdD = cw / dw;
2791 const double fdD = fw / dw;
2792
2793 const double gw = 3./16./eta2/eta*Rm_2*(c_1 - eta2*Seff);
2794 const double gdD = gw / dw;
2795
2796 /* Useful powers of the coefficients */
2797 const double hdD_2 = hdD * hdD;
2798 const double adDfdD = adD * fdD;
2799 const double adDfdDhdD = adDfdD * hdD;
2800 const double adDhdD_2 = adD * hdD_2;
2801
2802 #if DEBUG == 1
2803 printf("\na1dD = %.6f\n",a1dD);
2804 printf("a2dD = %.6f\n",a2dD);
2805 printf("adD = %.6f\n",adD);
2806 printf("cdD = %.6f\n",cdD);
2807 printf("hdD = %.6f\n",hdD);
2808 printf("fdD = %.6f\n",fdD);
2809 printf("Rm = %.6f\n",Rm);
2810 printf("Delta = %.6f\n",pPrec->Delta);
2811 printf("sqrt_cpcm = %.6f\n",sqrt_cpcm);
2812 printf("c1 = %.6f\n",pPrec->c1);
2813 printf("gdD = %.6f\n\n",gdD);
2814 #endif
2815
2816 // Eq. D10 in PRD, 95, 104004, (2017), arXiv:1703.03967
2817 pPrec->Omegaz0 = a1dD + adD;
2818
2819 // Eq. D11 in PRD, 95, 104004, (2017), arXiv:1703.03967
2820 pPrec->Omegaz1 = a2dD - adD*Seff - adD*hdD;
2821
2822 // Eq. D12 in PRD, 95, 104004, (2017), arXiv:1703.03967
2823 pPrec->Omegaz2 = adD*hdD*Seff + cdD - adD*fdD + adD*hdD_2;
2824
2825 // Eq. D13 in PRD, 95, 104004, (2017), arXiv:1703.03967
2826 pPrec->Omegaz3 = (adDfdD - cdD - adDhdD_2)*(Seff + hdD) + adDfdDhdD;
2827
2828 // Eq. D14 in PRD, 95, 104004, (2017), arXiv:1703.03967
2829 pPrec->Omegaz4 = (cdD + adDhdD_2 - 2.0*adDfdD)*(hdD*Seff + hdD_2 - fdD) - adD*fdD*fdD;
2830
2831 // Eq. D15 in PRD, 95, 104004, (2017), arXiv:1703.03967
2832 pPrec->Omegaz5 = (cdD - adDfdD + adDhdD_2) * fdD * (Seff + 2.0*hdD) - (cdD + adDhdD_2 - 2.0*adDfdD) * hdD_2 * (Seff + hdD) - adDfdD*fdD*hdD;
2833
2834 #if DEBUG == 1
2835 printf("Omegaz0 = %.6f\n",pPrec->Omegaz0);
2836 printf("Omegaz1 = %.6f\n",pPrec->Omegaz1);
2837 printf("Omegaz2 = %.6f\n",pPrec->Omegaz2);
2838 printf("Omegaz3 = %.6f\n",pPrec->Omegaz3);
2839 printf("Omegaz4 = %.6f\n",pPrec->Omegaz4);
2840 printf("Omegaz5 = %.6f\n\n",pPrec->Omegaz5);
2841 #endif
2842
2843 /*
2844 If Omegaz5 > 1000, this is larger than we expect and the system may be pathological.
2845 - Set MSA_ERROR = 1 to trigger an error
2846 */
2847 if(fabs(pPrec->Omegaz5) > 1000.0)
2848 {
2849 pPrec->MSA_ERROR = 1;
2850 XLAL_PRINT_WARNING("Warning, |Omegaz5| = %.16f, which is larger than expected and may be pathological. Triggering MSA failure.\n",pPrec->Omegaz5);
2851 }
2852
2853 const double g0 = pPrec->g0;
2854
2855 /* Coefficients of Eq. 65, as defined in Equations D16 - D21 of PRD, 95, 104004, (2017), arXiv:1703.03967 */
2856 pPrec->Omegaz0_coeff = 3.0 * g0 * pPrec->Omegaz0;
2857 pPrec->Omegaz1_coeff = 3.0 * g0 * pPrec->Omegaz1;
2858 pPrec->Omegaz2_coeff = 3.0 * (g0 * pPrec->Omegaz2 + pPrec->g2*pPrec->Omegaz0);
2859 pPrec->Omegaz3_coeff = 3.0 * (g0 * pPrec->Omegaz3 + pPrec->g2*pPrec->Omegaz1 + pPrec->g3*pPrec->Omegaz0);
2860 pPrec->Omegaz4_coeff = 3.0 * (g0 * pPrec->Omegaz4 + pPrec->g2*pPrec->Omegaz2 + pPrec->g3*pPrec->Omegaz1 + pPrec->g4*pPrec->Omegaz0);
2861 pPrec->Omegaz5_coeff = 3.0 * (g0 * pPrec->Omegaz5 + pPrec->g2*pPrec->Omegaz3 + pPrec->g3*pPrec->Omegaz2 + pPrec->g4*pPrec->Omegaz1 + pPrec->g5*pPrec->Omegaz0);
2862
2863 /* Coefficients of zeta: in Appendix E of PRD, 95, 104004, (2017), arXiv:1703.03967 */
2864 const double c1oveta2 = c_1 / eta2;
2865 pPrec->Omegazeta0 = pPrec->Omegaz0;
2866 pPrec->Omegazeta1 = pPrec->Omegaz1 + pPrec->Omegaz0 * c1oveta2;
2867 pPrec->Omegazeta2 = pPrec->Omegaz2 + pPrec->Omegaz1 * c1oveta2;
2868 pPrec->Omegazeta3 = pPrec->Omegaz3 + pPrec->Omegaz2 * c1oveta2 + gdD;
2869 pPrec->Omegazeta4 = pPrec->Omegaz4 + pPrec->Omegaz3 * c1oveta2 - gdD*Seff - gdD*hdD;
2870 pPrec->Omegazeta5 = pPrec->Omegaz5 + pPrec->Omegaz4 * c1oveta2 + gdD*hdD*Seff + gdD*(hdD_2 - fdD);
2871
2872 #if DEBUG == 1
2873 printf("Omegazeta0 = %.6f\n",pPrec->Omegazeta0);
2874 printf("Omegazeta1 = %.6f\n",pPrec->Omegazeta1);
2875 printf("Omegazeta2 = %.6f\n",pPrec->Omegazeta2);
2876 printf("Omegazeta3 = %.6f\n",pPrec->Omegazeta3);
2877 printf("Omegazeta4 = %.6f\n",pPrec->Omegazeta4);
2878 printf("Omegazeta5 = %.6f\n\n",pPrec->Omegazeta5);
2879 #endif
2880
2881 pPrec->Omegazeta0_coeff = -pPrec->g0 * pPrec->Omegazeta0;
2882 pPrec->Omegazeta1_coeff = -1.5 * pPrec->g0 * pPrec->Omegazeta1;
2883 pPrec->Omegazeta2_coeff = -3.0*(pPrec->g0 * pPrec->Omegazeta2 + pPrec->g2*pPrec->Omegazeta0);
2884 pPrec->Omegazeta3_coeff = 3.0*(pPrec->g0 * pPrec->Omegazeta3 + pPrec->g2*pPrec->Omegazeta1 + pPrec->g3*pPrec->Omegazeta0);
2885 pPrec->Omegazeta4_coeff = 3.0*(pPrec->g0 * pPrec->Omegazeta4 + pPrec->g2*pPrec->Omegazeta2 + pPrec->g3*pPrec->Omegazeta1 + pPrec->g4*pPrec->Omegazeta0);
2886 pPrec->Omegazeta5_coeff = 1.5*(pPrec->g0*pPrec->Omegazeta5 + pPrec->g2*pPrec->Omegazeta3 + pPrec->g3*pPrec->Omegazeta2 + pPrec->g4*pPrec->Omegazeta1 + pPrec->g5*pPrec->Omegazeta0);
2887
2888 /* Expansion order of corrections to retain */
2889 switch(ExpansionOrder)
2890 {
2891 /* Generate all orders */
2892 case -1:
2893 {
2894 break;
2895 }
2896 case 1:
2897 {
2898 pPrec->Omegaz1_coeff = 0.0;
2899 pPrec->Omegazeta1_coeff = 0.0;
2900 #if __GNUC__ >= 7 && !defined __INTEL_COMPILER
2901 __attribute__ ((fallthrough));
2902 #endif
2903
2904 }
2905 case 2:
2906 {
2907 pPrec->Omegaz2_coeff = 0.0;
2908 pPrec->Omegazeta2_coeff = 0.0;
2909 #if __GNUC__ >= 7 && !defined __INTEL_COMPILER
2910 __attribute__ ((fallthrough));
2911 #endif
2912
2913 }
2914 case 3:
2915 {
2916 pPrec->Omegaz3_coeff = 0.0;
2917 pPrec->Omegazeta3_coeff = 0.0;
2918 #if __GNUC__ >= 7 && !defined __INTEL_COMPILER
2919 __attribute__ ((fallthrough));
2920 #endif
2921
2922 }
2923 case 4:
2924 {
2925 pPrec->Omegaz4_coeff = 0.0;
2926 pPrec->Omegazeta4_coeff = 0.0;
2927 #if __GNUC__ >= 7 && !defined __INTEL_COMPILER
2928 __attribute__ ((fallthrough));
2929 #endif
2930
2931 }
2932 case 5:
2933 {
2934 pPrec->Omegaz5_coeff = 0.0;
2935 pPrec->Omegazeta5_coeff = 0.0;
2936 break;
2937 }
2938 default:
2939 {
2940 XLAL_ERROR(XLAL_EDOM, "Expansion order for MSA corrections = %i not recognized. Default is 5. Allowed values are: [-1,1,2,3,4,5].",ExpansionOrder);
2941 }
2942 }
2943
2944 #if DEBUG == 1
2945 printf("Omegaz0_coeff = %.6f\n",pPrec->Omegaz0_coeff);
2946 printf("Omegaz1_coeff = %.6f\n",pPrec->Omegaz1_coeff);
2947 printf("Omegaz2_coeff = %.6f\n",pPrec->Omegaz2_coeff);
2948 printf("Omegaz3_coeff = %.6f\n",pPrec->Omegaz3_coeff);
2949 printf("Omegaz4_coeff = %.6f\n",pPrec->Omegaz4_coeff);
2950 printf("Omegaz5_coeff = %.6f\n\n",pPrec->Omegaz5_coeff);
2951
2952 printf("Omegazeta0_coeff = %.6f\n",pPrec->Omegazeta0_coeff);
2953 printf("Omegazeta1_coeff = %.6f\n",pPrec->Omegazeta1_coeff);
2954 printf("Omegazeta2_coeff = %.6f\n",pPrec->Omegazeta2_coeff);
2955 printf("Omegazeta3_coeff = %.6f\n",pPrec->Omegazeta3_coeff);
2956 printf("Omegazeta4_coeff = %.6f\n",pPrec->Omegazeta4_coeff);
2957 printf("Omegazeta5_coeff = %.6f\n\n",pPrec->Omegazeta5_coeff);
2958 #endif
2959
2960 /* Get psi0 term */
2961 double psi_of_v0 = 0.0;
2962 double mm = 0.0;
2963 double tmpB = 0.0;
2964 double volume_element = 0.0;
2965 double vol_sign = 0.0;
2966
2967 #if DEBUG == 1
2968 printf("psi1 = %.6f\n",pPrec->psi1);
2969 printf("psi2 = %.6f\n\n",pPrec->psi2);
2970 printf("S_0_norm = %.6f\n\n",pPrec->S_0_norm);
2971 #endif
2972
2973 /* Tolerance chosen to be consistent with implementation in LALSimInspiralFDPrecAngles */
2974 if( fabs(pPrec->Smi2 - pPrec->Spl2) < 1.0e-5)
2975 {
2976 pPrec->psi0 = 0.0;
2977 }
2978 else
2979 {
2980 mm = sqrt( (pPrec->Smi2 - pPrec->Spl2) / (pPrec->S32 - pPrec->Spl2) );
2981 tmpB = (pPrec->S_0_norm*pPrec->S_0_norm - pPrec->Spl2) / (pPrec->Smi2 - pPrec->Spl2);
2982
2983 volume_element = IMRPhenomX_vector_dot_product( IMRPhenomX_vector_cross_product(L_0,S1v), S2v);
2984 vol_sign = (volume_element > 0) - (volume_element < 0);
2985
2986 psi_of_v0 = IMRPhenomX_psiofv(pPrec->v_0, pPrec->v_0_2, 0.0, pPrec->psi1, pPrec->psi2, pPrec);
2987
2988 if( tmpB < 0. || tmpB > 1. )
2989 {
2990 if(tmpB > 1.0 && (tmpB - 1.) < 0.00001)
2991 {
2992 pPrec->psi0 = gsl_sf_ellint_F(asin(vol_sign*sqrt(1.)) , mm, GSL_PREC_DOUBLE ) - psi_of_v0;
2993 }
2994 if(tmpB < 0.0 && tmpB > -0.00001)
2995 {
2996 pPrec->psi0 = gsl_sf_ellint_F(asin(vol_sign*sqrt(0.)), mm, GSL_PREC_DOUBLE ) - psi_of_v0;
2997 }
2998 }
2999 else
3000 {
3001 pPrec->psi0 = gsl_sf_ellint_F(asin( vol_sign * sqrt(tmpB) ), mm, GSL_PREC_DOUBLE) - psi_of_v0;
3002 }
3003 }
3004
3005 #if DEBUG == 1
3006 printf("psi0_of_v0 = %.6f\n",psi_of_v0);
3007 printf("tmpB = %.6f\n",tmpB);
3008 printf("psi0 = %.6f\n\n",pPrec->psi0);
3009 #endif
3010
3011 vector vMSA = {0.,0.,0.};
3012
3013 double phiz_0 = 0.0;
3014 UNUSED double phiz_0_MSA = 0.0;
3015
3016 double zeta_0 = 0.0;
3017 UNUSED double zeta_0_MSA = 0.0;
3018
3019 /* Tolerance chosen to be consistent with implementation in LALSimInspiralFDPrecAngles */
3020 if( fabs(pPrec->Spl2 - pPrec->Smi2) > 1.e-5 )
3021 {
3022 vMSA = IMRPhenomX_Return_MSA_Corrections_MSA(pPrec->v_0,pPrec->L_0_norm,pPrec->J_0_norm,pPrec);
3023
3024 phiz_0_MSA = vMSA.x;
3025 zeta_0_MSA = vMSA.y;
3026 }
3027
3028 // Initial \phi_z
3029 pPrec->phiz_0 = 0.0;
3030 phiz_0 = IMRPhenomX_Return_phiz_MSA(pPrec->v_0,pPrec->J_0_norm,pPrec);
3031
3032 // Initial \zeta
3033 pPrec->zeta_0 = 0.0;
3034 zeta_0 = IMRPhenomX_Return_zeta_MSA(pPrec->v_0,pPrec);
3035
3036 pPrec->phiz_0 = - phiz_0 - vMSA.x;
3037 pPrec->zeta_0 = - zeta_0 - vMSA.y;
3038
3039 #if DEBUG == 1
3040 printf("v_0 = %.6f\n",pPrec->v_0);
3041 printf("c1 = %.6f\n\n",pPrec->c1);
3042
3043 printf("eta = %.6f\n",pPrec->eta);
3044 printf("eta2 = %.6f\n",pPrec->eta2);
3045 printf("eta3 = %.6f\n",pPrec->eta3);
3046 printf("eta4 = %.6f\n",pPrec->eta4);
3047 printf("ieta = %.6f\n",pPrec->inveta);
3048 printf("ieta2 = %.6f\n",pPrec->inveta2);
3049 printf("ieta3 = %.6f\n\n",pPrec->inveta3);
3050
3051 printf("SAv = %.6f\n",pPrec->SAv);
3052 printf("SAv2 = %.6f\n\n",pPrec->SAv2);
3053 printf("invSAv2 = %.6f\n\n",pPrec->invSAv2);
3054 printf("invSAv = %.6f\n\n",pPrec->invSAv);
3055
3056 printf("J_0_norm = %.6f\n",pPrec->J_0_norm);
3057 printf("L_0_norm = %.6f\n\n",pPrec->L_0_norm);
3058
3059 printf("phiz_0 = %.6f\n",pPrec->phiz_0);
3060 printf("zeta_0 = %.6f\n",pPrec->zeta_0);
3061 printf("phiz_0_MSA = %.6f\n",phiz_0_MSA);
3062 printf("zeta_0_MSA = %.6f\n\n",zeta_0_MSA);
3063 #endif
3064
3065 return XLAL_SUCCESS;
3066}
3067
3068double IMRPhenomX_psiofv(const double v, const double v2, const double psi0, const double psi1, const double psi2, const IMRPhenomXPrecessionStruct *pPrec)
3069{
3070 /* Equation 51 in arXiv:1703.03967 */
3071 return ( psi0 - 0.75*pPrec->g0 * pPrec->delta_qq * (1.0 + psi1*v + psi2*v2) / (v2*v) );
3072}
3073
3074/**
3075 Here we solve for the roots of Eq 21 in Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967:
3076 - Roots for (d S^2)/(d t^2) = -A^2 (S^2 -S+^2)(S^2 - S-^2)(S^2 - S3^2)
3077 - Returns Spl2 (S+^2), Smi2 (S-^2) and S3^2
3078
3079 - Note: agrees with independent implementation in Mathematica
3080*/
3081vector IMRPhenomX_Return_Roots_MSA(double LNorm, double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
3082{
3083 vector vout;
3084
3085 double tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
3086 tmp1 = 0.0;
3087 tmp2 = 0.0;
3088 tmp3 = 0.0;
3089 tmp4 = 0.0;
3090 tmp5 = 0.0;
3091 tmp6 = 0.0;
3092
3093 vector vBCD;
3094 vBCD = IMRPhenomX_Return_Spin_Evolution_Coefficients_MSA(LNorm, JNorm, pPrec);
3095
3096 /* Update struct. Note, that this agreed with independent implementation in Mathematica. */
3097 const double B = vBCD.x;
3098 const double C = vBCD.y;
3099 const double D = vBCD.z;
3100
3101 const double S1Norm2 = pPrec->S1_norm_2;
3102 const double S2Norm2 = pPrec->S2_norm_2;
3103
3104 const double S0Norm2 = pPrec->S_0_norm_2;
3105
3106 const double B2 = B * B;
3107 const double B3 = B2 * B;
3108 const double BC = B * C;
3109
3110 const double p = C - B2 / 3;
3111 const double qc = (2.0/27.0)*B3 - BC/3.0 + D;
3112
3113 const double sqrtarg = sqrt(-p/3.0);
3114 double acosarg = 1.5 * qc/p/sqrtarg;
3115
3116 // Make sure that acosarg is appropriately bounded
3117 if(acosarg < -1)
3118 {
3119 acosarg = -1;
3120 }
3121 if(acosarg > 1)
3122 {
3123 acosarg = +1;
3124 }
3125 const double theta = acos(acosarg) / 3.0;
3126 const double cos_theta = cos(theta);
3127
3128 const double dotS1Ln = pPrec->dotS1Ln;
3129 const double dotS2Ln = pPrec->dotS2Ln;
3130
3131 double S32, Spl2, Smi2;
3132
3133 // tmp1 = S32
3134 // tmp2 = Smi2
3135 // tmp3 = Spl2
3136 if(theta != theta || sqrtarg!=sqrtarg || dotS1Ln == 1 || dotS2Ln == 1 || dotS1Ln == -1 || dotS2Ln == -1 || S1Norm2 == 0
3137 || S2Norm2 == 0)
3138 {
3139 S32 = 0.0;
3140
3141 Smi2 = S0Norm2;
3142
3143 /*
3144 Add a numerical perturbation to prevent azimuthal precession angle
3145 from diverging.
3146 */
3147
3148 // Smi2 = S02^2 + epsilon perturbation
3149 Spl2 = Smi2 + 1e-9;
3150 }
3151 else
3152 {
3153 /* E.g. see discussion on elliptic functions in arXiv:0711.4064 */
3154 tmp1 = 2.0*sqrtarg*cos(theta - 2.0*LAL_TWOPI/3.0) - B/3.0;
3155 tmp2 = 2.0*sqrtarg*cos(theta - LAL_TWOPI/3.0) - B/3.0;
3156 tmp3 = 2.0*sqrtarg*cos_theta - B/3.0;
3157
3158 tmp4 = fmax(fmax(tmp1,tmp2),tmp3);
3159 tmp5 = fmin(fmin(tmp1,tmp2),tmp3);
3160
3161 // As tmp1 and tmp3 are set by findind the min and max, find the remaining root
3162 if( (tmp4 - tmp3) > 0.0 && (tmp5 - tmp3) < 0.0)
3163 {
3164 tmp6 = tmp3;
3165 }
3166 else if( (tmp4 - tmp1) > 0.0 && (tmp5 - tmp1) < 0.0)
3167 {
3168 tmp6 = tmp1;
3169 }
3170 else
3171 {
3172 tmp6 = tmp2;
3173 }
3174
3175 /*
3176 When Spl2 ~ 0 to numerical roundoff then Smi2 can sometimes be ~ negative causing NaN's.
3177 This occurs in a very limited portion of the parameter space where spins are ~ 0 to numerical roundoff.
3178 We can circumvent by enforcing +ve definite behaviour when tmp4 ~ 0. Note that S32 can often be negative, this is fine.
3179 */
3180 tmp4 = fabs(tmp4);
3181 tmp6 = fabs(tmp6);
3182
3183 // Return the roots
3184 Spl2 = tmp4;
3185 S32 = tmp5;
3186 Smi2 = tmp6;
3187 }
3188
3189 vout.x = S32;
3190 vout.y = Smi2;
3191 vout.z = Spl2;
3192
3193 return vout;
3194}
3195
3196/**
3197 Get norm of J using Eq 41 of Chatziioannou et al, PRD 95, 104004, (2017)
3198*/
3199double IMRPhenomX_JNorm_MSA(const double LNorm, IMRPhenomXPrecessionStruct *pPrec)
3200{
3201 const double JNorm2 = (LNorm*LNorm + (2.0 * LNorm * pPrec->c1_over_eta) + pPrec->SAv2);
3202 return sqrt(JNorm2);
3203}
3204
3205/**
3206 Get norm of S, see PRD 95, 104004, (2017)
3207*/
3209{
3210 /*
3211 sn, cn are Jacobi elliptic functions
3212 psi is the phase and m a parameter entering the Jacobi elliptic functions
3213 */
3214 double sn, cn, dn, m, psi;
3215 double v2 = v*v;
3216
3217 /*
3218 If spin norms ~ cancel then we do not need to evaluate the Jacobi elliptic function.
3219 Check tolerance?
3220 */
3221 if( fabs(pPrec->Smi2 - pPrec->Spl2) < 1.0e-5 )
3222 {
3223 sn = 0.0;
3224 }
3225 else
3226 {
3227 /* Equation 25 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967 */
3228 m = (pPrec->Smi2 - pPrec->Spl2) / (pPrec->S32 - pPrec->Spl2);
3229
3230 psi = IMRPhenomX_psiofv(v, v2, pPrec->psi0, pPrec->psi1, pPrec->psi2, pPrec);
3231
3232 /* Evaluate the Jacobi ellptic functions */
3233 gsl_sf_elljac_e(psi, m, &sn, &cn, &dn);
3234 }
3235
3236 /* Equation 23 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967 */
3237 const double SNorm2 = pPrec->Spl2 + (pPrec->Smi2 - pPrec->Spl2)*sn*sn;
3238
3239 return sqrt(SNorm2);
3240}
3241
3242
3243/**
3244 Get coefficients for Eq 21 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3245*/
3247{
3248 vector vout;
3249
3250 // Total angular momenta: J = L + S1 + S2
3251 const double JNorm2 = JNorm * JNorm;
3252
3253 // Orbital angular momenta
3254 const double LNorm2 = LNorm * LNorm;
3255
3256 // Dimensionfull spin angular momenta
3257 const double S1Norm2 = pPrec->S1_norm_2;
3258 const double S2Norm2 = pPrec->S2_norm_2;
3259
3260 const double q = pPrec->qq;
3261 const double eta = pPrec->eta;
3262
3263 const double J2mL2 = (JNorm2 - LNorm2);
3264 const double J2mL2Sq = J2mL2 * J2mL2;
3265
3266 const double delta = pPrec->delta_qq;
3267 const double deltaSq = delta*delta;
3268
3269
3270 /*
3271 Note:
3272 S_{eff} \equiv \xi = (1 + q)(S1.L) + (1 + 1/q)(S2.L)
3273 */
3274 const double Seff = pPrec->Seff;
3275
3276 // Note that we do not evaluate Eq. B1 here as it is v dependent whereas B, C and D are not
3277
3278 // Set Eq. B2, B_coeff
3279 vout.x = (LNorm2 + S1Norm2)*q + 2.0*LNorm*Seff - 2.0*JNorm2 -
3280 S1Norm2 - S2Norm2 + (LNorm2 + S2Norm2)/q;
3281
3282 // Set Eq. B3, C_coeff
3283 vout.y = J2mL2Sq - 2.0*LNorm*Seff*J2mL2 - 2.0*((1.0 - q)/q)*LNorm2*(S1Norm2 - q*S2Norm2) +
3284 4.0*eta*LNorm2*Seff*Seff - 2.0*delta*(S1Norm2 - S2Norm2)*Seff*LNorm +
3285 2.0*((1.0 - q)/q)*(q*S1Norm2 - S2Norm2)*JNorm2;
3286
3287 // Set Eq. B4, D_coeff
3288 vout.z = ((1.0 - q)/q)*(S2Norm2 - q*S1Norm2)*J2mL2Sq
3289 + deltaSq*(S1Norm2 - S2Norm2)*(S1Norm2 - S2Norm2)*LNorm2/eta
3290 + 2.0*delta*LNorm*Seff*(S1Norm2 - S2Norm2)*J2mL2;
3291
3292
3293
3294
3295 return vout;
3296}
3297
3298
3299
3300/**
3301 Get c constants from Appendix B (B6, B7, B8) of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3302*/
3303vector IMRPhenomX_Return_Constants_c_MSA(const double v, const double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
3304{
3305 const double v2 = v*v;
3306 const double v3 = v*v2;
3307 const double v4 = v2*v2;
3308 const double v6 = v3*v3;
3309
3310 const double JNorm2 = JNorm * JNorm;
3311
3312 vector vout = {0.,0.,0.};
3313
3314 const double Seff = pPrec->Seff;
3315
3316 if(pPrec->IMRPhenomXPrecVersion != 220)
3317 {
3318 // Equation B6 of Chatziioannou et al, PRD 95, 104004, (2017)
3319 vout.x = JNorm * ( 0.75*(1.0 - Seff*v) * v2 * (pPrec->eta3 + 4.0*pPrec->eta3*Seff*v
3320 - 2.0*pPrec->eta*(JNorm2 - pPrec->Spl2 + 2.0*(pPrec->S1_norm_2 - pPrec->S2_norm_2)*pPrec->delta_qq)*v2
3321 - 4.0*pPrec->eta*Seff*(JNorm2 - pPrec->Spl2)*v3 + (JNorm2 - pPrec->Spl2)*(JNorm2 - pPrec->Spl2)*v4*pPrec->inveta) );
3322
3323 // Equation B7 of Chatziioannou et al, PRD 95, 104004, (2017)
3324 vout.y = JNorm * ( -1.5 * pPrec->eta * (pPrec->Spl2 - pPrec->Smi2)*(1.0 + 2.0*Seff*v - (JNorm2 - pPrec->Spl2)*v2*pPrec->inveta2) * (1.0 - Seff*v)*v4 );
3325
3326 // Equation B8 of Chatziioannou et al, PRD 95, 104004, (2017)
3327 vout.z = JNorm * ( 0.75 * pPrec->inveta * (pPrec->Spl2 - pPrec->Smi2)*(pPrec->Spl2 - pPrec->Smi2)*(1.0 - Seff * v)*v6 );
3328 }
3329 else
3330 {
3331 /* This is as implemented in LALSimInspiralFDPrecAngles, should be equivalent to above code.
3332 c.f. https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiralFDPrecAngles_internals.c#L578
3333 */
3334 double v_2 = v * v;
3335 double v_3 = v * v_2;
3336 double v_4 = v_2 * v_2;
3337 double v_6 = v_2 * v_4;
3338 double J_norm = JNorm;
3339 double delta = pPrec->delta_qq;
3340 double eta = pPrec->eta;
3341 double eta_2 = eta * eta;
3342
3343 vout.x = -0.75*((JNorm2-pPrec->Spl2)*(JNorm2-pPrec->Spl2)*v_4/(pPrec->eta) - 4.*(pPrec->eta)*(pPrec->Seff)*(JNorm2-pPrec->Spl2)*v_3-2.*(JNorm2-pPrec->Spl2+2*((pPrec->S1_norm_2)-(pPrec->S2_norm_2))*(delta))*(pPrec->eta)*v_2+(4.*(pPrec->Seff)*v+1)*(pPrec->eta)*(eta_2)) *J_norm*v_2*((pPrec->Seff)*v-1.);
3344 vout.y = 1.5*(pPrec->Smi2-pPrec->Spl2)*J_norm*((JNorm2-pPrec->Spl2)/(pPrec->eta)*v_2-2.*(pPrec->eta)*(pPrec->Seff)*v-(pPrec->eta))*((pPrec->Seff)*v-1.)*v_4;
3345 vout.z = -0.75*J_norm*((pPrec->Seff)*v-1.)*(pPrec->Spl2-pPrec->Smi2)*(pPrec->Spl2-pPrec->Smi2)*v_6/(pPrec->eta);
3346 }
3347
3348 return vout;
3349
3350}
3351
3352/**
3353 Get d constants from Appendix B (B9, B10, B11) of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3354*/
3355vector IMRPhenomX_Return_Constants_d_MSA(const double LNorm, const double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
3356{
3357
3358 const double LNorm2 = LNorm * LNorm;
3359 const double JNorm2 = JNorm * JNorm;
3360
3361 vector vout = {0.,0.,0.};
3362
3363 vout.x = -( JNorm2 - (LNorm + pPrec->Spl)*(LNorm + pPrec->Spl))
3364 * ( (JNorm2 - (LNorm - pPrec->Spl)*(LNorm - pPrec->Spl)) );
3365 vout.y = -2.0*(pPrec->Spl2 - pPrec->Smi2)*(JNorm2 + LNorm2 - pPrec->Spl2);
3366 vout.z = -(pPrec->Spl2 - pPrec->Smi2)*(pPrec->Spl2 - pPrec->Smi2);
3367
3368 return vout;
3369}
3370
3371
3372
3373/**
3374 Calculate (L dot J)
3375*/
3376double IMRPhenomX_costhetaLJ(const double L_norm, const double J_norm, const double S_norm)
3377{
3378
3379 double costhetaLJ = 0.5*(J_norm*J_norm + L_norm*L_norm - S_norm*S_norm)/(L_norm * J_norm);
3380
3381 if (costhetaLJ > 1.0) costhetaLJ = +1.0;
3382 if (costhetaLJ < -1.0) costhetaLJ = -1.0;
3383
3384 return costhetaLJ;
3385}
3386
3387/**
3388 Get \f$\psi\f$ using Eq 51 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967:
3389 - Here \f$\psi\f$ is the phase of S as in Eq 23
3390 - Note that the coefficients are defined in Appendix C (C1 and C2)
3391*/
3392double IMRPhenomX_Return_Psi_MSA(double v, double v2, const IMRPhenomXPrecessionStruct *pPrec)
3393{
3394 return ( -0.75 * pPrec->g0 * pPrec->delta_qq * (1.0 + pPrec->psi1*v + pPrec->psi2*v2) / (v2*v) );
3395}
3396
3397/**
3398 Get \f$\dot{\psi}\f$ using Eq 24 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967:
3399*/
3401{
3402 /*
3403 \frac{d \psi}{d t} = \frac{A}{2} \sqrt{S+^2 - S3^2}
3404 */
3405
3406 const double v2 = v*v;
3407
3408 const double A_coeff = -1.5 * (v2 * v2 * v2) * (1.0 - v*pPrec->Seff) * pPrec->sqrt_inveta;
3409 const double psi_dot = 0.5 * A_coeff * sqrt(pPrec->Spl2 - pPrec->S32);
3410
3411 return psi_dot;
3412}
3413
3414/**
3415 Get \f$\phi_z\f$ using Eq 66 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967:
3416 - The coefficients are given in Appendix D (D15 - D26)
3417*/
3418double IMRPhenomX_Return_phiz_MSA(const double v, const double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
3419{
3420 const double invv = 1.0/v;
3421 const double invv2 = invv * invv;
3422
3423 const double LNewt = (pPrec->eta/v);
3424
3425 const double c1 = pPrec->c1;
3426 const double c12 = c1 * c1;
3427
3428 const double SAv2 = pPrec->SAv2;
3429 const double SAv = pPrec->SAv;
3430 const double invSAv = pPrec->invSAv;
3431 const double invSAv2 = pPrec->invSAv2;
3432
3433 // These are log functions defined in Eq. D27 and D28 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3434 const double log1 = log( fabs(c1 + JNorm*pPrec->eta + pPrec->eta*LNewt) );
3435 const double log2 = log( fabs(c1 + JNorm*SAv*v + SAv2*v) );
3436
3437 // Eq. D22 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3438 const double phiz_0_coeff = (JNorm * pPrec->inveta4) * (0.5*c12 - c1*pPrec->eta2*invv/6.0 - SAv2*pPrec->eta2/3.0 - pPrec->eta4*invv2/3.0)
3439 - (c1 * 0.5 * pPrec->inveta)*(c12 * pPrec->inveta4 - SAv2 * pPrec->inveta2)*log1;
3440
3441 // Eq. D23 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967;
3442 // Note the factor of c12 in the second term
3443 const double phiz_1_coeff = - 0.5 * JNorm * pPrec->inveta2 * (c1 + pPrec->eta * LNewt) + 0.5*pPrec->inveta3*(c12 - pPrec->eta2*SAv2)*log1;
3444
3445 // Eq. D24 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3446 const double phiz_2_coeff = -JNorm + SAv*log2 - c1*log1*pPrec->inveta;
3447
3448 // Eq. D25 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3449 const double phiz_3_coeff = JNorm*v - pPrec->eta*log1 + c1*log2*pPrec->invSAv;
3450
3451 // Eq. D26 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3452 const double phiz_4_coeff = (0.5*JNorm*invSAv2*v)*(c1 + v*SAv2) - (0.5*invSAv2*invSAv)*(c12 - pPrec->eta2*SAv2)*log2;
3453
3454 // Eq. D27 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3455 const double phiz_5_coeff = -JNorm*v*( (0.5*c12*invSAv2*invSAv2) - (c1*v*invSAv2/6.0) - v*v/3.0 - pPrec->eta2*invSAv2/3.0)
3456 + (0.5*c1*invSAv2*invSAv2*invSAv)*(c12 - pPrec->eta2*SAv2)*log2;
3457
3458 /*
3459 Eq. 66 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3460
3461 \phi_{z,-1} = \sum^5_{n=0} <\Omega_z>^(n) \phi_z^(n) + \phi_{z,-1}^0
3462
3463 Note that the <\Omega_z>^(n) are given by pPrec->Omegazn_coeff's as in Eqs. D15-D20
3464 */
3465 double phiz_out = ( phiz_0_coeff*pPrec->Omegaz0_coeff
3466 + phiz_1_coeff*pPrec->Omegaz1_coeff
3467 + phiz_2_coeff*pPrec->Omegaz2_coeff
3468 + phiz_3_coeff*pPrec->Omegaz3_coeff
3469 + phiz_4_coeff*pPrec->Omegaz4_coeff
3470 + phiz_5_coeff*pPrec->Omegaz5_coeff
3471 + pPrec->phiz_0
3472 );
3473
3474 if (phiz_out != phiz_out) phiz_out = 0;
3475
3476 return (phiz_out);
3477}
3478
3479/**
3480 Get \f$\zeta\f$ using Eq F5 in Appendix F of Chatziioannou et al, PRD 95, 104004, (2017):
3481*/
3482double IMRPhenomX_Return_zeta_MSA(const double v, const IMRPhenomXPrecessionStruct *pPrec)
3483{
3484 /*
3485 Eq. F5 of Chatziioannou et al, PRD 95, 104004, (2017)
3486
3487 \zeta_{z,-1} = \eta v^{-3} \sum^5_{n=0} <\Omega_{\zeta}>^(n) v^(n) + \zeta_{-1}^0
3488
3489 Note that the <\Omega_{\eta}>^(n) are given by pPrec->Omegazetan_coeff's as in Eqs. F6-F11
3490 */
3491
3492 const double invv = 1.0/v;
3493 const double invv2 = invv*invv;
3494 const double invv3 = invv*invv2;
3495 const double v2 = v*v;
3496 const double logv = log(v);
3497
3498 // \zeta_{z,-1}
3499 // Note factor of log(v) as per LALSimInspiralFDPrecAngles_internals.c, https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiralFDPrecAngles_internals.c#L718
3500 double zeta_out = pPrec->eta * (
3501 pPrec->Omegazeta0_coeff*invv3
3502 + pPrec->Omegazeta1_coeff*invv2
3503 + pPrec->Omegazeta2_coeff*invv
3504 + pPrec->Omegazeta3_coeff*logv
3505 + pPrec->Omegazeta4_coeff*v
3506 + pPrec->Omegazeta5_coeff*v2
3507 )
3508 + pPrec->zeta_0;
3509
3510 if (zeta_out != zeta_out) zeta_out = 0;
3511
3512 // \zeta_{z,-1}
3513 return zeta_out;
3514}
3515
3516/*
3517 Get MSA corrections to \zeta and \phi_z using Eq. F19 in Appendix F and Eq. 67 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967 respectively:
3518 -
3519*/
3520vector IMRPhenomX_Return_MSA_Corrections_MSA(double v, double LNorm, double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
3521{
3522 vector c_vec = {0.,0.,0.};
3523 vector d_vec = {0.,0.,0.};
3524
3525 int pflag = pPrec->IMRPhenomXPrecVersion;
3526
3527 double v2 = v * v;
3528
3529 vector vMSA = {0.,0.,0.};
3530
3531 // Sets c0, c2 and c4 in pPrec as per Eq. B6-B8 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3532 c_vec = IMRPhenomX_Return_Constants_c_MSA(v, JNorm, pPrec);
3533
3534 // Sets d0, d2 and d4 in pPrec as per Eq. B9-B11 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3535 d_vec = IMRPhenomX_Return_Constants_d_MSA(LNorm, JNorm, pPrec);
3536
3537 const double c0 = c_vec.x;
3538 const double c2 = c_vec.y;
3539 const double c4 = c_vec.z;
3540
3541 const double d0 = d_vec.x;
3542 const double d2 = d_vec.y;
3543 const double d4 = d_vec.z;
3544
3545 // Pre-cache a bunch of useful variables
3546 const double two_d0 = 2.0 * d0;
3547
3548 // Eq. B20 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3549 const double sd = sqrt( fabs(d2*d2 - 4.0*d0*d4) );
3550
3551 // Eq. F20 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3552 const double A_theta_L = 0.5 * ( (JNorm/LNorm) + (LNorm/JNorm) - (pPrec->Spl2 / (JNorm * LNorm)) );
3553
3554 // Eq. F21 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967
3555 const double B_theta_L = 0.5 * pPrec->Spl2mSmi2 / (JNorm * LNorm);
3556
3557 // Coefficients for B16
3558 const double nc_num = 2.0*(d0 + d2 + d4);
3559 const double nc_denom = two_d0 + d2 + sd;
3560
3561 // Equations B16 and B17 respectively
3562 const double nc = nc_num / nc_denom;
3563 const double nd = nc_denom / two_d0;
3564
3565 const double sqrt_nc = sqrt(fabs(nc));
3566 const double sqrt_nd = sqrt(fabs(nd));
3567
3568 // Get phase and phase evolution of S
3569 const double psi = IMRPhenomX_Return_Psi_MSA(v,v2,pPrec) + pPrec->psi0;
3570 const double psi_dot = IMRPhenomX_Return_Psi_dot_MSA(v,pPrec);
3571
3572 // Trigonometric calls are expensive, pre-cache them
3573 // Note: arctan(tan(x)) = 0 if and only if x \in (−pi/2,pi/2).
3574 const double tan_psi = tan(psi);
3575 const double atan_psi = atan(tan_psi);
3576
3577 double C1, C2, C2num, C2den;
3578
3579 // Eq. B18
3580 C1 = -0.5 * (c0/d0 - 2.0*(c0+c2+c4)/nc_num);
3581
3582 // Eq. B19
3583 C2num = c0*( -2.0*d0*d4 + d2*d2 + d2*d4 ) - c2*d0*( d2 + 2.0*d4 ) + c4*d0*( two_d0 + d2 );
3584 C2den = 2.0 * d0 * sd * (d0 + d2 + d4);
3585 C2 = C2num / C2den;
3586
3587 // These are defined in Appendix B, B14 and B15 respectively
3588 double Cphi, Dphi;
3589 Cphi = (C1 + C2);
3590 Dphi = (C1 - C2);
3591
3592 double phiz_0_MSA_Cphi_term, phiz_0_MSA_Dphi_term;
3593
3594 /* Calculate C_phi term in Eq. 67 */
3595 if(nc == 1.0)
3596 {
3597 // Limit implied by Eq. 67
3598 phiz_0_MSA_Cphi_term = 0.0;
3599 }
3600 else
3601 {
3602 if(pflag == 222 || pflag == 223)
3603 {
3604 // As implemented in LALSimInspiralFDPrecAngles.c, c.f. https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiralFDPrecAngles_internals.c#L772
3605 phiz_0_MSA_Cphi_term = fabs( (c4 * d0 * ((2*d0+d2) + sd) - c2 * d0 * ((d2+2.*d4) - sd) - c0 * ((2*d0*d4) - (d2+d4) * (d2 - sd))) / (C2den)) * (sqrt_nc / (nc - 1.) * (atan_psi - atan(sqrt_nc * tan_psi))) / psi_dot;
3606 }
3607 else
3608 {
3609 // First term in Eq. 67
3610 phiz_0_MSA_Cphi_term = ( (Cphi / psi_dot) * sqrt_nc / (nc - 1.0) ) * atan( ( (1.0 - sqrt_nc) * tan_psi ) / ( 1.0 + (sqrt_nc * tan_psi * tan_psi) ) );
3611 }
3612 }
3613
3614 /* Calculate D_phi term in Eq. 67 */
3615 if(nd == 1.0)
3616 {
3617 // Limit implied by Eq. 67
3618 phiz_0_MSA_Dphi_term = 0.0;
3619 }
3620 else
3621 {
3622 if(pflag == 222 || pflag == 223)
3623 {
3624 // As implemented in LALSimInspiralFDPrecAngles.c, c.f. https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiralFDPrecAngles_internals.c#L779
3625 phiz_0_MSA_Dphi_term = fabs( (-c4 * d0 * ((2*d0+d2) - sd) + c2 * d0 * ((d2+2.*d4) + sd) - c0 * (-(2*d0*d4) + (d2+d4) * (d2 + sd)))) / (C2den) * (sqrt_nd / (nd - 1.) * (atan_psi - atan(sqrt_nd * tan_psi))) / psi_dot;
3626 }
3627 else
3628 {
3629 // Second term in Eq. 67
3630 phiz_0_MSA_Dphi_term = ( (Dphi / psi_dot) * sqrt_nd / (nd - 1.0) ) * atan( ( (1.0 - sqrt_nd) * tan_psi ) / ( 1.0 + (sqrt_nd * tan_psi * tan_psi) ) );
3631 }
3632 }
3633
3634 // Eq. 67
3635 vMSA.x = ( phiz_0_MSA_Cphi_term + phiz_0_MSA_Dphi_term );
3636
3637 /*
3638 The first MSA correction to \zeta as given in Eq. F19
3639 */
3640 if(pflag == 222 || pflag == 223 || pflag == 224)
3641 {
3642 /*
3643 As implemented in LALSimInspiralFDPrecAngles.c, c.f. https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiralFDPrecAngles_internals.c#L786
3644 Note that Cphi and Dphi are *not* used but phiz_0_MSA_Cphi_term and phiz_0_MSA_Dphi_term are
3645 */
3646 vMSA.y = A_theta_L*vMSA.x + 2.*B_theta_L*d0*(phiz_0_MSA_Cphi_term/(sd-d2) - phiz_0_MSA_Dphi_term/(sd+d2));
3647 }
3648 else
3649 {
3650 /* Eq. F19 as in arXiv:1703.03967 */
3651 vMSA.y = ( ( A_theta_L * (Cphi + Dphi) ) + (2.0 * d0 * B_theta_L) * ( ( Cphi / (sd - d2) ) - ( Dphi / (sd + d2) ) ) ) / psi_dot;
3652 }
3653
3654 // Return 0 if the angles are NAN
3655 if (vMSA.x != vMSA.x) vMSA.x = 0;
3656 if (vMSA.y != vMSA.y) vMSA.y = 0;
3657
3658 // Obsolete component that we initialize to zero just in case
3659 vMSA.z = 0.0;
3660
3661 return vMSA;
3662}
3663
3664
3665/**
3666 * Internal function to computes the PN spin-orbit couplings. As in LALSimInspiralFDPrecAngles.c
3667 * cf https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiralFDPrecAngles_internals.c#L798
3668 */
3669double IMRPhenomX_Get_PN_beta(const double a, const double b, const IMRPhenomXPrecessionStruct *pPrec)
3670{
3671 return (pPrec->dotS1L* (a + b*pPrec->qq) + pPrec->dotS2L*(a + b/pPrec->qq));
3672}
3673
3674/**
3675 * Internal function to compute PN spin-spin couplings. As in LALSimInspiralFDPrecAngles.c
3676 * cf https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiralFDPrecAngles_internals.c#L806
3677 */
3678double IMRPhenomX_Get_PN_sigma(const double a, const double b, const IMRPhenomXPrecessionStruct *pPrec)
3679{
3680 return pPrec->inveta * (a*pPrec->dotS1S2 - b*pPrec->dotS1L*pPrec->dotS2L);
3681}
3682
3683/**
3684 * Internal function to computes PN spin-spin couplings. As in LALSimInspiralFDPrecAngles.c
3685 */
3686double IMRPhenomX_Get_PN_tau(const double a, const double b, const IMRPhenomXPrecessionStruct *pPrec)
3687{
3688 return (pPrec->qq * ( (pPrec->S1_norm_2 * a) - b*pPrec->dotS1L*pPrec->dotS1L) + (a*pPrec->S2_norm_2 - b*pPrec->dotS2L*pPrec->dotS2L) / pPrec->qq) / pPrec->eta;
3689}
3690
3691
3692
3693/* ~~~~~~~~~~ Vector Utility Functions ~~~~~~~~~~ */
3695{
3696 return (v1.x*v2.x) + (v1.y*v2.y) + (v1.z*v2.z);
3697}
3698
3699
3701{
3702 vector v3;
3703 v3.x = v1.y*v2.z - v1.z*v2.y;
3704 v3.y = v1.z*v2.x - v1.x*v2.z;
3705 v3.z = v1.x*v2.y - v1.y*v2.x;
3706
3707 return v3;
3708}
3709
3711{
3712 const double dot_product = (v1.x*v1.x) + (v1.y*v1.y) + (v1.z*v1.z);
3713 return sqrt(dot_product);
3714}
3715
3717{
3718 vector v2;
3719 v2.x = a * v1.x;
3720 v2.y = a * v1.y;
3721 v2.z = a * v1.z;
3722
3723 return v2;
3724}
3725
3727{
3728 vector v3;
3729 v3.x = v1.x + v2.x;
3730 v3.y = v1.y + v2.y;
3731 v3.z = v1.z + v2.z;
3732 return v3;
3733}
3734
3736{
3737 vector v3;
3738 v3.x = v1.x - v2.x;
3739 v3.y = v1.y - v2.y;
3740 v3.z = v1.z - v2.z;
3741 return v3;
3742}
3743
3745{
3746 vector v2;
3747 const double rsinth = v1.r * sin(v1.theta);
3748
3749 v2.x = rsinth * cos(v1.phi);
3750 v2.y = rsinth * sin(v1.phi);
3751 v2.z = v1.r * cos(v1.theta);
3752
3753 return v2;
3754}
3755
3757{
3758 sphpolvector v2;
3759
3760 v2.r = sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z);
3761 v2.theta = acos(v1.z / v2.r);
3762 v2.phi = XLALSimIMRPhenomXatan2tol(v1.y,v1.x,1e-15);
3763
3764 return v2;
3765}
3766
3767/** Function to rotate vector about z axis by given angle */
3769{
3770 vector v2;
3771
3772 v2.x = v1.x * cos(angle) - v1.y * sin(angle);
3773 v2.y = v1.x * sin(angle) + v1.y * cos(angle);
3774 v2.z = v1.z;
3775
3776 return v2;
3777}
3778
3779/** Function to rotate vector about y axis by given angle */
3781{
3782 vector v2;
3783
3784 v2.x = + v1.x * cos(angle) + v1.z * sin(angle);
3785 v2.y = v1.y;
3786 v2.z = - v1.x * sin(angle) + v1.z * cos(angle);
3787
3788 return v2;
3789}
3790
3791
3792/** Function to rotate vector about z axis by given angle */
3794{
3795 const REAL8 tmpx = *vx;
3796 const REAL8 tmpy = *vy;
3797 const REAL8 tmpz = *vz;
3798
3799 REAL8 cosa=cos(angle), sina=sin(angle);
3800
3801 REAL8 tmp1 = tmpx*cosa - tmpy*sina;
3802 REAL8 tmp2 = tmpx*sina + tmpy*cosa;
3803
3804 *vx = tmp1;
3805 *vy = tmp2;
3806 *vz = tmpz;
3807}
3808
3809/** Function to rotate vector about y axis by given angle */
3811{
3812 const REAL8 tmpx = *vx;
3813 const REAL8 tmpy = *vy;
3814 const REAL8 tmpz = *vz;
3815
3816 REAL8 cosa=cos(angle), sina=sin(angle);
3817
3818 REAL8 tmp1 = + tmpx*cosa + tmpz*sina;
3819 REAL8 tmp2 = - tmpx*sina + tmpz*cosa;
3820
3821 *vx = tmp1;
3822 *vy = tmpy;
3823 *vz = tmp2;
3824}
3825
3826REAL8 IMRPhenomX_Cartesian_to_SphericalPolar_theta(const double x, const double y, const UNUSED double z)
3827{
3828 REAL8 theta;
3829 const REAL8 norm = sqrt(x*x + y*y + z*z);
3830
3831 /* Note that we add a numerical offset of 1e-16 to prevent accidental division by zero */
3832 theta = acos(z / (norm + 1e-16));
3833
3834 return theta;
3835}
3836
3837REAL8 IMRPhenomX_Cartesian_to_SphericalPolar_phi(const double x, const double y, const UNUSED double z)
3838{
3839 REAL8 phi;
3840
3841 phi = XLALSimIMRPhenomXatan2tol(y,x,1e-16);
3842
3843 return phi;
3844}
3845
3847{
3848 vector v1;
3849
3850 const double rsintheta = mag * sin(theta);
3851
3852 v1.x = rsintheta * cos(phi);
3853 v1.y = rsintheta * sin(phi);
3854 v1.z = mag * cos(theta);
3855
3856 return v1;
3857}
3858
3859
3860/** used in numerical evaluation of Euler angles */
3862 UINT4 origlen = start->data->length;
3863 start = XLALResizeREAL8TimeSeries(start, 0,
3864 start->data->length + end->data->length - 1);
3865
3866 memcpy(start->data->data + origlen -2, end->data->data,
3867 (end->data->length)*sizeof(REAL8));
3868
3869 XLALGPSAdd(&(start->epoch), -end->deltaT*(end->data->length - 1));
3871
3872
3873 return start;
3874}
3875
3876
3877/** Analytical continuation for alpha angle in MRD */
3878int alphaMRD_coeff(gsl_spline spline_alpha, gsl_interp_accel accel_alpha, double fmaxPN, IMRPhenomXWaveformStruct *pWF, PhenomXPalphaMRD *alpha_params){
3879
3880 int success = GSL_SUCCESS;
3881
3882 double ftrans = fmaxPN;
3883 double f1 = 0.97 *ftrans ;
3884 double f2 = 0.99 *ftrans ;
3885 double f1sq = f1*f1, f2sq = f2*f2;
3886 double f1cube = f1sq*f1;
3887
3888 double alpha1, alpha2, dalpha1;
3889
3890 success = gsl_spline_eval_e(&spline_alpha, f1, &accel_alpha,&alpha1);
3891 alpha1 = -alpha1;
3892 if(success != GSL_SUCCESS)
3893 {
3894 XLALPrintError("XLAL Error - %s: Alpha could not be interpolated at f=%.5f\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(f1,pWF->Mtot));
3895
3896 }
3897
3898 success = success + gsl_spline_eval_deriv_e (&spline_alpha, f1, &accel_alpha,&dalpha1);
3899 dalpha1 = -dalpha1;
3900 if(success != GSL_SUCCESS)
3901 {
3902 XLALPrintError("XLAL Error - %s: dalpha/df could not be interpolated at f=%.5f\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(f1,pWF->Mtot));
3903
3904 }
3905
3906
3907 success = success + gsl_spline_eval_e (&spline_alpha, f2, &accel_alpha,&alpha2);
3908 alpha2 = -alpha2;
3909 if(success != GSL_SUCCESS)
3910 {
3911 XLALPrintError("XLAL Error - %s: Alpha could not be interpolated at f=%.5f\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(f2,pWF->Mtot));
3912
3913 }
3914
3915 double aC=0., bC=0., cC=0.;
3916
3917 if(success == GSL_SUCCESS){
3918
3919 aC = (f1cube*(f1 - f2)*(f1 + f2)*dalpha1 + 2*(pow(f1,4) - 2*f1sq*f2sq)*alpha1 + 2*pow(f2,4)*alpha2)/(2.*pow(f1sq - f2sq,2));
3920
3921 bC = (pow(f1,4)*f2sq*(f1*(f1 - f2)*(f1 + f2)*dalpha1 + 2*f2sq*(-alpha1 + alpha2)))/(2.*pow(f1sq - f2sq,2));
3922
3923 cC = (f1sq*(f1*(-pow(f1,4) + pow(f2,4))*dalpha1 + 4*pow(f2,4)*(alpha1 - alpha2)))/(2.*pow(f1sq - f2sq,2));
3924
3925 }
3926 alpha_params->aRD = aC;
3927 alpha_params->bRD = bC;
3928 alpha_params->cRD = cC;
3929
3930 return success;
3931
3932
3933 }
3934
3935double alphaMRD(double Mf, PhenomXPalphaMRD *alpha_params){
3936
3937 double Mf2 = Mf*Mf;
3938 double Mf4 = Mf2*Mf2;
3939 double minalpha=alpha_params->aRD + alpha_params->bRD/Mf4 + alpha_params->cRD/Mf2;
3940
3941 return (-minalpha);
3942
3943 }
3944
3945double dalphaMRD(double Mf, PhenomXPalphaMRD *alpha_params){
3946
3947 double Mf2 = Mf*Mf;
3948 double Mf3 = Mf2*Mf;
3949 double Mf5 = Mf2*Mf3;
3950
3951 return (4.*alpha_params->bRD/Mf5+2.*alpha_params->cRD/Mf3);
3952
3953 }
3954
3955/** Function to determine coefficients of analytical continuation of beta through MRD */
3956int betaMRD_coeff(gsl_spline spline_cosb, gsl_interp_accel accel_cosb, double fmaxPN, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec){
3957
3958 int success = GSL_SUCCESS;
3959 double fdamp = pWF->fDAMP;
3960
3961 QNMFits *qnms = (QNMFits *) XLALMalloc(sizeof(QNMFits));
3963 pPrec->beta_params->dfdamp = qnms->fdamp_lm[0](pWF->afinal)/pWF->Mfinal-fdamp;
3964 LALFree(qnms);
3965
3966 double kappa = 2.*LAL_PI*pPrec->beta_params->dfdamp;
3967
3968 double f1 = 0.97 *fmaxPN ;
3969 double f2 = 0.98 *fmaxPN ;
3970 double f1sq = f1*f1, f2sq = f2*f2 ;
3971 double ef1 = exp(kappa*f1), ef2 = exp(kappa*f2);
3972
3973 double cosbeta1;
3974 success = gsl_spline_eval_e(&spline_cosb, f1, &accel_cosb, &cosbeta1);
3975
3976 double dcosbeta2;
3977 success=gsl_spline_eval_deriv_e (&spline_cosb, f2, &accel_cosb,&dcosbeta2);
3978
3979 double cosbeta2;
3980 success = gsl_spline_eval_e(&spline_cosb, f2, &accel_cosb,&cosbeta2);
3981
3982 double cosbetamax;
3983 success = gsl_spline_eval_e(&spline_cosb, pPrec->fmax_inspiral, &accel_cosb, &cosbetamax);
3984
3985 double aC,bC,cC,dC;
3986
3987 if(fabs(cosbeta1)>1 || fabs(cosbeta2)>1 || fabs(cosbetamax)>1||success!=GSL_SUCCESS)
3988 {
3989
3990 aC=0.;
3991 bC=0.;
3992 cC=0.;
3993 dC=0. ;
3994 pPrec->beta_params->flat_RD=true;
3995 success = GSL_SUCCESS;
3996 }
3997
3998 else{
3999
4000 double beta1 = acos(cosbeta1);
4001 double beta2 = acos(cosbeta2);
4002 double sqrtarg = 1.-cosbeta2*cosbeta2;
4003 double dbeta2 = - dcosbeta2/sqrt((sqrtarg <= 0. ? 1. : sqrtarg));
4004
4005 double off;
4006 if (pPrec->IMRPhenomXPrecVersion==330)
4007 {
4008 double betaRD = IMRPhenomX_PNR_GenerateRingdownPNRBeta( pWF, pPrec);
4009 off = betaRD;
4010 }
4011 else
4012 {
4013 off = (cosbetamax < 0. ? LAL_PI : 0.);
4014 }
4015
4016 aC= (-(ef1*pow(f1,4)*(off - beta1)) + ef2*pow(f2,3)*(f2*(-f1 + f2)*dbeta2 + (-(f2*(3 + f2*kappa)) + f1*(4 + f2*kappa))*(off - beta2)))/pow(f1 - f2,2);
4017
4018 bC =(2*ef1*pow(f1,4)*f2*(off - beta1) + ef2*pow(f2,3)*((f1 - f2)*f2*(f1 + f2)*dbeta2 - (-(f2sq*(2 + f2*kappa)) + f1sq*(4 + f2*kappa))*(off - beta2)))/pow(f1 - f2,2);
4019
4020 cC =(-(ef1*pow(f1,4)*f2sq*(off - beta1)) + ef2*f1*pow(f2,4)*(f2*(-f1 + f2)*dbeta2 + (-(f2*(2 + f2*kappa)) + f1*(3 + f2*kappa))*(off - beta2)))/pow(f1 - f2,2);
4021
4022 dC = off;
4023
4024
4025 pPrec->beta_params->flat_RD=false;
4026
4027 }
4028
4029 pPrec->beta_params->aRD = aC;
4030 pPrec->beta_params->bRD = bC;
4031 pPrec->beta_params->cRD = cC;
4032 pPrec->beta_params->dRD = dC;
4033
4034 pPrec->beta_params->cosbeta_sign = copysign(1.0, cosbetamax);
4035
4036 return success;
4037
4038 }
4039
4040
4041
4042double betaMRD(double Mf, UNUSED IMRPhenomXWaveformStruct *pWF,PhenomXPbetaMRD *beta_params){
4043
4044 double beta=0.;
4045
4046 if(beta_params->flat_RD)
4047 {
4048 beta=acos(beta_params->cosbeta_sign);
4049 }
4050 else
4051 {
4052 double kappa = 2.*LAL_PI*beta_params->dfdamp;
4053 double Mf2 = Mf*Mf;
4054 double Mf3 = Mf2* Mf;
4055 beta = exp(-Mf*kappa)/Mf*(beta_params->aRD/Mf+beta_params->bRD/(Mf2)+beta_params->cRD/Mf3)+beta_params->dRD;
4056 }
4057
4058 return(beta);
4059 }
4060
4061// define here and not in header file due to betaParams
4062
4063double beta_connection(double Mf, const IMRPhenomX_PNR_beta_parameters *betaParams);
4064
4065double beta_connection(double Mf, const IMRPhenomX_PNR_beta_parameters *betaParams){
4066
4067 double b0 = betaParams->beta_interp_0;
4068 double b1 = betaParams->beta_interp_1;
4069 double b2 = betaParams->beta_interp_2;
4070 double b3 = betaParams->beta_interp_3;
4071
4072 double beta = b0 + b1*Mf + b2*Mf*Mf + b3*Mf*Mf*Mf;
4073
4074 return(beta);
4075
4076 }
4077
4078
4079/* Integrate minimal rotation condition to compute gamma once alpha and beta are known
4080 Uses Boole's rule
4081*/
4082int gamma_from_alpha_cosbeta(double *gamma, double Mf, double deltaMf,IMRPhenomXWaveformStruct *pWF,IMRPhenomXPrecessionStruct *pPrec){
4083
4084 REAL8 Mf_high = Mf+deltaMf;
4085 REAL8 alphadoti, Mf_aux, gammai;
4086 REAL8 step = (Mf_high-Mf)*0.25;
4087
4088 double alphadotcosbeta[5], cosbeta_aux[5];
4089
4090 int status_alpha=GSL_SUCCESS, status_beta=GSL_SUCCESS;
4091
4092 if(Mf<=pPrec->ftrans_MRD)
4093 {
4094 for(UINT4 jdx=0; jdx<5; jdx++){
4095
4096 Mf_aux=Mf+jdx*step;
4097 status_beta=gsl_spline_eval_e(pPrec->cosbeta_spline, Mf_aux, pPrec->cosbeta_acc,&cosbeta_aux[jdx]);
4098 status_alpha=gsl_spline_eval_deriv_e(pPrec->alpha_spline, Mf_aux, pPrec->alpha_acc,&alphadoti);
4099 XLAL_CHECK(status_alpha == GSL_SUCCESS && status_beta==GSL_SUCCESS, XLAL_EFUNC, "Error in %s: could not evaluate splines for alpha and/or gamma angles.\n",__func__);
4100 alphadotcosbeta[jdx]=cosbeta_aux[jdx]*alphadoti;
4101 }
4102
4103 }
4104
4105 else
4106 {
4107 for(UINT4 jdx=0; jdx<5; jdx++){
4108
4109 Mf_aux=Mf+jdx*step;
4110 cosbeta_aux[jdx] = cos(betaMRD(Mf_aux,pWF,pPrec->beta_params));
4111 alphadoti = dalphaMRD(Mf_aux,pPrec->alpha_params);
4112 alphadotcosbeta[jdx]=cosbeta_aux[jdx]*alphadoti;
4113 }
4114 }
4115
4116 gammai= -2.*step/45.*(7.*alphadotcosbeta[0]+32.*alphadotcosbeta[1]+12.*alphadotcosbeta[2]+32.*alphadotcosbeta[3]+7.*alphadotcosbeta[4]);
4117 *gamma = gammai;
4118
4119 return(status_beta+status_alpha);
4120
4121
4122}
4123
4124/** This function evaluates the SpinTaylor Euler angles on a frequency grid passed by the user. Used in LALSimIMRPhenomX.c. */
4126 REAL8Sequence **alphaFS, /**< [out] Alpha angle frequency series [out] */
4127 REAL8Sequence **cosbetaFS, /**< [out] cos(Beta) angle frequency series [out] */
4128 REAL8Sequence **gammaFS, /**< [out] Gamma angle frequency series [out] */
4129 REAL8Sequence *freqsIN, /**< [in] Frequency grid on which Euler angles will be evaluated [in]*/
4130 IMRPhenomXWaveformStruct *pWF, /**< [in] Waveform structure [in]*/
4131 IMRPhenomXPrecessionStruct *pPrec, /**< [in] Precession structure [in]*/
4132 LALDict *LALparams /**< LAL Dictionary struct */
4133 )
4134 {
4135
4136 int status = XLAL_SUCCESS;
4137 REAL8 fRef= pWF->fRef;
4138 REAL8 fmin = freqsIN->data[0];
4139 REAL8 fmax = freqsIN->data[freqsIN->length-1];
4140 // some useful quantities in geom units
4141 REAL8 Mfmin=XLALSimIMRPhenomXUtilsHztoMf(fmin,pWF->Mtot);
4142
4143 /* Sanity checks */
4144 if(fmin <= 0.0) { XLAL_ERROR(XLAL_EDOM, "fmin must be positive.\n"); }
4145 if(fmax <= 0.0) { XLAL_ERROR(XLAL_EDOM, "fmax must be positive.\n"); }
4146 if(fmax <= fmin) { XLAL_ERROR(XLAL_EDOM, "fmax must be larger than fmin.\n"); }
4147 if(fRef < fmin){ XLAL_ERROR(XLAL_EDOM, "fRef must be >= fmin.\n"); }
4148
4149 size_t output_length = freqsIN->length;
4150
4151 //Evaluate splines for alpha and cosbeta
4153 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Error in %s: IMRPhenomX_InterpolateAlphaBeta_SpinTaylor failed.\n",__func__);
4154
4155 //Evaluate splines for gamma
4156 status = IMRPhenomX_InterpolateGamma_SpinTaylor(fmin,fmax,pWF,pPrec);
4157 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Error in %s: IMRPhenomX_InterpolateGamma_SpinTaylor failed.\n",__func__);
4158
4159 REAL8 alphamin=0., cosbetamin=0.;
4160
4161 status = gsl_spline_eval_e(pPrec->alpha_spline, Mfmin, pPrec->alpha_acc,&alphamin);
4162 if( status != GSL_SUCCESS)
4163 {
4164 XLALPrintError("Error in %s: could not evaluate alpha(f_min). Got alpha=%.4f \n",__func__,alphamin);
4166
4167 status = gsl_spline_eval_e(pPrec->cosbeta_spline, Mfmin, pPrec->cosbeta_acc,&cosbetamin);
4168 if ( status != GSL_SUCCESS)
4169 {XLALPrintError("Error in %s: could not evaluate cosbeta(f_min). Got cosbeta=%.4f \n",__func__,cosbetamin);
4171
4172 // check if we can evaluate alpha(fref) using PN or MRD analytical continuation
4173
4174 if(pWF->MfRef<=pPrec->ftrans_MRD)
4175 status=gsl_spline_eval_e(pPrec->alpha_spline, pWF->MfRef, pPrec->alpha_acc, &pPrec->alpha_ref);
4176 else if(pPrec->IMRPhenomXPrecVersion==320||pPrec->IMRPhenomXPrecVersion==321)
4177 pPrec->alpha_ref = alphaMRD(pWF->MfRef,pPrec->alpha_params);
4178 else
4179 status=gsl_spline_eval_e(pPrec->alpha_spline, pPrec->ftrans_MRD, pPrec->alpha_acc, &pPrec->alpha_ref);
4180
4181 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Error in %s: could not evaluate alpha(f_ref).\n",__func__);
4182
4183 *alphaFS = XLALCreateREAL8Sequence(output_length);
4184 *cosbetaFS = XLALCreateREAL8Sequence(output_length);
4185 *gammaFS = XLALCreateREAL8Sequence(output_length);
4186
4187 REAL8 alphaOff = pPrec->alpha0;
4188 pPrec->alpha_offset=-pPrec->alpha_ref+alphaOff;
4189
4190 (*gammaFS)->data[0] = 0.;
4191 (*alphaFS)->data[0] = alphamin-pPrec->alpha_ref+alphaOff;
4192 (*cosbetaFS)->data[0] = cosbetamin;
4193
4194
4195 REAL8 alphai=0., cosbetai=0., gammai=0.;
4196 REAL8 Mf;
4197
4198
4199 for( UINT4 i = 1; i < output_length; i++ ){
4200
4201 Mf=XLALSimIMRPhenomXUtilsHztoMf(freqsIN->data[i],pWF->Mtot);
4202 REAL8 deltaMF =XLALSimIMRPhenomXUtilsHztoMf(freqsIN->data[i]-freqsIN->data[i-1],pWF->Mtot);
4203
4204 if(Mf<pPrec->ftrans_MRD)
4205
4206 {
4207
4208 alphai = gsl_spline_eval(pPrec->alpha_spline, Mf, pPrec->alpha_acc);
4209 cosbetai = gsl_spline_eval(pPrec->cosbeta_spline, Mf, pPrec->cosbeta_acc);
4210 gammai = gsl_spline_eval(pPrec->gamma_spline, Mf, pPrec->gamma_acc);
4211
4212 (*alphaFS)->data[i] = alphai+pPrec->alpha_offset;
4213 (*cosbetaFS)->data[i] = cosbetai;
4214 (*gammaFS)-> data[i] = gammai;
4215
4216 }
4217
4218
4219 else {
4220
4221 if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321 ){
4222
4223 (*alphaFS)->data[i]=alphaMRD(Mf,pPrec->alpha_params)+pPrec->alpha_offset;
4224 REAL8 beta_MRD=betaMRD(Mf,pWF,pPrec->beta_params);
4225 (*cosbetaFS)->data[i]=cos(beta_MRD);
4226 REAL8 deltagamma = 0.;
4227 status = gamma_from_alpha_cosbeta(&deltagamma,Mf, deltaMF,pWF,pPrec);
4228 if(status==GSL_SUCCESS)
4229 (*gammaFS) -> data[i] = (*gammaFS)-> data[i-1]+deltagamma;
4230 else (*gammaFS) -> data[i] = (*gammaFS)-> data[i-1];
4231 }
4232
4233 // just continue angles into RD with constant values
4234
4235 else{
4236 (*alphaFS)->data[i]=(*alphaFS)->data[i-1];
4237 (*cosbetaFS)->data[i]=(*cosbetaFS)-> data[i-1];
4238 (*gammaFS) -> data[i]=(*gammaFS)-> data[i-1];
4239 }
4240 }
4241
4242
4243 }
4244
4245 return status;
4246
4247 }
4248
4249/** This function builds and stores splines for \f$\alpha\f$ and \f$\cos\beta\f$ in the frequency range covered by PN, and computes a spline for \f$\gamma\f$ between fmin and fmax. Used in XLALSimIMRPhenomX_PNR_GeneratePNRAngles. */
4251 REAL8 fmin, /**< [in] Minimum frequency of the gamma spline [in]*/
4252 REAL8 fmax, /**< [in] Maximum frequency of the gamma spline [in]*/
4253 IMRPhenomXWaveformStruct *pWF, /**< [in] Waveform structure [in]*/
4254 IMRPhenomXPrecessionStruct *pPrec, /**< [in] Precession structure [in]*/
4255 LALDict *LALparams /**< LAL Dictionary struct */
4256 )
4257 {
4258
4259 int status = XLAL_SUCCESS;
4260 REAL8 fRef= pWF->fRef;
4261
4262 /* Sanity checks */
4263 if(fmin <= 0.0) { XLAL_ERROR(XLAL_EDOM, "fmin must be positive.\n"); }
4264 if(fmax <= 0.0) { XLAL_ERROR(XLAL_EDOM, "fmax must be positive.\n"); }
4265 if(fmax <= fmin) { XLAL_ERROR(XLAL_EDOM, "fmax must be larger than fmin.\n"); }
4266 if(fRef < fmin){ XLAL_ERROR(XLAL_EDOM, "fRef must be >= fmin.\n"); }
4267
4268 //Evaluate splines for alpha and cosbeta
4270 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Error in %s: IMRPhenomX_InterpolateAlphaBeta_SpinTaylor failed.\n",__func__);
4271
4272 //Evaluate splines for gamma
4273 status = IMRPhenomX_InterpolateGamma_SpinTaylor(fmin,fmax,pWF,pPrec);
4274 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Error in %s: IMRPhenomX_InterpolateGamma_SpinTaylor failed.\n",__func__);
4275
4276 // check if we can evaluate alpha(fref) using PN or an analytical continuation
4277
4278 if(pWF->MfRef<=pPrec->ftrans_MRD)
4279 status=gsl_spline_eval_e(pPrec->alpha_spline, pWF->MfRef, pPrec->alpha_acc, &pPrec->alpha_ref);
4280 else if(pPrec->IMRPhenomXPrecVersion==320||pPrec->IMRPhenomXPrecVersion==321)
4281 pPrec->alpha_ref = alphaMRD(pWF->MfRef,pPrec->alpha_params);
4282 else
4283 status=gsl_spline_eval_e(pPrec->alpha_spline, pPrec->ftrans_MRD, pPrec->alpha_acc, &pPrec->alpha_ref);
4284
4285 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Error in %s: could not evaluate alpha(f_ref).\n",__func__);
4286
4287 return status;
4288
4289 }
4290
4291/** This function computes gamma from the minimal rotation condition and stores a spline for it */
4293 REAL8 fmin, /**< starting frequency (Hz) */
4294 REAL8 fmax, /**< maximum frequency (Hz) */
4295 IMRPhenomXWaveformStruct *pWF, /**< [in] Waveform structure [in]*/
4296 IMRPhenomXPrecessionStruct *pPrec /**< [in] Precession structure [in]*/
4297 )
4298 {
4299
4300 int status = XLAL_SUCCESS;
4301 REAL8 fRef= pWF->fRef;
4302
4303 // some useful quantities in geom units
4304 REAL8 Mfmin=XLALSimIMRPhenomXUtilsHztoMf(fmin,pWF->Mtot);
4305
4306 /* Sanity checks */
4307 if(fmin <= 0.0) { XLAL_ERROR(XLAL_EDOM, "fmin must be positive.\n"); }
4308 if(fmax <= 0.0) { XLAL_ERROR(XLAL_EDOM, "fmax must be positive.\n"); }
4309 if(fmax <= fmin) { XLAL_ERROR(XLAL_EDOM, "fmax must be larger than fmin.\n"); }
4310 if(fRef < fmin){ XLAL_ERROR(XLAL_EDOM, "fRef must be >= fmin.\n"); }
4311
4312 REAL8 deltaMF = get_deltaF_from_wfstruct(pWF);
4313 REAL8 deltaF = XLALSimIMRPhenomXUtilsMftoHz(deltaMF,pWF->Mtot);
4314 // length of frequency series expected by the user
4315 size_t iStart = (size_t) (fmin / deltaF);
4316 size_t iStop = (size_t) (fmax / deltaF) + 1;
4317 size_t output_length = iStop-iStart;
4318 if(output_length<4)
4319 {
4320 XLALPrintError("Error in %s: no. of points is insufficient for spline interpolation of gamma",__func__);
4322 }
4323
4324
4325 REAL8Sequence *frequencies = NULL;
4326 frequencies=XLALCreateREAL8Sequence(output_length);
4327 frequencies->data[0]=Mfmin;
4328
4329 REAL8Sequence *gamma_array = NULL;
4330 gamma_array=XLALCreateREAL8Sequence(output_length);
4331 gamma_array->data[0]=0.;
4332 REAL8 Mf;
4333 int gamma_status=GSL_SUCCESS;
4334
4335 for( UINT4 i = 1; i < output_length; i++ )
4336 {
4337
4338 Mf=Mfmin+i*deltaMF;
4339 frequencies->data[i]=Mf;
4340 REAL8 deltagamma=0.;
4341
4342 if(Mf<pPrec->ftrans_MRD){
4343 gamma_status=gamma_from_alpha_cosbeta(&deltagamma,Mf, deltaMF,pWF,pPrec);
4344 gamma_array->data[i]=gamma_array->data[i-1]+deltagamma;
4345 }
4346 else {
4347 if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321 ){
4348 gamma_status = gamma_from_alpha_cosbeta(&deltagamma, Mf, deltaMF,pWF,pPrec);
4349 gamma_array->data[i]=gamma_array->data[i-1]+deltagamma;
4350 }
4351 // just continue angles into RD with constant values
4352 else{
4353 gamma_array->data[i]=gamma_array->data[i-1];
4354 }
4355 }
4356
4357 if(gamma_status!=GSL_SUCCESS) status=gamma_status;
4358
4359 }
4360
4361 if(status==GSL_SUCCESS)
4362 {
4363 pPrec->gamma_acc = gsl_interp_accel_alloc();
4364 pPrec->gamma_spline = gsl_spline_alloc(gsl_interp_cspline, output_length);
4365 gsl_spline_init(pPrec->gamma_spline, frequencies->data, gamma_array -> data, output_length);
4366 pPrec->gamma_ref=gsl_spline_eval(pPrec->gamma_spline, pWF->MfRef, pPrec->gamma_acc);
4367 }
4368
4369 else{
4370
4371 gsl_spline_free(pPrec->alpha_spline);
4372 gsl_interp_accel_free(pPrec->alpha_acc);
4373
4374 gsl_spline_free(pPrec->cosbeta_spline);
4375 gsl_interp_accel_free(pPrec->cosbeta_acc);
4376
4377
4378 }
4379
4380
4381 XLALDestroyREAL8Sequence(frequencies);
4382 XLALDestroyREAL8Sequence(gamma_array);
4383
4384 return status;
4385
4386 }
4387
4388
4389
4390/** This function computes cubic splines of the alpha and beta inspiral Euler angles, which are then stored into a IMRPhenomXPrecessionStruct structure.
4391 - If the user passed PhenomXPFinalSpinMod=4, the function corrects the estimate for the final precessing spin based on the result of the PN integration.
4392 - For versions 32*, the function also computes the parameters needed to obtain a smooth MRD continuation of alpha/beta.
4393 - The memory allocated for the PN arrays is freed at the end of this function.
4394*/
4396 IMRPhenomXWaveformStruct *pWF, /**< [in] Waveform structure [in]*/
4397 IMRPhenomXPrecessionStruct *pPrec, /**< [in] Precession structure [in]*/
4398 LALDict *LALparams /**< LAL Dictionary struct */
4399 )
4400{
4401
4402 int status = XLAL_SUCCESS;
4403
4404 size_t lenPN = pPrec->PNarrays->V_PN->data->length;
4405
4406 REAL8Sequence *fgw =NULL ;
4407 /* Setup sequences for angles*/
4408 REAL8Sequence *alpha = NULL;
4409 REAL8Sequence *alphaaux = NULL;
4410 REAL8Sequence *cosbeta = NULL;
4411
4412
4413 fgw=XLALCreateREAL8Sequence(lenPN);
4415 alphaaux=XLALCreateREAL8Sequence(lenPN);
4416 cosbeta=XLALCreateREAL8Sequence(lenPN);
4417
4418
4419 REAL8 fgw_Mf, fgw_Hz, Mfmax_PN=0.;
4420 // i_max is used to discard possibly unphysical points in the calculation of the final spin
4421 UINT8 i_max=0;
4422 REAL8 LNhatx_temp,LNhaty_temp,LNhatz_temp;
4423
4424 for(UINT8 i=0; i < lenPN; i++){
4425
4426 LNhatx_temp = (pPrec->PNarrays->LNhatx_PN->data->data[i]);
4427 LNhaty_temp = (pPrec->PNarrays->LNhaty_PN->data->data[i]);
4428 LNhatz_temp = (pPrec->PNarrays->LNhatz_PN->data->data[i]);
4429
4430 IMRPhenomX_rotate_z(-pPrec->phiJ_Sf, &LNhatx_temp, &LNhaty_temp, &LNhatz_temp);
4431 IMRPhenomX_rotate_y(-pPrec->thetaJ_Sf, &LNhatx_temp, &LNhaty_temp, &LNhatz_temp);
4432 IMRPhenomX_rotate_z(-pPrec->kappa, &LNhatx_temp, &LNhaty_temp, &LNhatz_temp);
4433
4434 fgw_Hz= pow(pPrec->PNarrays->V_PN->data->data[i],3.)/pPrec->piGM;
4435 fgw_Mf= XLALSimIMRPhenomXUtilsHztoMf(fgw_Hz,pWF->Mtot);
4436
4437 if(fgw_Hz>0.){
4438
4439 /* Compute Euler angles in the J frame */
4440 alphaaux->data[i] = atan2(LNhaty_temp, LNhatx_temp);
4441 cosbeta->data[i] = LNhatz_temp;
4442 fgw->data[i] = fgw_Mf;
4443 Mfmax_PN = fgw_Mf;
4444 i_max = i;
4445 }
4446
4447 else
4448 break;
4449
4450
4451 }
4452
4453 REAL8 fmax_inspiral;
4455 fmax_inspiral = Mfmax_PN;
4456 else
4457 fmax_inspiral = Mfmax_PN-pWF->deltaMF;
4458
4459 if(fmax_inspiral > pWF->fRING-pWF->fDAMP) fmax_inspiral = 1.020 * pWF->fMECO;
4460
4461 pPrec->ftrans_MRD = 0.98*fmax_inspiral;
4462 pPrec->fmax_inspiral= fmax_inspiral;
4463
4464
4465 // Interpolate alpha
4466 XLALSimIMRPhenomXUnwrapArray(alphaaux->data, alpha->data, lenPN);
4467
4468 pPrec->alpha_acc = gsl_interp_accel_alloc();
4469 pPrec->alpha_spline = gsl_spline_alloc(gsl_interp_cspline, lenPN);
4470
4471 status = gsl_spline_init(pPrec->alpha_spline, fgw->data, alpha->data, lenPN);
4472
4473 if (status != GSL_SUCCESS)
4474 {
4475 XLALPrintError("Error in %s: error in computing gsl spline for alpha.\n",__func__);
4476 }
4477
4478 // Interpolate cosbeta
4479 pPrec->cosbeta_acc = gsl_interp_accel_alloc();
4480 pPrec->cosbeta_spline = gsl_spline_alloc(gsl_interp_cspline, lenPN);
4481 status =gsl_spline_init(pPrec->cosbeta_spline, fgw->data, cosbeta->data, lenPN);
4482
4483 if (status != GSL_SUCCESS)
4484 {
4485 XLALPrintError("Error in %s: error in computing gsl spline for cos(beta).\n",__func__);
4486 }
4487
4488 REAL8 cosbetamax;
4489
4490 status = gsl_spline_eval_e(pPrec->cosbeta_spline, fmax_inspiral, pPrec->cosbeta_acc,&cosbetamax);
4491 if(status != GSL_SUCCESS)
4492 {
4493 XLALPrintError("Error in %s: error in computing cosbeta.\n",__func__);
4494 }
4495
4496 // estimate final spin using spins at the end of the PN integration
4497
4499
4500 REAL8 m1 = pWF->m1_SI / pWF->Mtot_SI;
4501 REAL8 m2 = pWF->m2_SI / pWF->Mtot_SI;
4502
4503 vector Lnf = {pPrec->PNarrays->LNhatx_PN->data->data[i_max],pPrec->PNarrays->LNhaty_PN->data->data[i_max],pPrec->PNarrays->LNhatz_PN->data->data[i_max]};
4504 REAL8 Lnorm = sqrt(IMRPhenomX_vector_dot_product(Lnf,Lnf));
4505 vector S1f = {pPrec->PNarrays->S1x_PN->data->data[i_max],pPrec->PNarrays->S1y_PN->data->data[i_max],pPrec->PNarrays->S1z_PN->data->data[i_max]};
4506 vector S2f = {pPrec->PNarrays->S2x_PN->data->data[i_max],pPrec->PNarrays->S2y_PN->data->data[i_max],pPrec->PNarrays->S2z_PN->data->data[i_max]};
4507
4508
4509 REAL8 dotS1L = IMRPhenomX_vector_dot_product(S1f,Lnf)/Lnorm;
4510 REAL8 dotS2L = IMRPhenomX_vector_dot_product(S2f,Lnf)/Lnorm;
4511 vector S1_perp = IMRPhenomX_vector_diff(S1f,IMRPhenomX_vector_scalar(Lnf, dotS1L));
4512 S1_perp = IMRPhenomX_vector_scalar(S1_perp,m1*m1);
4513 vector S2_perp = IMRPhenomX_vector_diff(S2f,IMRPhenomX_vector_scalar(Lnf, dotS2L));
4514 S2_perp = IMRPhenomX_vector_scalar(S2_perp,m2*m2);
4515 vector Stot_perp = IMRPhenomX_vector_sum(S1_perp,S2_perp);
4516 REAL8 S_perp_norm = sqrt(IMRPhenomX_vector_dot_product(Stot_perp,Stot_perp));
4517 REAL8 chi_perp_norm = S_perp_norm *pow(m1 + m2,2)/pow(m1,2);
4518
4519 pWF->afinal= copysign(1.0, cosbetamax)* XLALSimIMRPhenomXPrecessingFinalSpin2017(pWF->eta,dotS1L,dotS2L,chi_perp_norm);
4520
4521 pWF->fRING = evaluate_QNMfit_fring22(pWF->afinal) / (pWF->Mfinal);
4522 pWF->fDAMP = evaluate_QNMfit_fdamp22(pWF->afinal) / (pWF->Mfinal);
4523 }
4524
4525 // initialize parameters for RD continuation
4526 pPrec->alpha_params = XLALMalloc(sizeof(PhenomXPalphaMRD));
4527 pPrec->beta_params = XLALMalloc(sizeof(PhenomXPbetaMRD));
4528
4529 if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321 || pPrec->IMRPhenomXPrecVersion==330 ){
4530
4531 status = alphaMRD_coeff(*pPrec->alpha_spline, *pPrec->alpha_acc, pPrec->fmax_inspiral, pWF, pPrec->alpha_params);
4532 if(status!=XLAL_SUCCESS) XLALPrintError("XLAL Error in %s: error in computing parameters for MRD continuation of Euler angles.\n",__func__);
4533
4534
4535 status = betaMRD_coeff(*pPrec->cosbeta_spline, *pPrec->cosbeta_acc, pPrec->fmax_inspiral, pWF, pPrec);
4536 if(status!=XLAL_SUCCESS) XLALPrintError("XLAL Error in %s: error in computing parameters for MRD continuation of Euler angles.\n",__func__);
4537
4538 }
4539
4540
4551 XLALFree(pPrec->PNarrays);
4552
4553
4554
4556 XLALDestroyREAL8Sequence(alphaaux);
4557 XLALDestroyREAL8Sequence(cosbeta);
4559
4560 if(status != GSL_SUCCESS){
4561
4562 gsl_spline_free(pPrec->alpha_spline);
4563 gsl_spline_free(pPrec->cosbeta_spline);
4564 gsl_interp_accel_free(pPrec->alpha_acc);
4565 gsl_interp_accel_free(pPrec->cosbeta_acc);
4566
4567
4568 }
4569
4570
4571
4572 return status;
4573
4574 }
4575
4576
4577
4578
4579/** Wrapper of XLALSimInspiralSpinTaylorPNEvolveOrbit : if integration is successful, stores arrays containing PN solution in a PhenomXPInspiralArrays struct */
4581 PhenomXPInspiralArrays *arrays, /**< [out] Struct containing solutions returned by PNEvolveOrbit */
4582 double* fmin_PN, /**< [out] Minimum frequency in PN solutions array */
4583 REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) at fRef */
4584 REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) at fRef */
4585 REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) at fRef */
4586 REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) at fRef */
4587 REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) at fRef */
4588 REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) at fRef */
4589 REAL8 fmin, /**< minimum frequency (Hz) */
4590 int PrecVersion, /**< precessing version (int) */
4591 IMRPhenomXWaveformStruct *pWF, /**< Waveform structure [in]*/
4592 LALDict *LALparams /**< LAL Dictionary struct */
4593 )
4594{
4595
4596 int status = XLAL_SUCCESS;
4597 REAL8 fRef=pWF->fRef;
4598
4599 /* Sanity checks */
4600 if(fmin <= 0.0) { XLAL_ERROR(XLAL_EDOM, "fmin must be positive.\n"); }
4601 if(fRef < fmin){ XLAL_ERROR(XLAL_EDOM, "fRef must be >= fmin.\n"); }
4602
4603
4604 REAL8 m1_SI=pWF->m1_SI,m2_SI=pWF->m2_SI;
4605 REAL8 s1x=chi1x, s1y=chi1y, s1z=chi1z;
4606 REAL8 s2x=chi2x, s2y=chi2y, s2z=chi2z;
4607
4608 UNUSED REAL8 piGM = LAL_PI * (pWF->m1_SI + pWF->m2_SI) * (LAL_G_SI / LAL_C_SI) / (LAL_C_SI * LAL_C_SI);
4609
4610 REAL8 quadparam1=pWF->quadparam1;
4611 REAL8 quadparam2=pWF->quadparam2;
4612 REAL8 lambda1=pWF->lambda1;
4613 REAL8 lambda2=pWF->lambda2;
4614
4615
4616 // Tidal parameters are preloaded in aligned spin waveform structure. If using BH values in the twisting up, overwrite these. Note that the aligned spin model (i.e. the co-precessing modes) will still be using the NS tidal parameters
4617 if (PrecVersion==311 || PrecVersion==321)
4618 {
4619 quadparam1 = 1.;
4620 quadparam2 = 1.;
4621 lambda1 = 0.;
4622 lambda2 = 0.;
4623 }
4624
4625
4629 int lscorr = XLALSimInspiralWaveformParamsLookupLscorr(LALparams);
4630 if(lscorr!=0) XLAL_PRINT_WARNING("IMRPhenomXP with SpinTaylor angles was only reviewed for lscorr=0.\n");
4631
4632
4633 // enforce default orders if user does not specify any: this is added to ensure backward compatibility should default PN orders change in the future
4634 if(phaseO == -1) XLALSimInspiralWaveformParamsInsertPNPhaseOrder(LALparams,7);
4635 if(spinO == -1) XLALSimInspiralWaveformParamsInsertPNSpinOrder(LALparams,6);
4636 if(tideO == -1) XLALSimInspiralWaveformParamsInsertPNTidalOrder(LALparams,12);
4637
4638 /* Initialize needed time series */
4639 REAL8TimeSeries *V = NULL;
4640 REAL8TimeSeries *Phi = NULL;
4641 REAL8TimeSeries *S1x = NULL;
4642 REAL8TimeSeries *S1y = NULL;
4643 REAL8TimeSeries *S1z = NULL;
4644 REAL8TimeSeries *S2x = NULL;
4645 REAL8TimeSeries *S2y = NULL;
4646 REAL8TimeSeries *S2z = NULL;
4647 REAL8TimeSeries *LNhatx = NULL;
4648 REAL8TimeSeries *LNhaty = NULL;
4649 REAL8TimeSeries *LNhatz = NULL;
4650 REAL8TimeSeries *E1x = NULL;
4651 REAL8TimeSeries *E1y = NULL;
4652 REAL8TimeSeries *E1z = NULL;
4653
4654
4655
4656 REAL8 lnhatx,lnhaty,lnhatz, e1y,e1z,e1x;
4657 lnhatx = lnhaty = e1y = e1z = 0;
4658 lnhatz = e1x = 1.;
4659
4660 // if the user does not specify any SpinTaylor approximant, default to SpinTaylorT4
4661 const char * approx_name=XLALSimInspiralWaveformParamsLookupPhenomXPSpinTaylorVersion(LALparams);
4662 if(approx_name==NULL)
4663 approx_name="SpinTaylorT4";
4665
4666 REAL8 fS,fE;
4667
4668 REAL8 fMECO_Hz=XLALSimIMRPhenomXUtilsMftoHz(pWF->fMECO,pWF->Mtot);
4669
4670 // for versions 32* to work, we need to start integration in the inspiral
4671 if(fmin>fMECO_Hz &&(PrecVersion==320 || PrecVersion==321))
4672 {
4673 fmin=fMECO_Hz;
4674 }
4675
4676 REAL8 fCut = XLALSimIMRPhenomXUtilsMftoHz(pWF->fRING+8.*pWF->fDAMP,pWF->Mtot);
4677 int n;
4678
4680
4681 if(coarse_fac < 1) { XLAL_ERROR(XLAL_EDOM, "Coarse factor must be >= 1!\n");}
4682
4683 REAL8 deltaT_coarse = 0.5*coarse_fac/(fCut);
4684
4685 fS=fmin;
4686 fE=fCut;
4687
4688 if( fRef < LAL_REAL4_EPS || fabs(fRef - fmin) < LAL_REAL4_EPS )
4689
4690 {
4691 fRef = fmin;
4692
4693 n=XLALSimInspiralSpinTaylorPNEvolveOrbit(&V, &Phi, &S1x, &S1y, &S1z, &S2x, &S2y, &S2z, &LNhatx, &LNhaty, &LNhatz, &E1x, &E1y, &E1z, deltaT_coarse, m1_SI, m2_SI,fS,fE,s1x,s1y,s1z,s2x,s2y,s2z,lnhatx,lnhaty,lnhatz,e1x,e1y,e1z,lambda1,lambda2,quadparam1, quadparam2, spinO, tideO, phaseO, lscorr, approx);
4694
4695 if( n < 0 )
4697 }
4698
4699
4700 else if((fRef - fmin) > LAL_REAL4_EPS )
4701 {
4702 /* Integrate backward to fStart */
4703 fS = fRef;
4704 fE = fmin-0.5;
4706 &S1x, &S1y, &S1z, &S2x, &S2y, &S2z,
4707 &LNhatx, &LNhaty, &LNhatz, &E1x, &E1y, &E1z,
4708 deltaT_coarse, m1_SI, m2_SI, fS, fE, s1x, s1y, s1z, s2x, s2y,
4709 s2z, lnhatx, lnhaty, lnhatz, e1x, e1y, e1z, lambda1, lambda2,
4710 quadparam1, quadparam2, spinO, tideO, phaseO, lscorr, approx);
4711
4712
4713 if( n < 0 ) XLAL_ERROR(XLAL_EFUNC);
4714
4715
4716 if(V->data->length>1){
4717
4718 REAL8TimeSeries *V2=NULL, *Phi2=NULL, *S1x2=NULL, *S1y2=NULL, *S1z2=NULL, *S2x2=NULL, *S2y2=NULL, *S2z2=NULL;
4719 REAL8TimeSeries *LNhatx2=NULL, *LNhaty2=NULL, *LNhatz2=NULL, *E1x2=NULL, *E1y2=NULL, *E1z2=NULL;
4720
4721
4722 /* Integrate forward to end of waveform */
4723 fS = fRef;
4724 fE = fCut;
4726 &S1x2, &S1y2, &S1z2, &S2x2, &S2y2, &S2z2,
4727 &LNhatx2, &LNhaty2, &LNhatz2, &E1x2, &E1y2, &E1z2,
4728 deltaT_coarse, m1_SI, m2_SI, fS, fE, s1x, s1y, s1z, s2x, s2y,
4729 s2z, lnhatx, lnhaty, lnhatz, e1x, e1y, e1z, lambda1, lambda2,
4730 quadparam1, quadparam2, spinO, tideO, phaseO, lscorr, approx);
4731 if( n < 0 ) XLAL_ERROR(XLAL_EFUNC);
4732
4733
4734
4735 // Stitch 2nd set of vectors onto 1st set.
4736 V = appendTS(V, V2);
4737 Phi = appendTS(Phi, Phi2);
4738 S1x = appendTS(S1x, S1x2);
4739 S1y = appendTS(S1y, S1y2);
4740 S1z = appendTS(S1z, S1z2);
4741 S2x = appendTS(S2x, S2x2);
4742 S2y = appendTS(S2y, S2y2);
4743 S2z = appendTS(S2z, S2z2);
4744 LNhatx = appendTS(LNhatx, LNhatx2);
4745 LNhaty = appendTS(LNhaty, LNhaty2);
4746 LNhatz = appendTS(LNhatz, LNhatz2);
4747 E1x = appendTS(E1x, E1x2);
4748 E1y = appendTS(E1y, E1y2);
4749 E1z = appendTS(E1z, E1z2);
4750 }
4751
4752 else{
4753
4768 // the failure will be caught by GetAndSetPrecessionVariables. If the integration fails, the waveform generation will default to prec. version 223
4769 return(XLAL_FAILURE);
4770
4771 }
4772
4773 }
4774
4775 else
4776 {
4778 }
4779
4780 size_t copyLength;
4781
4782 if(coarse_fac>1)
4783 {
4784 // at high frequencies, perform a fine integration
4785 // stop some bins before the last one
4786
4787 int lenLow=V->data->length;
4788 int nbuffer=MIN(9,lenLow-1);
4789
4790 if(lenLow-1-nbuffer<0) nbuffer=lenLow-1;
4791
4792 copyLength=lenLow-1-nbuffer;
4793
4794 REAL8 vtrans = V->data->data[lenLow-1-nbuffer];
4795 REAL8 ftrans = pow(vtrans,3.)/piGM;
4796
4797 REAL8 LNhatx_trans=LNhatx->data->data[lenLow-1-nbuffer];
4798 REAL8 LNhaty_trans=LNhaty->data->data[lenLow-1-nbuffer];
4799 REAL8 LNhatz_trans=LNhatz->data->data[lenLow-1-nbuffer];
4800
4801 REAL8 E1x_trans, E1y_trans, E1z_trans;
4802 E1x_trans = E1x->data->data[lenLow-1-nbuffer];
4803 E1y_trans = E1y->data->data[lenLow-1-nbuffer];
4804 E1z_trans = E1z->data->data[lenLow-1-nbuffer];
4805
4806 REAL8 S1x_trans, S1y_trans, S1z_trans, S2x_trans, S2y_trans, S2z_trans;
4807 S1x_trans = S1x->data->data[lenLow-1-nbuffer];
4808 S1y_trans = S1y->data->data[lenLow-1-nbuffer];
4809 S1z_trans = S1z->data->data[lenLow-1-nbuffer];
4810
4811 S2x_trans = S2x->data->data[lenLow-1-nbuffer];
4812 S2y_trans = S2y->data->data[lenLow-1-nbuffer];
4813 S2z_trans = S2z->data->data[lenLow-1-nbuffer];
4814
4815 fS=ftrans;
4816 fE=fCut;
4817 REAL8 deltaT = 0.5/(fCut);
4818
4819 XLAL_CHECK(fS > 0., XLAL_EFUNC, "Error: Transition frequency in PN integration is not positive.\n");
4820
4821 REAL8TimeSeries *Phi_PN=NULL, *E1x_PN=NULL, *E1y_PN=NULL, *E1z_PN=NULL;
4822
4823 n=XLALSimInspiralSpinTaylorPNEvolveOrbit(&arrays->V_PN, &Phi_PN, &arrays->S1x_PN, &arrays->S1y_PN, &arrays->S1z_PN, &arrays->S2x_PN, &arrays->S2y_PN, &arrays->S2z_PN, &arrays->LNhatx_PN, &arrays->LNhaty_PN, &arrays->LNhatz_PN, &E1x_PN, &E1y_PN, &E1z_PN, deltaT, m1_SI, m2_SI,fS,fE,S1x_trans,S1y_trans,S1z_trans,S2x_trans,S2y_trans,S2z_trans,LNhatx_trans,LNhaty_trans,LNhatz_trans,E1x_trans, E1y_trans, E1z_trans,lambda1,lambda2,quadparam1, quadparam2, spinO, tideO, phaseO, lscorr, approx);
4824
4825 // free high frequency arrays
4830
4831 if( n < 0 )
4833
4834 size_t lenPN=lenLow-nbuffer-1+arrays->V_PN->data->length;
4835
4836 if(lenPN < 4) {
4837 XLALPrintError("Error in %s: no. of points is insufficient for spline interpolation",__func__);
4839 }
4840
4841 // resize series to full length (coarse+fine)
4842 XLALResizeREAL8TimeSeries(arrays->V_PN,-(lenLow-nbuffer-1),lenPN);
4843 XLALResizeREAL8TimeSeries(arrays->LNhatx_PN,-(lenLow-nbuffer-1),lenPN);
4844 XLALResizeREAL8TimeSeries(arrays->LNhaty_PN,-(lenLow-nbuffer-1),lenPN);
4845 XLALResizeREAL8TimeSeries(arrays->LNhatz_PN,-(lenLow-nbuffer-1),lenPN);
4846 XLALResizeREAL8TimeSeries(arrays->S1x_PN,-(lenLow-nbuffer-1),lenPN);
4847 XLALResizeREAL8TimeSeries(arrays->S1y_PN,-(lenLow-nbuffer-1),lenPN);
4848 XLALResizeREAL8TimeSeries(arrays->S1z_PN,-(lenLow-nbuffer-1),lenPN);
4849 XLALResizeREAL8TimeSeries(arrays->S2x_PN,-(lenLow-nbuffer-1),lenPN);
4850 XLALResizeREAL8TimeSeries(arrays->S2y_PN,-(lenLow-nbuffer-1),lenPN);
4851 XLALResizeREAL8TimeSeries(arrays->S2z_PN,-(lenLow-nbuffer-1),lenPN);
4852
4853 }
4854
4855 else{
4856
4857 copyLength=V->data->length-1;
4858 if(copyLength < 4) {
4859 XLALPrintError("Error in %s: no. of points is insufficient for spline interpolation",__func__);
4861 }
4862
4863 LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO;
4864
4865 /* allocate memory for output vectors */
4866 arrays->V_PN = XLALCreateREAL8TimeSeries( "PN_EXPANSION_PARAMETER", &ligotimegps_zero, 0.,
4867 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4868 arrays->S1x_PN = XLALCreateREAL8TimeSeries( "SPIN1_X_COMPONENT", &ligotimegps_zero, 0.,
4869 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4870 arrays->S1y_PN = XLALCreateREAL8TimeSeries( "SPIN1_Y_COMPONENT", &ligotimegps_zero, 0.,
4871 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4872 arrays->S1z_PN = XLALCreateREAL8TimeSeries( "SPIN1_Z_COMPONENT", &ligotimegps_zero, 0.,
4873 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4874 arrays->S2x_PN = XLALCreateREAL8TimeSeries( "SPIN2_X_COMPONENT", &ligotimegps_zero, 0.,
4875 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4876 arrays->S2y_PN = XLALCreateREAL8TimeSeries( "SPIN2_Y_COMPONENT", &ligotimegps_zero, 0.,
4877 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4878 arrays->S2z_PN = XLALCreateREAL8TimeSeries( "SPIN2_Z_COMPONENT", &ligotimegps_zero, 0.,
4879 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4880 arrays->LNhatx_PN = XLALCreateREAL8TimeSeries( "LNHAT_X_COMPONENT", &ligotimegps_zero, 0.,
4881 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4882 arrays->LNhaty_PN = XLALCreateREAL8TimeSeries( "LNHAT_Y_COMPONENT", &ligotimegps_zero, 0.,
4883 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4884 arrays->LNhatz_PN = XLALCreateREAL8TimeSeries( "LNHAT_Z_COMPONENT", &ligotimegps_zero, 0.,
4885 deltaT_coarse, &lalDimensionlessUnit, copyLength);
4886
4887
4888
4889 }
4890
4891
4892 // copy coarse-grid data into fine-grid arrays
4893 memcpy(arrays->V_PN->data->data,V->data->data,(copyLength)*sizeof(REAL8));
4894 memcpy(arrays->LNhatx_PN->data->data,LNhatx->data->data,(copyLength)*sizeof(REAL8));
4895 memcpy(arrays->LNhaty_PN->data->data,LNhaty->data->data,(copyLength)*sizeof(REAL8));
4896 memcpy(arrays->LNhatz_PN->data->data,LNhatz->data->data,(copyLength)*sizeof(REAL8));
4897 memcpy(arrays->S1x_PN->data->data,S1x->data->data,(copyLength)*sizeof(REAL8));
4898 memcpy(arrays->S1y_PN->data->data,S1y->data->data,(copyLength)*sizeof(REAL8));
4899 memcpy(arrays->S1z_PN->data->data,S1z->data->data,(copyLength)*sizeof(REAL8));
4900 memcpy(arrays->S2x_PN->data->data,S2x->data->data,(copyLength)*sizeof(REAL8));
4901 memcpy(arrays->S2y_PN->data->data,S2y->data->data,(copyLength)*sizeof(REAL8));
4902 memcpy(arrays->S2z_PN->data->data,S2z->data->data,(copyLength)*sizeof(REAL8));
4903
4904
4919
4920 // check that the first frequency node returned is indeed below the fmin requested, to avoid interpolation errors. If not return an error which will trigger the fallback to MSA
4921 REAL8 fminPN=pow(arrays->V_PN->data->data[0],3.)/piGM;
4922 *fmin_PN = fminPN;
4923 if(fminPN<0.||fminPN>fmin) return(XLAL_FAILURE);
4924
4925 return status;
4926
4927 }
4928
4929
4930
4931/** Wrapper of IMRPhenomX_SpinTaylorAnglesSplinesAll: fmin and fmax are determined by the function based on the mode content and binary's parameters . Used in LALSimIMRPhenomXPHM.c */
4935 LALDict *lalParams
4936)
4937
4938{
4939 int status = XLAL_SUCCESS;
4941
4942 /* See if PNR angles were turned off in pPrec check */
4943 REAL8 buffer=pPrec->integration_buffer, fminAngles;
4944
4945 // start below fMin to avoid interpolation artefacts
4946 fminAngles = (pWF->fMin-buffer)*2./pPrec->M_MAX;
4947 // check we still pass a meaningful fmin
4948 XLAL_CHECK(fminAngles > 0., XLAL_EFUNC, "Error - %s: fMin is too low and numerical angles could not be computed.\n",__func__);
4949
4950 // If MB is on, we take advantage of the fact that we can compute angles on an array
4951
4952 if(thresholdPMB>0.)
4953 pPrec->Mfmax_angles = pWF->fRING+4.*pWF->fDAMP;
4954 else
4955 pPrec->Mfmax_angles = (MAX(pWF->MfMax,pWF->fRING+4.*pWF->fDAMP)+XLALSimIMRPhenomXUtilsHztoMf(buffer,pWF->Mtot))*2./pPrec->M_MIN;
4956 REAL8 fmaxAngles = XLALSimIMRPhenomXUtilsMftoHz(pPrec->Mfmax_angles,pWF->Mtot);
4957
4958 // we add a few bins to fmax to make sure we do not run into interpolation errors
4959 status = IMRPhenomX_SpinTaylorAnglesSplinesAll(fminAngles,fmaxAngles,pWF,pPrec,lalParams);
4960 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "%s: IMRPhenomX_SpinTaylorAnglesSplinesAll failed.",__func__);
4961
4962 status = gsl_spline_eval_e(pPrec->alpha_spline, pPrec->ftrans_MRD, pPrec->alpha_acc,&pPrec->alpha_ftrans);
4963 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "%s: could not compute alpha et the end of inspiral.",__func__);
4964
4965 status = gsl_spline_eval_e(pPrec->cosbeta_spline, pPrec->ftrans_MRD, pPrec->cosbeta_acc,&pPrec->cosbeta_ftrans);
4966 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "%s: could not compute cosbeta et the end of inspiral.",__func__);
4967
4968 status = gsl_spline_eval_e(pPrec->gamma_spline, pPrec->ftrans_MRD, pPrec->gamma_acc,&pPrec->gamma_ftrans);
4969 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "%s: could not compute gamma et the end of inspiral.",__func__);
4970
4971 return status;
4972
4973}
4974
4975
4976
4977
4978
4979/** XLAL function that evaluates the SpinTaylor Euler angles on a frequency grid passed by the user. Used in LALSimIMRPhenomX.c. */
4981 REAL8Sequence **alphaFS, /**< [out] Alpha angle frequency series [out] */
4982 REAL8Sequence **cosbetaFS, /**< [out] cos(Beta) angle frequency series [out] */
4983 REAL8Sequence **gammaFS, /**< [out] Gamma angle frequency series [out] */
4984 REAL8 m1_SI, /**< Mass of companion 1 (kg) */
4985 REAL8 m2_SI, /**< Mass of companion 2 (kg) */
4986 REAL8 s1x, /**< x component of primary spin*/
4987 REAL8 s1y, /**< y component of primary spin*/
4988 REAL8 s1z, /**< z component of primary spin */
4989 REAL8 s2x, /**< x component of secondary spin*/
4990 REAL8 s2y, /**< y component of secondary spin*/
4991 REAL8 s2z, /**< z component of secondary spin */
4992 REAL8 fmin, /**< starting GW frequency (Hz) */
4993 REAL8 fmax, /**< maximum GW frequency (Hz) */
4994 REAL8 deltaF, /**< starting GW frequency (Hz) */
4995 REAL8 fRef, /**< reference GW frequency (Hz) */
4996 REAL8 phiRef, /**< reference orbital phase (rad) */
4997 LALDict *LALparams /**< LAL Dictionary struct */
4998 )
4999 {
5000
5001 int status = XLAL_SUCCESS;
5003
5004 /* Sanity checks */
5005 if(fRef < 0.0) { XLAL_ERROR(XLAL_EDOM, "fRef must be positive or set to 0 to ignore.\n"); }
5006 if(deltaF <= 0.0) { XLAL_ERROR(XLAL_EDOM, "deltaF must be positive.\n"); }
5007 if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1_SI must be positive.\n"); }
5008 if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2_SI must be positive.\n"); }
5009 if(fmin <= 0.0) { XLAL_ERROR(XLAL_EDOM, "fmin must be positive.\n"); }
5010 if(fmax <= 0.0) { XLAL_ERROR(XLAL_EDOM, "fmax must be positive.\n"); }
5011 if(fRef > 0.0 && fRef < fmin){ XLAL_ERROR(XLAL_EDOM, "fRef must be >= fmin or =0 to use fmin.\n"); }
5012
5013 // length of frequency series expected by the user
5014 size_t iStart = (size_t) (fmin / deltaF);
5015 size_t iStop = (size_t) (fmax / deltaF) + 1;
5016 size_t output_length = iStop-iStart;
5017
5018 status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&s1x,&s1y,&s1z,&s2x,&s2y,&s2z);
5019 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
5020
5021 /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
5023 pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
5024 status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, s1z, s2z, deltaF, fRef, phiRef, fmin, fmax, 1e6*LAL_PC_SI, 0., LALparams, 0);
5025 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
5026
5028 pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
5029 pPrec->M_MIN = 2, pPrec->M_MAX = 2;
5030 status = IMRPhenomXGetAndSetPrecessionVariables(pWF,pPrec,m1_SI,m2_SI,s1x,s1y,s1z,s2x,s2y,s2z,LALparams,0);
5031 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetAndSetPrecessionVariables failed.\n");
5032
5033 // if version has been changed because of fallback, raise error
5034 XLAL_CHECK(pPrec->IMRPhenomXPrecVersion == pversion, XLAL_EFUNC, "Error: %s failed.\n",__func__);
5035
5036 //Evaluate splines for alpha and cosbeta
5038 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Error in %s: IMRPhenomX_InterpolateAlphaBeta_SpinTaylor failed.\n",__func__);
5039
5040 REAL8 alphamin=0., cosbetamin=0.;
5041 int success=XLAL_SUCCESS;
5042 REAL8 Mfmin=XLALSimIMRPhenomXUtilsHztoMf(fmin,pWF->Mtot);
5043
5044 success = gsl_spline_eval_e(pPrec->alpha_spline, Mfmin, pPrec->alpha_acc,&alphamin);
5045 success = success + gsl_spline_eval_e(pPrec->cosbeta_spline, Mfmin, pPrec->cosbeta_acc,&cosbetamin);
5046 XLAL_CHECK(XLAL_SUCCESS == success, XLAL_EFUNC, "Error: %s: could not evaluate angles at fMin.\n",__func__);
5047
5048
5049 // check if we can evaluate alpha(fref) using PN or an analytical continuation
5050
5051 if(pWF->MfRef<pPrec->ftrans_MRD)
5052 success=gsl_spline_eval_e(pPrec->alpha_spline, pWF->MfRef, pPrec->alpha_acc, &pPrec->alpha_ref);
5053 else if(pPrec->IMRPhenomXPrecVersion==320||pPrec->IMRPhenomXPrecVersion==321)
5054 pPrec->alpha_ref = alphaMRD(pWF->MfRef,pPrec->alpha_params);
5055 else
5056 success=gsl_spline_eval_e(pPrec->alpha_spline, pPrec->ftrans_MRD, pPrec->alpha_acc, &pPrec->alpha_ref);
5057
5058 XLAL_CHECK(XLAL_SUCCESS == success, XLAL_EFUNC, "Error: %s: could not evaluate angles at fRef.\n",__func__);
5059
5060
5061
5062 *alphaFS = XLALCreateREAL8Sequence(output_length);
5063 *cosbetaFS = XLALCreateREAL8Sequence(output_length);
5064 *gammaFS = XLALCreateREAL8Sequence(output_length);
5065
5066
5067 REAL8 alphaOff = pPrec->alpha0;
5068 pPrec->alpha_offset=-pPrec->alpha_ref+alphaOff;
5069
5070 // determine offset for gamma et the end
5071 (*gammaFS)->data[0] = 0.;
5072 (*alphaFS)->data[0] = alphamin-pPrec->alpha_ref+alphaOff;
5073 (*cosbetaFS)->data[0] = cosbetamin;
5074
5075
5076 REAL8 alphai=0., cosbetai=0., gammai=0.;
5077 REAL8 Mf;
5078 REAL8Sequence *frequencies = NULL;
5079 frequencies=XLALCreateREAL8Sequence(output_length);
5080 frequencies->data[0]=Mfmin;
5081
5082 for( UINT4 i = 1; i < output_length; i++ ){
5083
5084 Mf=Mfmin+i*pWF->deltaMF;
5085 frequencies->data[i]=Mf;
5086 REAL8 deltagamma=0.;
5087
5088 if(Mf<pPrec->ftrans_MRD)
5089
5090
5091 {
5092
5093 success = gsl_spline_eval_e(pPrec->alpha_spline, Mf, pPrec->alpha_acc,&alphai);
5094 success = success + gsl_spline_eval_e(pPrec->cosbeta_spline, Mf, pPrec->cosbeta_acc,&cosbetai);
5095 success = success + gamma_from_alpha_cosbeta(&deltagamma,Mf, pWF->deltaMF,pWF,pPrec);
5096 if(success != XLAL_SUCCESS)
5097 XLALPrintError("%s: Interpolation of SpinTaylor angles failed at f=%.5f)\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mf,pWF->Mtot));
5098 gammai=(*gammaFS)->data[i-1]+deltagamma;
5099
5100 (*alphaFS)->data[i] = alphai+pPrec->alpha_offset;
5101 (*cosbetaFS)->data[i] = cosbetai;
5102 (*gammaFS)-> data[i] = gammai;
5103
5104
5105 }
5106
5107
5108 else {
5109
5110 if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321){
5111
5112 (*alphaFS)->data[i]=alphaMRD(Mf,pPrec->alpha_params)+pPrec->alpha_offset;
5113 REAL8 beta_MRD=betaMRD(Mf,pWF,pPrec->beta_params);
5114 (*cosbetaFS)->data[i]=cos(beta_MRD);
5115 status=gamma_from_alpha_cosbeta(&deltagamma,Mf, pWF->deltaMF,pWF,pPrec);
5116 if(success != XLAL_SUCCESS)
5117 XLALPrintError("%s: could not integrate minimal rotation condition at f=%.5f)\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mf,pWF->Mtot));
5118 gammai=(*gammaFS)->data[i-1]+deltagamma;
5119 (*gammaFS)-> data[i] =gammai;
5120
5121 }
5122
5123 // just continue angles into RD with constant values
5124
5125 else{
5126 (*alphaFS)->data[i]=(*alphaFS)->data[i-1];
5127 (*cosbetaFS)->data[i]=(*cosbetaFS)-> data[i-1];
5128 (*gammaFS) -> data[i]=(*gammaFS)-> data[i-1];
5129 }
5130 }
5131
5132
5133 }
5134
5135
5136 pPrec->gamma_acc = gsl_interp_accel_alloc();
5137 pPrec->gamma_spline = gsl_spline_alloc(gsl_interp_cspline, output_length);
5138 gsl_spline_init(pPrec->gamma_spline, frequencies->data, (*gammaFS) -> data, output_length);
5139 pPrec->gamma_ref=gsl_spline_eval(pPrec->gamma_spline, pWF->MfRef, pPrec->gamma_acc);
5140
5141 for( UINT4 i = 0; i < output_length; i++ ) (*gammaFS) -> data[i]=(*gammaFS)-> data[i]-pPrec->gamma_ref-pPrec->epsilon0;
5142
5143 LALFree(pPrec->alpha_params);
5144 LALFree(pPrec->beta_params);
5145
5146 gsl_spline_free(pPrec->alpha_spline);
5147 gsl_interp_accel_free(pPrec->alpha_acc);
5148
5149 gsl_spline_free(pPrec->cosbeta_spline);
5150 gsl_interp_accel_free(pPrec->cosbeta_acc);
5151
5152 gsl_spline_free(pPrec->gamma_spline);
5153 gsl_interp_accel_free(pPrec->gamma_acc);
5154
5155
5156 LALFree(pWF);
5157 LALFree(pPrec);
5158 XLALDestroyREAL8Sequence(frequencies);
5159
5160
5161 return status;
5162
5163 }
5164
5165
5166/* Piecewise function to return the IMR alpha angle in PhenomXPNR (which corresponds to IMRPhenomXPrecVersion=330 */
5167
5169 REAL8 Mf, /**< Frequency in geometric units at which the function is to be evaluated */
5170 IMRPhenomXWaveformStruct *pWF, /**< PhenomX waveform struct */
5171 IMRPhenomXPrecessionStruct *pPrec /**< PhenomX precssion struct */
5172 )
5173{
5174 REAL8 alpha=0.;
5175 int status=GSL_SUCCESS;
5176
5177 // inspiral angle
5178 if(Mf<pPrec->ftrans_MRD){
5179
5180 status=gsl_spline_eval_e(pPrec->alpha_spline, Mf, pPrec->alpha_acc,&alpha);
5181 XLAL_CHECK(status == GSL_SUCCESS, XLAL_EFUNC,"%s: error in alpha interpolation at f=%.12f, when fmin_integration=%.12f, with input parameters q=%.12f, Mtot=%.12f MSUN, chi1=[%.12f,%.12f,%.12f], chi2=[%.12f,%.12f,%.12f].\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mf,pWF->Mtot),pPrec->fmin_integration,pWF->q, pWF->Mtot, pPrec->chi1x,pPrec->chi1y,pPrec->chi1z,pPrec->chi2x,pPrec->chi2y,pPrec->chi2z);
5182 alpha = alpha+pPrec->alpha_offset;
5183 }
5184
5185 // continuation past the inspiral region
5186 else{
5187
5188 if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321 || pPrec->IMRPhenomXPrecVersion==330)
5189 {
5190 alpha=alphaMRD(Mf,pPrec->alpha_params)+pPrec->alpha_offset;
5191 }
5192 else{
5193
5194 status=gsl_spline_eval_e(pPrec->alpha_spline, pPrec->ftrans_MRD, pPrec->alpha_acc,&alpha);
5195 XLAL_CHECK(status == GSL_SUCCESS, XLAL_EFUNC,"%s: error in alpha interpolation at f=%.12f, when fmin_integration=%.12f, with input parameters q=%.12f, Mtot=%.12f MSUN, chi1=[%.12f,%.12f,%.12f], chi2=[%.12f,%.12f,%.12f].\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mf,pWF->Mtot),pPrec->fmin_integration,pWF->q, pWF->Mtot, pPrec->chi1x,pPrec->chi1y,pPrec->chi1z,pPrec->chi2x,pPrec->chi2y,pPrec->chi2z);
5196
5197 }
5198
5199 }
5200
5201return(alpha);
5202
5203}
5204
5205/* declared here due to betaParams */
5207
5208/* wrapper of IMR beta angle: used for PNR angles */
5210 REAL8 Mf,
5213 const IMRPhenomX_PNR_beta_parameters *betaParams
5214 )
5215{
5216 REAL8 beta=0., cosbeta=0.;
5217 int status=GSL_SUCCESS;
5218
5219 // inspiral angle
5220 double ftrans;
5221 if (pPrec->UseMRbeta)
5222 {
5223 if( pPrec->IMRPhenomXPrecVersion != 330 )
5224 {
5225 ftrans = pPrec->ftrans_MRD;
5226 }
5227 else
5228 {
5229 ftrans = ( pPrec->ftrans_MRD <= 0.9*betaParams->Mf_beta_lower ) ? pPrec->ftrans_MRD - 0.0005 : 0.9*betaParams->Mf_beta_lower - 0.0005;
5230 }
5231 }
5232 else
5233 {
5234 ftrans = pPrec->ftrans_MRD;
5235 }
5236
5237 if(Mf<ftrans){
5238
5239 status=gsl_spline_eval_e(pPrec->cosbeta_spline, Mf, pPrec->cosbeta_acc,&cosbeta);
5240 XLAL_CHECK(status == GSL_SUCCESS, XLAL_EFUNC,"%s: error in beta interpolation at f=%.12f, when fmin_integration=%.12f, with input parameters q=%.12f, Mtot=%.12f MSUN, chi1=[%.12f,%.12f,%.12f], chi2=[%.12f,%.12f,%.12f].\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mf,pWF->Mtot),pPrec->fmin_integration,pWF->q, pWF->Mtot, pPrec->chi1x,pPrec->chi1y,pPrec->chi1z,pPrec->chi2x,pPrec->chi2y,pPrec->chi2z);
5241 cosbeta = MAX(-1, MIN(1, cosbeta));
5242 beta=acos(cosbeta);
5243 }
5244
5245 // continuation past the inspiral region
5246 else{
5247
5248 int pflag = pPrec->IMRPhenomXPrecVersion;
5249 switch( pflag )
5250 {
5251 case 310:
5252 case 311:
5253 {
5254 status=gsl_spline_eval_e(pPrec->cosbeta_spline, pPrec->ftrans_MRD, pPrec->cosbeta_acc,&cosbeta);
5255 XLAL_CHECK(status == GSL_SUCCESS, XLAL_EFUNC,"%s: error in beta interpolation at f=%.12f, when fmin_integration=%.12f, with input parameters q=%.12f, Mtot=%.12f MSUN, chi1=[%.12f,%.12f,%.12f], chi2=[%.12f,%.12f,%.12f].\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mf,pWF->Mtot),pPrec->fmin_integration,pWF->q,pWF->Mtot, pPrec->chi1x,pPrec->chi1y,pPrec->chi1z,pPrec->chi2x,pPrec->chi2y,pPrec->chi2z);
5256 cosbeta = MAX(-1, MIN(1, cosbeta));
5257 beta=acos(cosbeta);
5258
5259 break;
5260 }
5261 case 320:
5262 case 321:
5263 {
5264 beta=betaMRD(Mf,pWF,pPrec->beta_params);
5265
5266 break;
5267 }
5268 case 330:
5269 {
5270 if (pPrec->UseMRbeta)
5271 {
5272 beta = beta_connection(Mf, betaParams);
5273 }
5274 else
5275 {
5276 beta = betaMRD(Mf,pWF,pPrec->beta_params);
5277 }
5278 break;
5279 }
5280 default:
5281 {
5282 XLAL_ERROR(XLAL_EDOM, "Precessing version %d is not supported by PhenomX with SpinTaylor angles.\n",pflag);
5283 }
5284 }
5285
5286 }
5287
5288return(beta);
5289
5290}
5291
5292
5293
5295
5296 INT2Sequence *modeseq;
5297 modeseq=XLALSimInspiralModeArrayReadModes(ModeArray);
5298
5299 int nmodes=modeseq->length/2;
5300 float M_MAX=1., M_MIN=4.;
5301 for(int jj=0; jj<nmodes; jj++)
5302 {
5303 if(abs(modeseq->data[2*jj+1])>M_MAX) M_MAX=(float)abs(modeseq->data[2*jj+1]);
5304 if(abs(modeseq->data[2*jj+1])<M_MIN) M_MIN=(float)abs(modeseq->data[2*jj+1]);
5305 }
5306
5307 XLALDestroyINT2Sequence(modeseq);
5308
5309 pPrec->M_MIN = M_MIN; pPrec->M_MAX=M_MAX;
5310 return;
5311
5312}
5313
5315
5316 REAL8 seglen=XLALSimInspiralChirpTimeBound(pWF->fRef, pWF->m1_SI, pWF->m2_SI, pWF->chi1L,pWF->chi2L);
5317 REAL8 deltaFv1= 1./MAX(4.,pow(2, ceil(log(seglen)/log(2))));
5318 REAL8 deltaF = MIN(deltaFv1,0.1);
5319 REAL8 deltaMF = XLALSimIMRPhenomXUtilsHztoMf(deltaF,pWF->Mtot);
5320 return(deltaMF);
5321
5322}
5323
5324/** Core twisting up routine for SpinTaylor angles */
5326 const COMPLEX16 hAS, /**< Underlying aligned-spin IMRPhenomXAS strain */
5327 REAL8 alpha, /**< cosbeta Euler angle series */
5328 REAL8 cos_beta, /**< cosbeta Euler angle series */
5329 REAL8 gamma, /**< gamma Euler angle series */
5330 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
5331 COMPLEX16 *hp, /**< [out] h_+ polarization \f$\tilde h_+\f$ */
5332 COMPLEX16 *hc /**< [out] h_x polarization \f$\tilde h_x\f$ */
5333
5334)
5335{
5336 XLAL_CHECK(hp != NULL, XLAL_EFAULT);
5337 XLAL_CHECK(hc != NULL, XLAL_EFAULT);
5338
5339
5340 double cBetah = 0.0;
5341 double sBetah = 0.0;
5342
5343 double epsilon=-(gamma-pPrec->gamma_ref)+pPrec->epsilon0;
5344
5345 if(pPrec->IMRPhenomXPrecVersion!=310 && pPrec->IMRPhenomXPrecVersion!=311 && pPrec->IMRPhenomXPrecVersion!=320 && pPrec->IMRPhenomXPrecVersion!=321 && pPrec->IMRPhenomXPrecVersion!=330) XLAL_ERROR(XLAL_EDOM, "Error in IMRPhenomXPTwistUp22_NumericalAngles, incorrect precessing version passed (must be 310, 311, 320, 321 or 330)!\n");
5346
5347 INT4 status = 0;
5348 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
5349 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
5350
5351 /* Useful powers of the Wigner coefficients */
5352 const REAL8 cBetah2 = cBetah * cBetah;
5353 const REAL8 cBetah3 = cBetah * cBetah2;
5354 const REAL8 cBetah4 = cBetah * cBetah3;
5355 const REAL8 sBetah2 = sBetah * sBetah;
5356 const REAL8 sBetah3 = sBetah * sBetah2;
5357 const REAL8 sBetah4 = sBetah * sBetah3;
5358
5359 /*
5360 Compute the Wigner d coefficients, see Appendix A of arXiv:2004.06503
5361 d22 = Table[WignerD[{2, mp, 2}, 0, -\[Beta], 0], {mp, -2, 2}]
5362 d2m2 = Table[WignerD[{2, mp, -2}, 0, -\[Beta], 0], {mp, -2, 2}]
5363 */
5364 // d22 = {d^2_{-2,2} , d^2_{-1,2}, d^2_{0,2}, d^2_{1,2}, d^2_{2,2} }
5365 const COMPLEX16 d22[5] = {sBetah4, 2.0*cBetah*sBetah3, pPrec->sqrt6*sBetah2*cBetah2, 2.0*cBetah3*sBetah, cBetah4};
5366
5367 // d2m2 = {d^2_{-2,-2} , d^2_{-1,-2}, d^2_{0,-2}, d^2_{1,-2}, d^2_{2,-2} }
5368 const COMPLEX16 d2m2[5] = {d22[4], -d22[3], d22[2], -d22[1], d22[0]}; /* Exploit symmetry d^2_{-2,m} = (-1)^m d^2_{2,-m}*/
5369
5370 const COMPLEX16 Y2mA[5] = {pPrec->Y2m2, pPrec->Y2m1, pPrec->Y20, pPrec->Y21, pPrec->Y22};
5371
5372 /* Precompute powers of e^{i m alpha} */
5373 COMPLEX16 cexp_i_alpha = cexp(+I*alpha);
5374 COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
5375 COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
5376 COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
5377 COMPLEX16 cexp_im_alpha_l2[5] = {cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha};
5378
5379 COMPLEX16 hp_sum = 0;
5380 COMPLEX16 hc_sum = 0;
5381
5382 /* Loop over m' modes and perform the actual twisting up */
5383 for(int m=-2; m<=2; m++)
5384 {
5385 /* Transfer functions, see Eqs. 3.5-3.7 of arXiv:2004.06503 */
5386 COMPLEX16 A2m2emm = cexp_im_alpha_l2[-m+2] * d2m2[m+2] * Y2mA[m+2]; /* = cexp(I*m*alpha) * d22[m+2] * Y2mA[m+2] */
5387 COMPLEX16 A22emmstar = cexp_im_alpha_l2[m+2] * d22[m+2] * conj(Y2mA[m+2]); /* = cexp(-I*m*alpha) * d2m2[m+2] * conj(Y2mA[m+2]) */
5388 hp_sum += A2m2emm + A22emmstar;
5389 hc_sum += I*(A2m2emm - A22emmstar);
5390 }
5391
5392 /* Note that \gamma = - \epsilon */
5393 COMPLEX16 eps_phase_hP = cexp(-2.0*I*epsilon) * hAS / 2.0;
5394
5395 /* Return h_+ and h_x */
5396 *hp = eps_phase_hP * hp_sum;
5397 *hc = eps_phase_hP * hc_sum;
5398
5399 return XLAL_SUCCESS;
5400}
5401
5402
5403#ifdef __cplusplus
5404}
5405#endif
REAL8 zeta
#define LALFree(p)
const double b1
const double c1
const double b2
const double b0
const double g0
const double c2
const double c0
static double fdamp(double eta, double chi1, double chi2, double finspin)
fdamp is the complex part of the ringdown frequency 1508.07250 figure 9
#define MAX_TOL_ATAN
Tolerance used below which numbers are treated as zero for the calculation of atan2.
static double evaluate_QNMfit_fdamp22(double finalDimlessSpin)
static double evaluate_QNMfit_fring22(double finalDimlessSpin)
static double evaluate_QNMfit_fring21(double finalDimlessSpin)
static double double delta
IMRPhenomX_UsefulPowers powers_of_lalpi
int IMRPhenomX_PNR_precompute_alpha_coefficients(IMRPhenomX_PNR_alpha_parameters *alphaParams, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
This function evaluates the coefficients outlined in Sec 7A of arXiv:2107.08876 for alpha.
REAL8 IMRPhenomX_PNR_GenerateRingdownPNRBeta(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
We evaluate beta at the final Mf_beta_upper connection frequency to approximate the final value of be...
int IMRPhenomX_PNR_precompute_beta_coefficients(IMRPhenomX_PNR_beta_parameters *betaParams, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
This function evaluates the Ansatz coefficients of beta outlined in Eq.
int IMRPhenomX_PNR_BetaConnectionFrequencies(IMRPhenomX_PNR_beta_parameters *betaParams)
Here we work through the construction of the connection frequency for beta, outlined in Sec.
INT4 IMRPhenomX_PNR_GetAndSetCoPrecParams(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
REAL8 IMRPhenomX_PNR_HMInterpolationDeltaF(REAL8 f_min, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
Here we compute an appropriate deltaF to be used when generating the (2,2) angle interpolants and map...
INT4 IMRPhenomX_PNR_GetAndSetPNRVariables(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
This function computes the required single-spin quantities used to parameterize the MR tuned function...
int IMRPhenomXSetWaveformVariables(IMRPhenomXWaveformStruct *wf, const REAL8 m1_SI, const REAL8 m2_SI, const REAL8 chi1L_In, const REAL8 chi2L_In, const REAL8 deltaF, const REAL8 fRef, const REAL8 phi0, const REAL8 f_min, const REAL8 f_max, const REAL8 distance, const REAL8 inclination, LALDict *LALParams, UNUSED const UINT4 debug)
double IMRPhenomX_psiofv(const double v, const double v2, const double psi0, const double psi1, const double psi2, const IMRPhenomXPrecessionStruct *pPrec)
double IMRPhenomX_Return_Psi_MSA(double v, double v2, const IMRPhenomXPrecessionStruct *pPrec)
Get using Eq 51 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967:
double IMRPhenomX_Get_PN_tau(const double a, const double b, const IMRPhenomXPrecessionStruct *pPrec)
Internal function to computes PN spin-spin couplings.
vector IMRPhenomX_vector_PolarToCartesian(const sphpolvector v1)
double dalphaMRD(double Mf, PhenomXPalphaMRD *alpha_params)
int betaMRD_coeff(gsl_spline spline_cosb, gsl_interp_accel accel_cosb, double fmaxPN, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
Function to determine coefficients of analytical continuation of beta through MRD.
vector IMRPhenomX_vector_sum(const vector v1, const vector v2)
int IMRPhenomX_SpinTaylorAnglesSplinesAll(REAL8 fmin, REAL8 fmax, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *LALparams)
This function builds and stores splines for and in the frequency range covered by PN,...
vector IMRPhenomX_vector_cross_product(const vector v1, const vector v2)
double IMRPhenomX_Return_Psi_dot_MSA(const double v, const IMRPhenomXPrecessionStruct *pPrec)
Get using Eq 24 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967:
REAL8 beta_SpinTaylor_IMR(REAL8 Mf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, const IMRPhenomX_PNR_beta_parameters *betaParams)
vector IMRPhenomX_Return_MSA_Corrections_MSA(double v, double LNorm, double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
sphpolvector IMRPhenomX_vector_CartesianToPolar(const vector v1)
vector IMRPhenomX_Return_Spin_Evolution_Coefficients_MSA(const double LNorm, const double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
Get coefficients for Eq 21 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703....
double IMRPhenomX_Get_PN_beta(const double a, const double b, const IMRPhenomXPrecessionStruct *pPrec)
Internal function to computes the PN spin-orbit couplings.
int IMRPhenomXPSpinTaylorAnglesIMR(REAL8Sequence **alphaFS, REAL8Sequence **cosbetaFS, REAL8Sequence **gammaFS, REAL8Sequence *freqsIN, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *LALparams)
This function evaluates the SpinTaylor Euler angles on a frequency grid passed by the user.
int IMRPhenomX_InterpolateGamma_SpinTaylor(REAL8 fmin, REAL8 fmax, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
This function computes gamma from the minimal rotation condition and stores a spline for it.
double IMRPhenomX_Return_zeta_MSA(const double v, const IMRPhenomXPrecessionStruct *pPrec)
Get using Eq F5 in Appendix F of Chatziioannou et al, PRD 95, 104004, (2017):
vector IMRPhenomX_Return_Constants_d_MSA(const double LNorm, const double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
Get d constants from Appendix B (B9, B10, B11) of Chatziioannou et al, PRD 95, 104004,...
int IMRPhenomX_Initialize_MSA_System(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, int ExpansionOrder)
This function initializes all the core variables required for the MSA system.
#define MIN(a, b)
vector IMRPhenomX_Return_Roots_MSA(double LNorm, double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
Here we solve for the roots of Eq 21 in Chatziioannou et al, PRD 95, 104004, (2017),...
int IMRPhenomXPTwistUp22(const REAL8 Mf, const COMPLEX16 hAS, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, COMPLEX16 *hp, COMPLEX16 *hc)
Core twisting up routine, see Section III A of arXiv:2004.06503.
REAL8 XLALSimIMRPhenomXPNEuleralphaNNLO(const REAL8 f, const REAL8 eta, const REAL8 chi1L, const REAL8 chi2L, const REAL8 chip, const REAL8 alpha0)
External wrapper function to next-to-next-to-leading (NNLO) in spin-orbit expression for the PN Euler...
double beta_connection(double Mf, const IMRPhenomX_PNR_beta_parameters *betaParams)
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.
double IMRPhenomX_vector_L2_norm(const vector v1)
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_Return_SNorm_MSA(const double v, IMRPhenomXPrecessionStruct *pPrec)
Get norm of S, see PRD 95, 104004, (2017)
vector IMRPhenomX_vector_PolarToCartesian_components(const REAL8 mag, const REAL8 theta, const REAL8 phi)
int IMRPhenomX_InterpolateAlphaBeta_SpinTaylor(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *LALparams)
This function computes cubic splines of the alpha and beta inspiral Euler angles, which are then stor...
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.
INT4 IMRPhenomX_SetPrecessingRemnantParams(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
void IMRPhenomX_GetandSetModes(LALValue *ModeArray, IMRPhenomXPrecessionStruct *pPrec)
REAL8 XLALSimIMRPhenomXL2PNNS(const REAL8 v, const REAL8 eta)
2PN non-spinning orbital angular momentum as a function of x = v^2 = (Pi M f)^{2/3}
int IMRPhenomXPCheckMaxOpeningAngle(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
Helper function to check if maximum opening angle > pi/2 or pi/4 and issues a warning.
int IMRPhenomXWignerdCoefficients(REAL8 *cos_beta_half, REAL8 *sin_beta_half, const REAL8 v, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
REAL8 XLALSimIMRPhenomXL4PNAS(const REAL8 v, const REAL8 eta, const REAL8 chi1L, const REAL8 chi2L, const REAL8 delta)
4PN orbital angular momentum as a function of x = v^2 = (Pi M f)^{2/3}
void IMRPhenomX_rotate_y(REAL8 angle, REAL8 *vx, REAL8 *vy, REAL8 *vz)
Function to rotate vector about y axis by given angle.
vector IMRPhenomX_vector_rotate_z(const REAL8 angle, const vector v1)
Function to rotate vector about z axis by given angle.
int gamma_from_alpha_cosbeta(double *gamma, double Mf, double deltaMf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
vector IMRPhenomX_vector_scalar(const vector v1, const double a)
vector IMRPhenomX_vector_diff(const vector v1, const vector v2)
vector IMRPhenomX_Return_phi_zeta_costhetaL_MSA(const double v, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
Wrapper to generate , and at a given frequency.
void IMRPhenomX_rotate_z(const REAL8 angle, REAL8 *vx, REAL8 *vy, REAL8 *vz)
Function to rotate vector about z axis by given angle.
REAL8 XLALSimIMRPhenomXL4PNLOSIAS(const REAL8 v, const REAL8 eta, const REAL8 chi1L, const REAL8 chi2L, const REAL8 delta)
4PN orbital angular momentum as a function of x = v^2 = (Pi M f)^{2/3}
double get_deltaF_from_wfstruct(IMRPhenomXWaveformStruct *pWF)
double alphaMRD(double Mf, PhenomXPalphaMRD *alpha_params)
int IMRPhenomXPTwistUp22_NumericalAngles(const COMPLEX16 hAS, REAL8 alpha, REAL8 cos_beta, REAL8 gamma, IMRPhenomXPrecessionStruct *pPrec, COMPLEX16 *hp, COMPLEX16 *hc)
Core twisting up routine for SpinTaylor angles.
double IMRPhenomX_Get_PN_sigma(const double a, const double b, const IMRPhenomXPrecessionStruct *pPrec)
Internal function to compute PN spin-spin couplings.
int alphaMRD_coeff(gsl_spline spline_alpha, gsl_interp_accel accel_alpha, double fmaxPN, IMRPhenomXWaveformStruct *pWF, PhenomXPalphaMRD *alpha_params)
Analytical continuation for alpha angle in MRD.
REAL8 IMRPhenomX_Cartesian_to_SphericalPolar_theta(const double x, const double y, const UNUSED double z)
double IMRPhenomX_L_norm_3PN_of_v(const double v, const double v2, const double L_norm, IMRPhenomXPrecessionStruct *pPrec)
Returns the 3PN accurate orbital angular momentum as implemented in LALSimInspiralFDPrecAngles_intern...
int IMRPhenomX_InspiralAngles_SpinTaylor(PhenomXPInspiralArrays *arrays, double *fmin_PN, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, REAL8 fmin, int PrecVersion, IMRPhenomXWaveformStruct *pWF, LALDict *LALparams)
Wrapper of XLALSimInspiralSpinTaylorPNEvolveOrbit : if integration is successful, stores arrays conta...
REAL8 XLALSimIMRPhenomXL3PNAS(const REAL8 v, const REAL8 eta, const REAL8 chi1L, const REAL8 chi2L, const REAL8 delta)
3PN orbital angular momentum as a function of x = v^2 = (Pi M f)^{2/3}
double IMRPhenomX_costhetaLJ(const double L_norm, const double J_norm, const double S_norm)
Calculate (L dot J)
int IMRPhenomX_Initialize_Euler_Angles(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
Wrapper of IMRPhenomX_SpinTaylorAnglesSplinesAll: fmin and fmax are determined by the function based ...
REAL8 IMRPhenomX_Cartesian_to_SphericalPolar_phi(const double x, const double y, const UNUSED double z)
vector IMRPhenomX_vector_rotate_y(const REAL8 angle, const vector v1)
Function to rotate vector about y axis by given angle.
static REAL8TimeSeries * appendTS(REAL8TimeSeries *start, REAL8TimeSeries *end)
used in numerical evaluation of Euler angles
REAL8 XLALSimIMRPhenomXPNEulerepsilonNNLO(REAL8 f, REAL8 eta, REAL8 chi1L, REAL8 chi2L, REAL8 chip, REAL8 epsilon0)
External wrapper to NNLO PN epsilon angle.
vector IMRPhenomX_Return_Constants_c_MSA(const double v, const double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
Get c constants from Appendix B (B6, B7, B8) of Chatziioannou et al, PRD 95, 104004,...
double IMRPhenomX_JNorm_MSA(const double LNorm, IMRPhenomXPrecessionStruct *pPrec)
Get norm of J using Eq 41 of Chatziioannou et al, PRD 95, 104004, (2017)
double IMRPhenomX_Return_phiz_MSA(const double v, const double JNorm, const IMRPhenomXPrecessionStruct *pPrec)
Get using Eq 66 of Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967:
double IMRPhenomX_vector_dot_product(const vector v1, const vector v2)
REAL8 alpha_SpinTaylor_IMR(REAL8 Mf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
#define DEBUG
void Get_alphaepsilon_atfref(REAL8 *alpha_offset, REAL8 *epsilon_offset, UINT4 mprime, IMRPhenomXPrecessionStruct *pPrec, IMRPhenomXWaveformStruct *pWF)
Get alpha and epsilon offset depending of the mprime (second index of the non-precessing mode)
int IMRPhenomXWignerdCoefficients_cosbeta(REAL8 *cos_beta_half, REAL8 *sin_beta_half, const REAL8 cos_beta)
int IMRPhenomXGetAndSetPrecessionVariables(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams, INT4 debug_flag)
Function to populate the IMRPhenomXPrecessionStruct:
int XLALSimIMRPhenomXPSpinTaylorAngles(REAL8Sequence **alphaFS, REAL8Sequence **cosbetaFS, REAL8Sequence **gammaFS, REAL8 m1_SI, REAL8 m2_SI, REAL8 s1x, REAL8 s1y, REAL8 s1z, REAL8 s2x, REAL8 s2y, REAL8 s2z, REAL8 fmin, REAL8 fmax, REAL8 deltaF, REAL8 fRef, REAL8 phiRef, LALDict *LALparams)
XLAL function that evaluates the SpinTaylor Euler angles on a frequency grid passed by the user.
double betaMRD(double Mf, UNUSED IMRPhenomXWaveformStruct *pWF, PhenomXPbetaMRD *beta_params)
#define MAX(a, b)
REAL8 IMRPhenomXHM_GenerateRingdownFrequency(UINT4 ell, UINT4 emm, IMRPhenomXWaveformStruct *wf22)
void IMRPhenomXHM_Initialize_QNMs(QNMFits *qnms)
void XLALSimIMRPhenomXUnwrapArray(double *in, double *out, int len)
Function to unwrap a time series that contains an angle, to obtain a continuous time series.
INT4 XLALIMRPhenomXPCheckMassesAndSpins(REAL8 *m1, REAL8 *m2, REAL8 *chi1x, REAL8 *chi1y, REAL8 *chi1z, REAL8 *chi2x, REAL8 *chi2y, REAL8 *chi2z)
Check if m1 > m2.
REAL8 XLALSimIMRPhenomXFinalSpin2017(REAL8 eta, REAL8 chi1L, REAL8 chi2L)
Final Dimensionless Spin, X Jimenez-Forteza et al, PRD, 95, 064024, (2017), arXiv:1611....
REAL8 XLALSimIMRPhenomXatan2tol(REAL8 a, REAL8 b, REAL8 tol)
REAL8 XLALSimIMRPhenomXUtilsHztoMf(REAL8 fHz, REAL8 Mtot_Msun)
Convert from frequency in Hz to geometric frequency.
REAL8 XLALSimIMRPhenomXUtilsMftoHz(REAL8 Mf, REAL8 Mtot_Msun)
Convert from geometric frequency to Hz.
REAL8 XLALSimIMRPhenomXPrecessingFinalSpin2017(const REAL8 eta, const REAL8 chi1L, const REAL8 chi2L, const REAL8 chi_inplane)
Wrapper for the final spin in generically precessing binary black holes.
REAL8 XLALSimInspiralChirpTimeBound(REAL8 fstart, REAL8 m1, REAL8 m2, REAL8 s1, REAL8 s2)
Routine to compute an overestimate of the inspiral time from a given frequency.
int XLALSimInspiralGetApproximantFromString(const char *waveform)
Parses a waveform string to determine approximant.
static double beta(const double a, const double b, const sysq *system)
Internal function that computes the spin-orbit couplings.
static REAL8 UNUSED C2(REAL8 e0, REAL8 f0)
static REAL8 UNUSED C1(REAL8 Mtotal)
LALValue * XLALSimInspiralWaveformParamsLookupModeArray(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPHMMBandVersion(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPNPhaseOrder(LALDict *params, INT4 value)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPExpansionOrder(LALDict *params)
const char * XLALSimInspiralWaveformParamsLookupPhenomXPSpinTaylorVersion(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPhenomXPHMThresholdMband(LALDict *params, REAL8 value)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPTransPrecessionMethod(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPhenomXPNRUseTunedAngles(LALDict *params, INT4 value)
REAL8 XLALSimInspiralWaveformParamsLookupPhenomXPHMThresholdMband(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPFinalSpinMod(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPhenomXAntisymmetricWaveform(LALDict *params, INT4 value)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPConvention(LALDict *params)
REAL8 XLALSimInspiralWaveformParamsLookupPhenomXPNRInterpTolerance(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPhenomXHMThresholdMband(LALDict *params, REAL8 value)
int XLALSimInspiralWaveformParamsInsertPhenomXPNRUseTunedCoprec(LALDict *params, INT4 value)
INT4 XLALSimInspiralWaveformParamsLookupPNPhaseOrder(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPNSpinOrder(LALDict *params, INT4 value)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPNRUseTunedAngles(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXAntisymmetricWaveform(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPNTidalOrder(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPNTidalOrder(LALDict *params, INT4 value)
INT4 XLALSimInspiralWaveformParamsLookupPNSpinOrder(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPSpinTaylorCoarseFactor(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupLscorr(LALDict *params)
void XLALDestroyValue(LALValue *value)
REAL8 tmp3
REAL8 tmp5
REAL8 tmp1
REAL8 tmp6
Definition: SEOBNRv2_opt_.h:35
REAL8 tmp4
Definition: SEOBNRv2_opt_.h:7
#define fprintf
int s
Definition: bh_qnmode.c:137
REAL8 M
Definition: bh_qnmode.c:133
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
double theta
Definition: bh_sphwf.c:118
double flow
const double sn
const double u3
const double vy
const double u5
sigmaKerr data[0]
const double u2
const double vz
const double u4
const double B
const double vx
#define __attribute__(x)
#define LAL_LN2
#define LAL_PI_2
#define LAL_C_SI
#define LAL_PI
#define LAL_TWOPI
#define LAL_PC_SI
#define LAL_PI_4
#define LAL_GAMMA
#define LAL_G_SI
#define LAL_REAL4_EPS
uint64_t UINT8
#define LIGOTIMEGPSZERO
double complex COMPLEX16
double REAL8
int16_t INT2
uint32_t UINT4
int32_t INT4
void * XLALMalloc(size_t n)
void XLALFree(void *p)
Approximant
Enum that specifies the PN approximant to be used in computing the waveform.
int XLALSimInspiralSpinTaylorPNEvolveOrbit(REAL8TimeSeries **V, REAL8TimeSeries **Phi, 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 deltaT, REAL8 m1, REAL8 m2, REAL8 fStart, REAL8 fEnd, REAL8 s1x, REAL8 s1y, REAL8 s1z, REAL8 s2x, REAL8 s2y, REAL8 s2z, REAL8 lnhatx, REAL8 lnhaty, REAL8 lnhatz, REAL8 e1x, REAL8 e1y, REAL8 e1z, REAL8 lambda1, REAL8 lambda2, REAL8 quadparam1, REAL8 quadparam2, LALSimInspiralSpinOrder spinO, LALSimInspiralTidalOrder tideO, INT4 phaseO, INT4 lscorr, Approximant approx)
This function evolves the orbital equations for a precessing binary using the "TaylorT1/T5/T4" approx...
int XLALSimInspiralModeArrayIsModeActive(LALValue *modes, unsigned l, int m)
INT2Sequence * XLALSimInspiralModeArrayReadModes(LALValue *modes)
static const INT4 m
static const INT4 q
static const INT4 a
void XLALDestroyINT2Sequence(INT2Sequence *sequence)
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
COMPLEX16 XLALSpinWeightedSphericalHarmonic(REAL8 theta, REAL8 phi, int s, int l, int m)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
REAL8TimeSeries * XLALResizeREAL8TimeSeries(REAL8TimeSeries *series, int first, size_t length)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
const LALUnit lalDimensionlessUnit
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
#define XLAL_PRINT_WARNING(...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EDOM
XLAL_EINVAL
XLAL_FAILURE
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
list y
list mu
string status
end
p
x
double alpha
Definition: sgwb.c:106
REAL8 epsilon_offset_3
offset passed to modes.
REAL8 Ny_Sf
Line-of-sight vector component in L frame.
REAL8 chi2y_evolved
y-component of spin on secondary at end of SpinTaylor evolution
PhenomXPbetaMRD * beta_params
Parameters needed for analytical MRD continuation of cosbeta.
REAL8 Mfmin_integration
Minimum frequency covered by the integration of PN spin-precessing equations for SpinTaylor models
REAL8 PNR_q_window_lower
Boundary values for PNR angle transition window.
INT4 ExpansionOrder
Flag to control expansion order of MSA system of equations.
REAL8 QArunz_Jf
Component of triad basis vector Q in J frame, arXiv:0810.5336.
REAL8 S2L_pav
Precession averaged coupling , Eq.
REAL8 QAruny_Jf
Component of triad basis vector Q in J frame, arXiv:0810.5336.
REAL8 chi2x_evolved
x-component of spin on secondary at end of SpinTaylor evolution
REAL8 alpha_offset_4
offset passed to modes.
REAL8 chi1x_evolved
x-component of spin on primary at end of SpinTaylor evolution
REAL8 zeta_polarization
Angle to rotate the polarizations.
REAL8 v_0
Orbital velocity at reference frequency, .
REAL8 A2
Mass weighted pre-factor, see Eq.
REAL8 phiJ_Sf
Azimuthal angle of in the L frame.
REAL8 thetaJ_Sf
Angle between and (z-direction)
REAL8 PNR_HM_Mflow
Mf_alpha_lower stored from alphaParams struct, 2 A4 / 7 from arXiv:2107.08876.
REAL8 cosbeta_ftrans
Record value of cosbeta at end of inspiral, used in IMRPhenomXPHMTwistUp and IMRPhenomXPHMTwistUpOneM...
REAL8 alpha_ftrans
Record value of alpha at end of inspiral, used in IMRPhenomXPHMTwistUp and IMRPhenomXPHMTwistUpOneMod...
INT4 MBandPrecVersion
Flag to control multibanding for Euler angles.
REAL8 thetaJN
Angle between J0 and line of sight (z-direction)
REAL8 alpha_offset_3
offset passed to modes.
REAL8 epsilon_offset_4
offset passed to modes.
REAL8 chi1y_evolved
y-component of spin on primary at end of SpinTaylor evolution
REAL8 alpha_offset_1
offset passed to modes.
REAL8 Spl2
Largest root of polynomial , Eq.
REAL8 Ny_Jf
Line-of-sight vector component in J frame.
REAL8 costheta_singleSpin
Polar angle of effective single spin, Eq.
REAL8 PArunz_Jf
Component of triad basis vector P in J frame, arXiv:0810.5336.
REAL8 S2Lsq_pav
Precession averaged coupling , Eq.
REAL8 Xx_Sf
Component of triad basis vector X in L frame.
REAL8 gamma_ref
Record value of gamma at f_ref.
REAL8 fmin_integration
Minimum frequency covered by the integration of PN spin-precessing equations for SpinTaylor models
REAL8 S32
Third root of polynomial , Eq.
IMRPhenomXWaveformStruct * pWF22AS
REAL8 gamma_ftrans
Record value of gamma at end of inspiral, used in IMRPhenomXPHMTwistUp and IMRPhenomXPHMTwistUpOneMod...
REAL8 PArunx_Jf
Component of triad basis vector P in J frame, arXiv:0810.5336.
REAL8 Nx_Jf
Line-of-sight vector component in J frame.
REAL8 QArunx_Jf
Component of triad basis vector Q in J frame, arXiv:0810.5336.
INT4 IMRPhenomXPrecVersion
Flag to set version of Euler angles used.
REAL8 Smi2
Smallest root of polynomial , Eq.
REAL8 Nz_Jf
Line-of-sight vector component in LJ frame.
INT4 MSA_ERROR
Flag to track errors in initialization of MSA system.
REAL8 chi_singleSpin_antisymmetric
magnitude of effective single spin of a two spin system for the antisymmetric waveform
REAL8 PAruny_Jf
Component of triad basis vector P in J frame, arXiv:0810.5336.
REAL8 Nz_Sf
Line-of-sight vector component in L frame.
REAL8 chi2z_evolved
z-component of spin on secondary at end of SpinTaylor evolution
REAL8 chi1z_evolved
z-component of spin on primary at end of SpinTaylor evolution
PhenomXPalphaMRD * alpha_params
Parameters needed for analytical MRD continuation of alpha.
REAL8 S1S2_pav
Precession averaged coupling , Eq.
REAL8 S1Lsq_pav
Precession averaged coupling , Eq.
REAL8 Xy_Sf
Component of triad basis vector X in L frame.
REAL8 chi_singleSpin
Magnitude of effective single spin used for tapering two-spin angles, Eq.
REAL8 theta_antisymmetric
Polar angle effective single spin for antisymmetric waveform.
REAL8 epsilon_offset_1
offset passed to modes.
REAL8 A1
Mass weighted pre-factor, see Eq.
REAL8 phi0_aligned
Initial phase to feed the underlying aligned-spin model.
REAL8 v_0_2
Orbital velocity at reference frequency squared, .
REAL8 integration_buffer
Buffer region for integration of SpinTaylor equations: added so that interpolated angles cover the fr...
REAL8 alpha_ref
Record value of alpha at f_ref.
REAL8 Xz_Sf
Component of triad basis vector X in L frame.
REAL8 Nx_Sf
Line-of-sight vector component in L frame.
REAL8 PNR_HM_Mfhigh
Mf_beta_lower stored from betaParams struct, Eq.
REAL8 S1LS2L_pav
Precession averaged coupling , Eq.
REAL8 S1L_pav
Precession averaged coupling , Eq.
REAL8 costheta_final_singleSpin
Polar angle of approximate final spin, see technical document FIXME: add reference.
UINT4 length
INT2 * data
fitQNM_fdamp fdamp_lm[N_HIGHERMODES_IMPLEMENTED]
REAL8Sequence * data
LIGOTimeGPS epoch
REAL8 * data
double V
Definition: unicorn.c:25
double deltaT
Definition: unicorn.c:24