LALSimulation  5.4.0.1-89842e6
LALSimIMRPhenomXPHM.c
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2019 Cecilio García Quirós, 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 #include <lal/LALSimIMR.h>
21 #include <lal/SphericalHarmonics.h>
22 #include <lal/Sequence.h>
23 #include <lal/Date.h>
24 #include <lal/Units.h>
25 #include <lal/LALConstants.h>
26 #include <lal/FrequencySeries.h>
27 #include <lal/LALDatatypes.h>
28 #include <lal/LALStdlib.h>
29 #include <lal/XLALError.h>
30 
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 
35 #ifndef _OPENMP
36 #define omp ignore
37 #endif
38 
39 #define L_MAX 4
40 
41 #ifndef PHENOMXHMDEBUG
42 #define DEBUG 0
43 #define PHENOMXDEBUG 0
44 #else
45 #define DEBUG 1 //print debugging info
46 #define PHENOMXDEBUG 1
47 #endif
48 
49 #include "LALSimIMRPhenomXPHM.h"
50 #include "LALSimIMRPhenomX_PNR.h"
52 
53 #define MIN(a,b) (((a)<(b))?(a):(b))
54 #define MAX(a,b) (((a)>(b))?(a):(b))
55 
56 
57 /* Generic routine for twisting up higher multipole models */
58 static int IMRPhenomXPHMTwistUp(
59  const REAL8 Mf, /**< Frequency (Hz) */
60  const COMPLEX16 hHM, /**< Underlying aligned-spin IMRPhenomXAS waveform*/
61  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
62  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
63  INT4 ell, /**< l index of the (l,m) mode */
64  INT4 emmprime, /**< m index of the (l,m) mode */
65  COMPLEX16 *hp, /**< [out] h_+ polarization \f$\tilde h_+\f$ */
66  COMPLEX16 *hc /**< [out] h_x polarization \f$\tilde h_x\f$ */
67 );
68 
69 /*
70  Core twisting up routine for one single mode.
71  Twist the waveform in the precessing L-frame to the inertial J-frame for one frequency point.
72  This function will be inside a loop of frequencies insid a loop over mprime >0 up to l.
73 */
75  const REAL8 Mf, /**< Frequency (Mf geometric units) */
76  const COMPLEX16 hlmprime, /**< Underlying aligned-spin IMRPhenomXHM waveform. The loop is with mprime positive, but the mode has to be the negative one for positive frequencies.*/
77  const COMPLEX16 hlmprime_antisym, /**< antisymmetric waveform in the co-precessing frame */
78  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
79  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
80  UINT4 l, /**< l index of the (l,m) (non-)precessing mode */
81  UINT4 mprime, /**< second index of the (l,mprime) non-precessing mode */
82  INT4 m, /**< second index of the (l,m) precessing mode */
83  COMPLEX16Sequence *hlminertial /**< [out] hlm for one frequency in the inertial frame (precessing mode) */
84 );
85 
86 //This is a wrapper function for adding higher modes to the ModeArray
87 LALDict *IMRPhenomXPHM_setup_mode_array(LALDict *lalParams);
88 
89 
90 static int IMRPhenomXPHM_hplushcross(
91  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency domain h+ GW strain */
92  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency domain hx GW strain */
93  const REAL8Sequence *freqs_In, /**< Frequency array to evaluate the model. (fmin, fmax) for equally spaced grids. */
94  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
95  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
96  LALDict *lalParams /**< LAL Dictionary Structure */
97 );
98 
100  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency domain h+ GW strain */
101  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency domain hx GW strain */
102  const REAL8Sequence *freqs_In, /**< Frequency array to evaluate the model. (fmin, fmax) for equally spaced grids. */
103  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
104  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
105  LALDict *lalParams /**< LAL Dictionary Structure */
106 );
107 
108 static int IMRPhenomXPHM_OneMode(
109  COMPLEX16FrequencySeries **hlmpos, /**< [out] Frequency domain hlm GW strain inertial frame positive frequencies */
110  COMPLEX16FrequencySeries **hlmneg, /**< [out] Frequency domain hlm GW strain inertial frame negative frequencies */
111  const REAL8Sequence *freqs_In, /**< Input frequency grid (Hz) */
112  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
113  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
114  UINT4 ell, /**< l index of the (l,m) precessing mode */
115  INT4 m, /**< m index of the (l,m) precessing mode */
116  LALDict *lalParams /**< LAL Dictionary Structure */
117 );
118 
119 /* Return the 3 Post-Newtonian Euler angles evaluated in (2*pi*Mf/mprime) */
120 static int Get_alpha_beta_epsilon(
121  REAL8 *alpha, /**< [out] Azimuthal angle of L w.r.t J */
122  REAL8 *cBetah, /**< [out] Cosine of polar angle between L and J */
123  REAL8 *sBetah, /**< [out] Sine of polar angle between L and J */
124  REAL8 *epsilon, /**< [out] Minus the third Euler angle (-gamma) describing L w.r.t J, fixed by minimal rotation condition */
125  INT4 mprime, /**< Second index of the non-precesssing mode (l, mprime) */
126  REAL8 Mf, /**< Frequency geometric units */
127  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precessing structure*/
128  IMRPhenomXWaveformStruct *pWF /**< IMRPhenomX Waveform structure*/
129 );
130 
131 /* Return the offset at reference frequency for alpha and epsilon Euler angles for a particular non-precessing mode.
132  alpha_offset_mprime = alpha(2*pi*MfRef/mprime) - alpha0. Used for Pv2 and Pv3 angles. */
133 static double Get_alpha_epsilon_offset(
134  REAL8 *alpha_offset_mprime, /**< [out] Offset alpha angle at reference frequency */
135  REAL8 *epsilon_offset_mprime, /**< [out] Offset epsilon angle at reference frequency */
136  INT4 mprime, /**< Second index of the non-precesssing mode (l, mprime) */
137  IMRPhenomXPrecessionStruct *pPrec /**< IMRPhenomXP Precessing structure*/
138 );
139 
140 
141 
142 /**
143  * @addtogroup LALSimIMRPhenomX_c
144  * @{
145  *
146  * @name Routines for IMRPhenomXPHM
147  * @{
148  *
149  */
150 
151 
152 /*********************************************/
153 /* */
154 /* MULTIMODE PRECESSING FUNCTIONS */
155 /* */
156 /*********************************************/
157 
158 /** Returns hptilde and hctilde of the multimode precessing waveform for positive frequencies in an equally spaced grid.
159 
160 This is the default function used when calling ChooseFDWaveform.
161 
162 It computes the non-precessing modes just once and do the twisting up according to eqs. 3.5-3.7 in the Precessing paper.
163 
164 It is just a wrapper of the internal function that actually carries out the calculation: IMRPhenomXPHM_hplushcross.
165 
166 */
168  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency-domain waveform h+ */
169  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency-domain waveform hx */
170  REAL8 m1_SI, /**< mass of companion 1 (kg) */
171  REAL8 m2_SI, /**< mass of companion 2 (kg) */
172  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
173  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
174  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
175  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
176  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
177  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
178  REAL8 distance, /**< Distance of source (m) */
179  REAL8 inclination, /**< inclination of source (rad) */
180  REAL8 phiRef, /**< Orbital phase (rad) at reference frequency */
181  REAL8 f_min, /**< Starting GW frequency (Hz) */
182  REAL8 f_max, /**< Ending GW frequency (Hz); Defaults to Mf = 0.3 if no f_max is specified. */
183  REAL8 deltaF, /**< Sampling frequency (Hz). To use non-uniform frequency grid, set deltaF <= 0. */
184  REAL8 fRef_In, /**< Reference frequency (Hz) */
185  LALDict *lalParams /**< LAL Dictionary struct */
186 )
187 {
188  /* Variable to check correct calls to functions. */
189  UINT4 status;
190 
191  /*
192  Set initial values of masses and z-components of spins to pass to IMRPhenomXSetWaveformVariables() so it can swap the
193  matter parameters (and masses and spins) appropriately if m1 < m2, since the masses and spin vectors will also be
194  swapped by XLALIMRPhenomXPCheckMassesAndSpins() below.
195  */
196  const REAL8 m1_SI_init = m1_SI;
197  const REAL8 m2_SI_init = m2_SI;
198  const REAL8 chi1z_init = chi1z;
199  const REAL8 chi2z_init = chi2z;
200 
201  /* Check if m1 > m2, swap the bodies otherwise. */
202  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
203  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
204 
205  #if DEBUG == 1
206  printf("fRef_In : %e\n",fRef_In);
207  printf("m1_SI : %e\n",m1_SI);
208  printf("m2_SI : %e\n",m2_SI);
209  printf("chi1L : %e\n",chi1z);
210  printf("chi2L : %e\n",chi2z);
211  printf("phiRef : %e\n",phiRef);
212  printf("Prec V. : %d\n\n",XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams));
213  printf("Performing sanity checks...\n");
214  #endif
215 
216  /* Perform initial sanity checks */
217  XLAL_CHECK(NULL != hptilde, XLAL_EFAULT, "Error: hptilde already defined. \n");
218  XLAL_CHECK(NULL != hctilde, XLAL_EFAULT, "Error: hctilde already defined. \n");
219  XLAL_CHECK(fRef_In >= 0, XLAL_EFUNC, "Error: fRef_In must be positive or set to 0 to ignore. \n");
220  XLAL_CHECK(deltaF > 0, XLAL_EFUNC, "Error: deltaF must be positive and greater than 0. \n");
221  XLAL_CHECK(m1_SI > 0, XLAL_EFUNC, "Error: m1 must be positive and greater than 0. \n");
222  XLAL_CHECK(m2_SI > 0, XLAL_EFUNC, "Error: m2 must be positive and greater than 0. \n");
223  XLAL_CHECK(f_min > 0, XLAL_EFUNC, "Error: f_min must be positive and greater than 0. \n");
224  XLAL_CHECK(f_max >= 0, XLAL_EFUNC, "Error: f_max must be non-negative. \n");
225  XLAL_CHECK(distance > 0, XLAL_EFUNC, "Error: Distance must be positive and greater than 0. \n");
226 
227  /*
228  Perform a basic sanity check on the region of the parameter space in which model is evaluated.
229  Behaviour is as follows, consistent with the choices for IMRPhenomXAS/IMRPhenomXHM
230  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
231  - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
232  - For mass ratios > 1000: throw a hard error that model is not valid.
233  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
234 
235  */
236  REAL8 mass_ratio;
237  if(m1_SI > m2_SI)
238  {
239  mass_ratio = m1_SI / m2_SI;
240  }
241  else
242  {
243  mass_ratio = m2_SI / m1_SI;
244  }
245  if(mass_ratio > 20.0 ) { XLAL_PRINT_WARNING("Warning: Extrapolating outside of Numerical Relativity calibration domain. NNLO angles may become pathological at large mass ratios.\n"); }
246  if(mass_ratio > 1000. && fabs(mass_ratio - 1000) > 1e-12) { XLAL_ERROR(XLAL_EDOM, "ERROR: Model not valid at mass ratios beyond 1000.\n"); } // The 1e-12 is to avoid rounding errors
247  if(fabs(chi1z) > 0.99 || fabs(chi2z) > 0.99) { XLAL_PRINT_WARNING("Warning: Extrapolating to extremal spins, model is not trusted.\n"); }
248 
249  /* Check that the modes chosen are available for the model */
250  XLAL_CHECK(check_input_mode_array(lalParams) == XLAL_SUCCESS, XLAL_EFAULT, "Not available mode chosen.\n");
251 
252  /* If no reference frequency is given, set it to the starting gravitational wave frequency. */
253  REAL8 fRef = (fRef_In == 0.0) ? f_min : fRef_In;
254 
255  /* Use an auxiliar laldict to not overwrite the input argument */
256  LALDict *lalParams_aux;
257  /* setup mode array */
258  if (lalParams == NULL)
259  {
260  lalParams_aux = XLALCreateDict();
261  }
262  else{
263  lalParams_aux = XLALDictDuplicate(lalParams);
264  }
265 
266  #if DEBUG == 1
267  printf("\n\n **** Initializing waveform struct... **** \n\n");
268  #endif
269 
270  /* Initialize the useful powers of LAL_PI */
272  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
273 
274  /* Initialize IMRPhenomX Waveform struct and check that it initialized correctly */
276  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
277  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI_init, m2_SI_init, chi1z_init, chi2z_init, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, PHENOMXDEBUG);
278  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
279 
280  /*
281  Create a REAL8 frequency series.
282  Use fLow, fHigh, deltaF to compute frequency sequence. Only pass the boundaries (fMin, fMax).
283  */
285  freqs->data[0] = pWF->fMin;
286  freqs->data[1] = pWF->f_max_prime;
287 
289  XLAL_CHECK(
290  (fRef >= pWF->fMin)&&(fRef <= pWF->f_max_prime),
291  XLAL_EFUNC,
292  "Error: f_min = %.2f <= fRef = %.2f < f_max = %.2f required when using tuned angles.\n",pWF->fMin,fRef,pWF->f_max_prime);
293  }
294 
295  #if DEBUG == 1
296  printf("\n\n **** Initializing precession struct... **** \n\n");
297  #endif
298 
299  /* Initialize IMRPhenomX Precession struct and check that it generated successfully */
301  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
302 
303  lalParams_aux = IMRPhenomXPHM_setup_mode_array(lalParams_aux);
304 
306  pWF,
307  pPrec,
308  m1_SI,
309  m2_SI,
310  chi1x,
311  chi1y,
312  chi1z,
313  chi2x,
314  chi2y,
315  chi2z,
316  lalParams_aux,
318  );
319  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
320 
321 
322  #if DEBUG == 1
323  printf("\n\n **** Calling IMRPhenomXPHM_hplushcross... **** \n\n");
324  #endif
325 
326  /* We now call the core IMRPhenomXPHM waveform generator */
327  status = IMRPhenomXPHM_hplushcross(hptilde, hctilde, freqs, pWF, pPrec, lalParams_aux);
328  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_hplushcross failed to generate IMRPhenomXHM waveform.\n");
329 
330 
331 
332  #if DEBUG == 1
333  printf("\n\n **** Call to IMRPhenomXPHM_hplus_hcross complete. **** \n\n");
334  #endif
335 
336 
337  /* Resize hptilde, hctilde */
338  REAL8 lastfreq;
339  if (pWF->f_max_prime < pWF->fMax)
340  {
341  /* The user has requested a higher f_max than Mf = fCut.
342  Resize the frequency series to fill with zeros beyond the cutoff frequency. */
343  lastfreq = pWF->fMax;
344  XLAL_PRINT_WARNING("The input f_max = %.2f Hz is larger than the internal cutoff of Mf=0.3 (%.2f Hz). Array will be filled with zeroes between these two frequencies.\n", pWF->fMax, pWF->f_max_prime);
345  }
346  else{ // We have to look for a power of 2 anyway.
347  lastfreq = pWF->f_max_prime;
348  }
349  // We want to have the length be a power of 2 + 1
350  size_t n_full = NextPow2(lastfreq / deltaF) + 1;
351  size_t n = (*hptilde)->data->length;
352 
353  /* Resize the COMPLEX16 frequency series */
354  *hptilde = XLALResizeCOMPLEX16FrequencySeries(*hptilde, 0, n_full);
355  XLAL_CHECK (*hptilde, XLAL_ENOMEM, "Failed to resize h_+ COMPLEX16FrequencySeries of length %zu (for internal fCut=%f) to new length %zu (for user-requested f_max=%f).", n, pWF->fCut, n_full, pWF->fMax );
356 
357  /* Resize the COMPLEX16 frequency series */
358  *hctilde = XLALResizeCOMPLEX16FrequencySeries(*hctilde, 0, n_full);
359  XLAL_CHECK (*hctilde, XLAL_ENOMEM, "Failed to resize h_x COMPLEX16FrequencySeries of length %zu (for internal fCut=%f) to new length %zu (for user-requested f_max=%f).", n, pWF->fCut, n_full, pWF->fMax );
360 
361 
362  /* Free memory */
363  LALFree(pWF);
364  LALFree(pPrec);
366  XLALDestroyDict(lalParams_aux);
367 
368  return XLAL_SUCCESS;
369 }
370 
371 /** Returns hptilde and hctilde of the multimode precessing waveform for positive frequencies in an equally space grid.
372 
373 This function is equivalent to XLALSimIMRPhenomXPHM, gives the same result but it is computed by first calling
374 all the precessing indiviudal modes in the J-frame and then sum them all with Ylm(thetaJN, 0) since the J-frame is aligned
375 such that the line of sight N is in the x-z plane of the J-frame. See appendix C and in particular eq. C8 in Precessing paper.
376 
377 This function is slower since it has to compute the non-precessing modes again for every precessing mode.
378 
379 It is just a wrapper of the internal function that actually carries out the calculation: IMRPhenomXPHM_hplushcross_from_modes.
380 
381 */
383  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency-domain waveform h+ */
384  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency-domain waveform hx */
385  REAL8 m1_SI, /**< mass of companion 1 (kg) */
386  REAL8 m2_SI, /**< mass of companion 2 (kg) */
387  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
388  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
389  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
390  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
391  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
392  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
393  REAL8 distance, /**< Distance of source (m) */
394  REAL8 inclination, /**< inclination of source (rad) */
395  REAL8 phiRef, /**< Orbital phase (rad) at reference frequency */
396  REAL8 f_min, /**< Starting GW frequency (Hz) */
397  REAL8 f_max, /**< Ending GW frequency (Hz); Defaults to Mf = 0.3 if no f_max is specified. */
398  REAL8 deltaF, /**< Sampling frequency (Hz). To use non-uniform frequency grid, set deltaF <= 0. */
399  REAL8 fRef_In, /**< Reference frequency (Hz) */
400  LALDict *lalParams /**< LAL Dictionary struct */
401 )
402 {
403  /* Variable to check correct calls to functions. */
404  UINT4 status;
405 
406  /* Check if m1 > m2, swap the bodies otherwise. */
407  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
408  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
409 
410  #if DEBUG == 1
411  printf("fRef_In : %e\n",fRef_In);
412  printf("m1_SI : %e\n",m1_SI);
413  printf("m2_SI : %e\n",m2_SI);
414  printf("chi1L : %e\n",chi1z);
415  printf("chi2L : %e\n",chi2z);
416  printf("phiRef : %e\n",phiRef);
417  printf("Prec V. : %d\n\n",XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams));
418  printf("Performing sanity checks...\n");
419  #endif
420 
421  /* Perform initial sanity checks */
422  XLAL_CHECK(NULL != hptilde, XLAL_EFAULT, "Error: hptilde already defined. \n");
423  XLAL_CHECK(NULL != hctilde, XLAL_EFAULT, "Error: hctilde already defined. \n");
424  XLAL_CHECK(fRef_In >= 0, XLAL_EFUNC, "Error: fRef_In must be positive or set to 0 to ignore. \n");
425  XLAL_CHECK(deltaF > 0, XLAL_EFUNC, "Error: deltaF must be positive and greater than 0. \n");
426  XLAL_CHECK(m1_SI > 0, XLAL_EFUNC, "Error: m1 must be positive and greater than 0. \n");
427  XLAL_CHECK(m2_SI > 0, XLAL_EFUNC, "Error: m2 must be positive and greater than 0. \n");
428  XLAL_CHECK(f_min > 0, XLAL_EFUNC, "Error: f_min must be positive and greater than 0. \n");
429  XLAL_CHECK(f_max >= 0, XLAL_EFUNC, "Error: f_max must be non-negative. \n");
430  XLAL_CHECK(distance > 0, XLAL_EFUNC, "Error: Distance must be positive and greater than 0. \n");
431 
432  /*
433  Perform a basic sanity check on the region of the parameter space in which model is evaluated.
434  Behaviour is as follows, consistent with the choices for IMRPhenomXAS/IMRPhenomXHM
435  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
436  - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
437  - For mass ratios > 1000: throw a hard error that model is not valid.
438  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
439 
440  */
441  REAL8 mass_ratio;
442  if(m1_SI > m2_SI)
443  {
444  mass_ratio = m1_SI / m2_SI;
445  }
446  else
447  {
448  mass_ratio = m2_SI / m1_SI;
449  }
450  if(mass_ratio > 20.0 ) { XLAL_PRINT_WARNING("Warning: Extrapolating outside of Numerical Relativity calibration domain. NNLO angles may become pathological at large mass ratios.\n"); }
451  if(mass_ratio > 1000. && fabs(mass_ratio - 1000) > 1e-12) { XLAL_ERROR(XLAL_EDOM, "ERROR: Model not valid at mass ratios beyond 1000."); } // The 1e-12 is to avoid rounding errors
452  if(fabs(chi1z) > 0.99 || fabs(chi2z) > 0.99) { XLAL_PRINT_WARNING("Warning: Extrapolating to extremal spins, model is not trusted."); }
453 
454  /* Check that the modes chosen are available for the model */
455  XLAL_CHECK(check_input_mode_array(lalParams) == XLAL_SUCCESS, XLAL_EFAULT, "Not available mode chosen.\n");
456 
457  /* If no reference frequency is given, set it to the starting gravitational wave frequency */
458  REAL8 fRef = (fRef_In == 0.0) ? f_min : fRef_In;
459 
460  /* Use an auxiliar laldict to not overwrite the input argument */
461  LALDict *lalParams_aux;
462  /* setup mode array */
463  if (lalParams == NULL)
464  {
465  lalParams_aux = XLALCreateDict();
466  }
467  else{
468  lalParams_aux = XLALDictDuplicate(lalParams);
469  }
470 
471  #if DEBUG == 1
472  printf("\n\n **** Initializing waveform struct... **** \n\n");
473  #endif
474 
475  /* Initialize the useful powers of LAL_PI */
477  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
478 
479  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
481  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
482  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1z, chi2z, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, PHENOMXDEBUG);
483  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
484 
485  /*
486  Create a REAL8 frequency series.
487  Use fLow, fHigh, deltaF to compute frequency sequence. Only pass the boundaries (fMin, fMax).
488  */
490  freqs->data[0] = pWF->fMin;
491  freqs->data[1] = pWF->f_max_prime;
492 
494  XLAL_CHECK(
495  (fRef >= pWF->fMin)&&(fRef <= pWF->f_max_prime),
496  XLAL_EFUNC,
497  "Error: f_min = %.2f <= fRef = %.2f < f_max = %.2f required when using tuned angles.\n",pWF->fMin,fRef,pWF->f_max_prime);
498  }
499 
500  #if DEBUG == 1
501  printf("\n\n **** Initializing precession struct... **** \n\n");
502  #endif
503 
504  /* Initialize IMRPhenomX Precession struct and check that it generated successfully. */
506  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
507 
508  lalParams_aux = IMRPhenomXPHM_setup_mode_array(lalParams_aux);
509 
511  pWF,
512  pPrec,
513  m1_SI,
514  m2_SI,
515  chi1x,
516  chi1y,
517  chi1z,
518  chi2x,
519  chi2y,
520  chi2z,
521  lalParams_aux,
523  );
524  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
525 
526 
527  #if DEBUG == 1
528  printf("\n\n **** Calling IMRPhenomXPHM_hplushcross_from_modes... **** \n\n");
529  #endif
530 
531  /* We now call the core IMRPhenomXPHMFromModes waveform generator */
532  status = IMRPhenomXPHM_hplushcross_from_modes(hptilde, hctilde, freqs, pWF, pPrec, lalParams_aux);
533  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_hplushcross_from_modes failed to generate IMRPhenomXPHM waveform.");
534 
535  #if DEBUG == 1
536  printf("\n\n **** Call to IMRPhenomXPHM_from _modes complete. **** \n\n");
537  #endif
538 
539  /* Resize hptilde, hctilde */
540  REAL8 lastfreq;
541  if (pWF->f_max_prime < pWF->fMax)
542  {
543  /* The user has requested a higher f_max than Mf = fCut.
544  Resize the frequency series to fill with zeros beyond the cutoff frequency. */
545  lastfreq = pWF->fMax;
546  XLAL_PRINT_WARNING("The input f_max = %.2f Hz is larger than the internal cutoff of Mf=0.3 (%.2f Hz). Array will be filled with zeroes between these two frequencies.\n", pWF->fMax, pWF->f_max_prime);
547  }
548  else{ // We have to look for a power of 2 anyway.
549  lastfreq = pWF->f_max_prime;
550  }
551  // We want to have the length be a power of 2 + 1
552  size_t n_full = NextPow2(lastfreq / deltaF) + 1;
553  size_t n = (*hptilde)->data->length;
554 
555  /* Resize the COMPLEX16 frequency series */
556  *hptilde = XLALResizeCOMPLEX16FrequencySeries(*hptilde, 0, n_full);
557  XLAL_CHECK (*hptilde, XLAL_ENOMEM, "Failed to resize h_+ COMPLEX16FrequencySeries of length %zu (for internal fCut=%f) to new length %zu (for user-requested f_max=%f).", n, pWF->fCut, n_full, pWF->fMax );
558 
559  /* Resize the COMPLEX16 frequency series */
560  *hctilde = XLALResizeCOMPLEX16FrequencySeries(*hctilde, 0, n_full);
561  XLAL_CHECK (*hctilde, XLAL_ENOMEM, "Failed to resize h_x COMPLEX16FrequencySeries of length %zu (for internal fCut=%f) to new length %zu (for user-requested f_max=%f).", n, pWF->fCut, n_full, pWF->fMax );
562 
563 
564  /* Free memory */
565  LALFree(pWF);
566  LALFree(pPrec);
568  XLALDestroyDict(lalParams_aux);
569 
570  return XLAL_SUCCESS;
571 }
572 
573 
574 /**
575  * Returns hptilde and hctilde as a complex frequency series with entries exactly at the frequencies specified in
576  * the REAL8Sequence *freqs (which can be unequally spaced). No zeros are added. Assumes positive frequencies.
577  * This is the function used when calling ChooseFDWaveformSequence.
578  * It is a wrapper that calls the function IMRPhenomXPHM_hplushcross or IMRPhenomXPHM_hplushcross_from_modes
579  * and we can change which one is used by modifying the option 'UseModes' in the LAL dictionary.
580  * No multibanding is used since this technique is only for equal-spaced frequency grids.
581  */
583  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency-domain waveform h+ */
584  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency-domain waveform hx */
585  const REAL8Sequence *freqs, /**< Input Frequency series (Hz) */
586  REAL8 m1_SI, /**< mass of companion 1 (kg) */
587  REAL8 m2_SI, /**< mass of companion 2 (kg) */
588  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
589  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
590  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
591  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
592  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
593  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
594  REAL8 distance, /**< Distance of source (m) */
595  REAL8 inclination, /**< inclination of source (rad) */
596  REAL8 phiRef, /**< Orbital phase (rad) at reference frequency */
597  REAL8 fRef_In, /**< Reference frequency (Hz) */
598  LALDict *lalParams /**< LAL Dictionary struct */
599 )
600 {
601  /* Variable to check correct calls to functions. */
602  INT4 status;
603 
604  /* Check if m1 > m2, swap the bodies otherwise. */
605  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
606  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
607 
608  /* Perform initial sanity checks */
609  XLAL_CHECK(NULL != hptilde, XLAL_EFAULT, "Error: hptilde already defined. \n");
610  XLAL_CHECK(NULL != hctilde, XLAL_EFAULT, "Error: hctilde already defined. \n");
611  XLAL_CHECK(NULL != freqs, XLAL_EFAULT, "Error: Input freq array must be defined. \n");
612  XLAL_CHECK(fRef_In >= 0, XLAL_EFUNC, "Error: fRef must be positive and greater than 0. \n");
613  XLAL_CHECK(m1_SI > 0, XLAL_EFUNC, "Error: m1 must be positive and greater than 0. \n");
614  XLAL_CHECK(m2_SI > 0, XLAL_EFUNC, "Error: m2 must be positive and greater than 0. \n");
615  XLAL_CHECK(distance > 0, XLAL_EFUNC, "Error: Distance must be positive and greater than 0. \n");
616 
617  /*
618  Perform a basic sanity check on the region of the parameter space in which model is evaluated.
619  Behaviour is as follows, consistent with the choices for IMRPhenomXAS/IMRPhenomXHM
620  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
621  - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
622  - For mass ratios > 1000: throw a hard error that model is not valid.
623  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
624 
625  */
626  REAL8 mass_ratio;
627  if(m1_SI > m2_SI)
628  {
629  mass_ratio = m1_SI / m2_SI;
630  }
631  else
632  {
633  mass_ratio = m2_SI / m1_SI;
634  }
635  if(mass_ratio > 20.0 ) { XLAL_PRINT_INFO("Warning: Extrapolating outside of Numerical Relativity calibration domain."); }
636  if(mass_ratio > 1000. && fabs(mass_ratio - 1000) > 1e-12) { XLAL_ERROR(XLAL_EDOM, "ERROR: Model not valid at mass ratios beyond 1000."); } // The 1e-12 is to avoid rounding errors
637  if(fabs(chi1z) > 0.99 || fabs(chi2z) > 0.99) { XLAL_PRINT_INFO("Warning: Extrapolating to extremal spins, model is not trusted."); }
638 
639  /* Check that the modes chosen are available for the model */
640  XLAL_CHECK(check_input_mode_array(lalParams) == XLAL_SUCCESS, XLAL_EFAULT, "Not available mode chosen.\n");
641 
642  /* If no reference frequency is given, set it to the starting gravitational wave frequency */
643  REAL8 fRef = (fRef_In == 0.0) ? freqs->data[0] : fRef_In; //It is giving valgrind error, but it is not needed. f_ref = f_min in WaveformCache.c and SimInspiral.c.
644 
645  /* Get minimum and maximum frequencies. */
646  REAL8 f_min_In = freqs->data[0];
647  REAL8 f_max_In = freqs->data[freqs->length - 1];
648 
650  XLAL_CHECK(
651  (fRef >= f_min_In)&&(fRef <= f_max_In),
652  XLAL_EFUNC,
653  "Error: f_min = %.2f <= fRef = %.2f < f_max = %.2f required when using tuned angles.\n",f_min_In,fRef,f_max_In);
654  }
655 
656  /*
657  Passing deltaF = 0 implies that freqs is a frequency grid with non-uniform spacing.
658  The function waveform then start at lowest given frequency.
659  The Multibanding has to be switched off since it is built only for equally spaced frequency grid.
660  */
661  /* Use an auxiliar laldict to not overwrite the input argument */
662  LALDict *lalParams_aux;
663  /* setup mode array */
664  if (lalParams == NULL)
665  {
666  lalParams_aux = XLALCreateDict();
667  }
668  else{
669  lalParams_aux = XLALDictDuplicate(lalParams);
670  }
672  {
673  XLAL_PRINT_WARNING("Warning: Function is aimed for non-uniform frequency grid, switching off Multibanding.");
676  }
677 
679  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
680 
681  /* Initialize IMRPhenomX waveform struct and perform sanity check. */
683  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
684  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1z, chi2z, 0.0, fRef, phiRef, f_min_In, f_max_In, distance, inclination, lalParams_aux, DEBUG);
685  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
686 
687  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
689  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
690 
691  lalParams_aux = IMRPhenomXPHM_setup_mode_array(lalParams_aux);
692 
694  pWF,
695  pPrec,
696  m1_SI,
697  m2_SI,
698  chi1x,
699  chi1y,
700  chi1z,
701  chi2x,
702  chi2y,
703  chi2z,
704  lalParams_aux,
706  );
707  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
708 
709 
710  /* Now call the core IMRPhenomXPHM waveform generator */
711  /* Choose between the two options for computing the polarizations. The default is 0. */
713  {
714  status = IMRPhenomXPHM_hplushcross(hptilde, hctilde, freqs, pWF, pPrec, lalParams_aux);
715  }
716  else
717  {
718  status = IMRPhenomXPHM_hplushcross_from_modes(hptilde, hctilde, freqs, pWF, pPrec, lalParams_aux);
719  }
720  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_hplushcross failed to generate IMRPhenomXPHM waveform.");
721 
722  /* Free memory */
723  LALFree(pPrec);
724  LALFree(pWF);
725  XLALDestroyDict(lalParams_aux);
726 
727  return XLAL_SUCCESS;
728  }
729  /** @} **
730  * @} **/
731 
732 
733 /**
734  Core function of XLALSimIMRPhenomXPHM and XLALSimIMRPhenomXPHMFrequencySequence.
735  Returns hptilde, hctilde for positive frequencies.
736  The default non-precessing modes are 2|2|, 2|1|, 3|3|, 3|2| and 4|4|.
737  It returns also the contribution of the corresponding negatives modes.
738  It can be evaulated in a non-uniform frequency grid. Assumes positive frequencies.
739 */
741  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency domain h+ GW strain */
742  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency domain hx GW strain */
743  const REAL8Sequence *freqs_In, /**< Frequency array to evaluate the model. (fmin, fmax) for equally spaced grids. */
744  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
745  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
746  LALDict *lalParams /**< LAL Dictionary Structure */
747 )
748 {
749 
750  if (pWF->f_max_prime <= pWF->fMin)
751  {
752  XLAL_ERROR(XLAL_EDOM, "(fCut = %g Hz) <= f_min = %g\n", pWF->f_max_prime, pWF->fMin);
753  }
754 
755  /* Set LIGOTimeGPS */
756  LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO; // = {0,0}
757 
758  REAL8 deltaF = pWF->deltaF;
759 
760  LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(lalParams);
761 
762  /* At this point ModeArray should contain the list of modes
763  and therefore if NULL then something is wrong and abort. */
764  if (ModeArray == NULL)
765  {
766  XLAL_ERROR(XLAL_EDOM, "ModeArray is NULL when it shouldn't be. Aborting.\n");
767  }
768 
769  INT4 status = 0; //Variable to check correct functions calls.
770  /*
771  Take input/default value for the threshold of the Multibanding for the hlms modes.
772  If = 0 then do not use Multibanding. Default value defined in XLALSimInspiralWaveformParams.c.
773  If the input freqs_In is non-uniform the Multibanding has been already switche off.
774  */
776 
777  if(pPrec->precessing_tag==3){
778  status=IMRPhenomX_Initialize_Euler_Angles(pWF,pPrec,lalParams);
779  XLAL_CHECK(status==XLAL_SUCCESS, XLAL_EDOM, "%s: Error in IMRPhenomX_Initialize_Euler_Angles.\n",__func__);
780  }
781 
782 
783  /* Build the frequency array and initialize hptilde to the length of freqs. */
784  REAL8Sequence *freqs;
785  UINT4 offset = SetupWFArrays(&freqs, hptilde, freqs_In, pWF, ligotimegps_zero);
786 
787  /* Initialize hctilde according to hptilde. */
788  size_t npts = (*hptilde)->data->length;
789  *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &(*hptilde)->epoch, (*hptilde)->f0, pWF->deltaF, &lalStrainUnit, npts);
790  XLAL_CHECK (*hctilde, XLAL_ENOMEM, "Failed to allocated waveform COMPLEX16FrequencySeries of length %zu.", npts);
791  memset((*hctilde)->data->data, 0, npts * sizeof(COMPLEX16));
792  XLALUnitMultiply(&((*hctilde)->sampleUnits), &((*hctilde)->sampleUnits), &lalSecondUnit);
793 
794  /* Object to store the non-precessing 22 mode waveform and to be recycled when calling the 32 mode in multibanding. */
795  COMPLEX16FrequencySeries *htilde22 = NULL;
796 
797 
798 
799  /* Initialize the power of pi for the HM internal functions. */
801  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Failed to initialize useful powers of LAL_PI.");
802 
803 
805  *hlms = NULL;
807  {
808  /* evaluate all hlm modes */
810  hlms,
811  freqs,
812  pWF->m1_SI,
813  pWF->m2_SI,
814  pPrec->chi1x,
815  pPrec->chi1y,
816  pWF->chi1L,
817  pPrec->chi2x,
818  pPrec->chi2y,
819  pWF->chi2L,
820  pWF->phi0,
821  //pWF->deltaF,
822  0,
823  pWF->fRef,
824  lalParams);
825  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "XLALSimIMRPhenomHMGethlmModes failed");
826  }
827 
828  /* Set up code for using PNR tuned angles */
829  int IMRPhenomXPNRUseTunedAngles = pPrec->IMRPhenomXPNRUseTunedAngles;
830  int AntisymmetricWaveform = pPrec->IMRPhenomXAntisymmetricWaveform;
831 
832  IMRPhenomX_PNR_angle_spline *hm_angle_spline = NULL;
833  REAL8 Mf_RD_22 = pWF->fRING;
834  REAL8 Mf_RD_lm = 0.0;
835 
836  if (IMRPhenomXPNRUseTunedAngles)
837  {
838  /* We're using tuned angles! */
839  /* Allocate the spline interpolant struct */
840 
842  if (!hm_angle_spline)
843  {
844  XLAL_ERROR(XLAL_EFUNC, "hm_angle_spline struct allocation failed in LALSimIMRPhenomXPHM.c.");
845  }
846 
847  /* Generate interpolant structs for the (2,2) angles */
848  status = IMRPhenomX_PNR_GeneratePNRAngleInterpolants(hm_angle_spline, pWF, pPrec, lalParams);
849  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_GeneratePNRAngleInterpolants failed.\n");
850 
851  /* Here we assign the reference values of alpha and gamma to their values in the precession struct */
852  /* NOTE: the contribution from pPrec->alpha0 is assigned in IMRPhenomX_PNR_RemapThetaJSF */
853  pPrec->alpha_offset = gsl_spline_eval(hm_angle_spline->alpha_spline, pWF->fRef, hm_angle_spline->alpha_acc);
854  /* NOTE: the sign is flipped between gamma and epsilon */
855  pPrec->epsilon_offset = -gsl_spline_eval(hm_angle_spline->gamma_spline, pWF->fRef, hm_angle_spline->gamma_acc) - pPrec->epsilon0; // note the sign difference between gamma and epsilon
856 
857  /* Remap the J-frame sky location to use beta instead of ThetaJN */
858  REAL8 betaPNR_ref = gsl_spline_eval(hm_angle_spline->beta_spline, pWF->fRef, hm_angle_spline->beta_acc);
859  status = IMRPhenomX_PNR_RemapThetaJSF(betaPNR_ref, pWF, pPrec, lalParams);
860  XLAL_CHECK(
861  XLAL_SUCCESS == status,
862  XLAL_EFUNC,
863  "Error: IMRPhenomX_PNR_RemapThetaJSF failed in IMRPhenomX_PNR_GeneratePNRAngles.");
864  }
865 
866  /******************************************************/
867  /******** Antisymmetric waveform generated here ********/
868  /******************************************************/
869  REAL8Sequence *antiSym_amp = NULL;
870  REAL8Sequence *antiSym_phi = NULL;
871 
872  if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
873  {
874  antiSym_amp = XLALCreateREAL8Sequence(freqs->length);
875  antiSym_phi = XLALCreateREAL8Sequence(freqs->length);
876  }
877 
878 
879 
880  /***** Loop over non-precessing modes ******/
881  for (UINT4 ell = 2; ell <= L_MAX; ell++)
882  {
883  for (UINT4 emmprime = 1; emmprime <= ell; emmprime++)
884  {
885  /* Loop over only positive mprime is intentional.
886  The single mode function returns the negative mode h_l-mprime, and the positive
887  is added automatically in during the twisting up in IMRPhenomXPHMTwistUp.
888  First check if (l,m) mode is 'activated' in the ModeArray.
889  If activated then generate the mode, else skip this mode.
890  */
891  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ell, emmprime) != 1)
892  { /* skip mode */
893  continue;
894  } /* else: generate mode */
895 
896  /* Skip twisting-up if the non-precessing mode is zero. */
897  if((pWF->q == 1) && (pWF->chi1L == pWF->chi2L) && (emmprime % 2 != 0))
898  {
899  continue;
900  }
901 
902  /* Compute and store phase alignment quantities for each
903  non-precessing (ell,emm) multipole moment. Note that the
904  (2,2) moment is handled separately within XAS routines. */
905  if (
906  pWF->APPLY_PNR_DEVIATIONS && pWF->IMRPhenomXPNRForceXHMAlignment && (ell != 2) && (emmprime != 2)
907  )
908  {
909  /* Compute and store phase alignment quantities for each
910  non-precessing (ell,emm) multipole moment */
911  IMRPhenomXHM_PNR_SetPhaseAlignmentParams(ell,emmprime,pWF,pPrec,lalParams);
912  }
913 
914  #if DEBUG == 1
915  printf("\n\n*********************************\n*Non-precessing Mode %i%i \n******************************\n",ell, emmprime);
916  // Save the hlm mode into a file
917  FILE *fileangle;
918  char fileSpec[40];
919 
920  if(pPrec->MBandPrecVersion == 0)
921  {
922  sprintf(fileSpec, "angles_hphc_%i%i.dat", ell, emmprime);
923  }
924  else
925  {
926  sprintf(fileSpec, "angles_hphc_MB_%i%i.dat", ell, emmprime);
927  }
928  printf("\nOutput angle file: %s\r\n", fileSpec);
929  fileangle = fopen(fileSpec,"w");
930 
931  fprintf(fileangle,"# q = %.16e m1 = %.16e m2 = %.16e chi1 = %.16e chi2 = %.16e lm = %i%i Mtot = %.16e distance = %.16e\n", pWF->q, pWF->m1, pWF->m2, pWF->chi1L, pWF->chi2L, ell, emmprime, pWF->Mtot, pWF->distance/LAL_PC_SI/1e6);
932  fprintf(fileangle,"#fHz cexp_i_alpha(re im) cexp_i_epsilon(re im) cexp_i_betah(re im)\n");
933 
934  fclose(fileangle);
935  #endif
936 
937  /* Variable to store the strain of only one (negative) mode: h_l-mprime */
938  COMPLEX16FrequencySeries *htildelm = NULL;
939 
941  {
942  INT4 minus1l = 1;
943  if(ell % 2 !=0) minus1l = -1;
944  COMPLEX16FrequencySeries *htildelmPhenomHM = NULL;
945  /* Initialize the htilde frequency series */
946  htildelm = XLALCreateCOMPLEX16FrequencySeries("htildelm: FD waveform", &ligotimegps_zero, 0, pWF->deltaF, &lalStrainUnit, npts);
947  /* Check that frequency series generated okay */
948  XLAL_CHECK(htildelm,XLAL_ENOMEM,"Failed to allocate COMPLEX16FrequencySeries of length %zu for f_max = %f, deltaF = %g.\n", npts, freqs_In->data[freqs_In->length - 1], pWF->deltaF);
949  memset((htildelm)->data->data, 0, npts * sizeof(COMPLEX16));
950  XLALUnitMultiply(&((htildelm)->sampleUnits), &((htildelm)->sampleUnits), &lalSecondUnit);
951 
952  htildelmPhenomHM = XLALSphHarmFrequencySeriesGetMode(*hlms, ell, emmprime);
953  for(UINT4 idx = 0; idx < freqs->length; idx++)
954  {
955  htildelm->data->data[idx+offset] = minus1l * htildelmPhenomHM->data->data[idx] * pWF->amp0;
956  }
957  //XLALDestroyCOMPLEX16FrequencySeries(htildelmPhenomHM);
958  }
959  else
960  {
961  /* Compute non-precessing mode */
962  if (thresholdMB == 0){ // No multibanding
963  if(ell == 2 && emmprime == 2)
964  {
965  status = IMRPhenomXASGenerateFD(&htildelm, freqs, pWF, lalParams);
966  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXASGenerateFD failed to generate IMRPhenomXHM waveform.");
967  }
968  else
969  {
970  status = IMRPhenomXHMGenerateFDOneMode(&htildelm, freqs, pWF, ell, emmprime, lalParams);
971  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMGenerateFDOneMode failed to generate IMRPhenomXHM waveform.");
972  }
973  }
974  else{ // With multibanding
975  if(ell==3 && emmprime==2){ // mode with mode-mixing
976  status = IMRPhenomXHMMultiBandOneModeMixing(&htildelm, htilde22, pWF, ell, emmprime, lalParams);
977  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMMultiBandOneModeMixing failed to generate IMRPhenomXHM waveform.");
978  }
979  else{ // modes without mode-mixing including 22 mode
980  status = IMRPhenomXHMMultiBandOneMode(&htildelm, pWF, ell, emmprime, lalParams);
981  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMMultiBandOneMode failed to generate IMRPhenomXHM waveform.");
982  }
983 
984  /* IMRPhenomXHMMultiBandOneMode* functions set pWF->deltaF=0 internally, we put it back here. */
985  pWF->deltaF = deltaF;
986 
987  /* If the 22 and 32 modes are active, we recycle the 22 mode for the mixing in the 32 and it is passed to IMRPhenomXHMMultiBandOneModeMixing.
988  The 22 mode is always computed first than the 32, we store the 22 mode in the variable htilde22. */
989  if(ell==2 && emmprime==2 && XLALSimInspiralModeArrayIsModeActive(ModeArray, 3, 2)==1){
990  htilde22 = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &(ligotimegps_zero), 0.0, pWF->deltaF, &lalStrainUnit, htildelm->data->length);
991  for(UINT4 idx = 0; idx < htildelm->data->length; idx++){
992  htilde22->data->data[idx] = htildelm->data->data[idx];
993  }
994  }
995  }
996  }
997 
998  if(ell==2 && emmprime==2)
999  {
1000  if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
1001  {
1002  IMRPhenomX_PNR_GenerateAntisymmetricWaveform(antiSym_amp,antiSym_phi,freqs,pWF,pPrec,lalParams);
1003  }
1004  }
1005 
1006  if (!(htildelm)){ XLAL_ERROR(XLAL_EFUNC); }
1007 
1008  /*
1009  For very special cases of deltaF, it can happen that building htildelm with 'freqs_In' or with 'freqs' gives different lengths.
1010  In case that happens we resize here to the correct length. We could also have called GenerateFD passing freqs_In,
1011  but in that ways we would be computing the uniform frequency array twice.
1012  Alsom doing as here we cover the multibanding and PhenomHM cases.
1013  */
1014  if(htildelm->data->length != npts)
1015  {
1016  htildelm = XLALResizeCOMPLEX16FrequencySeries(htildelm, 0, npts);
1017  XLAL_CHECK (htildelm, XLAL_ENOMEM, "Failed to resize hlm COMPLEX16FrequencySeries" );
1018  }
1019 
1020  /* htildelm is recomputed every time in the loop. Check that it always comes out with the same length */
1021  XLAL_CHECK ( ((*hptilde)->data->length==htildelm->data->length)
1022  && ((*hctilde)->data->length==htildelm->data->length),
1023  XLAL_EBADLEN,
1024  "Inconsistent lengths between frequency series htildelm (%d), hptilde (%d) and hctilde (%d).",
1025  htildelm->data->length, (*hptilde)->data->length, (*hctilde)->data->length
1026  );
1027 
1028  /*
1029  TWISTING UP
1030  Transform modes from the precessing L-frame to inertial J-frame.
1031  */
1032 
1033 
1034  /* Variable to store the non-precessing waveform in one frequency point. */
1035  COMPLEX16 hlmcoprec=0.0;
1036  COMPLEX16 hlmcoprec_antiSym=0.0;
1037 
1038  /* No Multibanding for the angles. */
1039  if(pPrec->MBandPrecVersion == 0)
1040  {
1041  #if DEBUG == 1
1042  printf("\n****************************************************************\n");
1043  printf("\n* NOT USING MBAND FOR ANGLES %i *\n", offset);
1044  printf("\n****************************************************************\n");
1045  #endif
1046 
1047  // Let the people know if twisting up will not take place
1048  if( pWF->IMRPhenomXReturnCoPrec == 1 )
1049  {
1050  #if DEBUG == 1
1051  printf("\n** We will not twist up the HM waveforms **\n");
1052  #endif
1053  }
1054 
1055  /* set variables for PNR angles if needed */
1056  REAL8 Mf_high = 0.0;
1057  REAL8 Mf_low = 0.0;
1058  UINT4 PNRtoggleInspiralScaling = pPrec->PNRInspiralScaling;
1059 
1060  // set PNR transition frequencies if needed
1061  if (IMRPhenomXPNRUseTunedAngles)
1062  {
1063 
1064  if((ell==2)&&(emmprime==2))
1065  {
1066  /* the frequency parameters don't matter in this case */
1067  Mf_RD_lm = 0.0;
1068  }
1069  else
1070  {
1071  /* Get the (l,m) RD frequency */
1072 
1073  Mf_RD_lm = IMRPhenomXHM_GenerateRingdownFrequency(ell, emmprime, pWF);
1074 
1075  status = IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies(&Mf_low, &Mf_high, emmprime, Mf_RD_22, Mf_RD_lm, pPrec);
1076  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies failed.\n");
1077  }
1078  }
1079 
1080  if(pPrec->precessing_tag==3)
1081  pPrec->gamma_in = 0.;
1082 
1083  for (UINT4 idx = 0; idx < freqs->length; idx++)
1084  {
1085  double Mf = pWF->M_sec * freqs->data[idx];
1086 
1087  /* Do not generate waveform above Mf_max (default Mf = 0.3) */
1088  if(Mf <= (pWF->f_max_prime * pWF->M_sec))
1089  {
1090  hlmcoprec = htildelm->data->data[idx + offset]; /* Co-precessing waveform for one freq point */
1091  COMPLEX16 hplus = 0.0; /* h_+ */
1092  COMPLEX16 hcross = 0.0; /* h_x */
1093 
1094  if( pWF->IMRPhenomXReturnCoPrec == 1 )
1095  {
1096  // Do not twist up
1097  hplus = 0.5 * hlmcoprec;
1098  hcross = -0.5 * I * hlmcoprec;
1099 
1100  //
1101  if( pWF->PhenomXOnlyReturnPhase ){
1102  // Set hplus to phase (as will be stored in hlmcoprec) and hcross to zero
1103  hplus = hlmcoprec; // NOTE that here hlmcoprec = waveform_phase (assuming one multipole moment)
1104  hcross = 0;
1105  }
1106 
1107  }
1108  else
1109  {
1110  if(IMRPhenomXPNRUseTunedAngles)
1111  {
1112  REAL8 Mf_mapped = IMRPhenomX_PNR_LinearFrequencyMap(Mf, ell, emmprime, Mf_low, Mf_high, Mf_RD_22, Mf_RD_lm, PNRtoggleInspiralScaling);
1113  REAL8 f_mapped = XLALSimIMRPhenomXUtilsMftoHz(Mf_mapped, pWF->Mtot);
1114 
1115  pPrec->alphaPNR = gsl_spline_eval(hm_angle_spline->alpha_spline, f_mapped, hm_angle_spline->alpha_acc);
1116  pPrec->betaPNR = gsl_spline_eval(hm_angle_spline->beta_spline, f_mapped, hm_angle_spline->beta_acc);
1117  pPrec->gammaPNR = gsl_spline_eval(hm_angle_spline->gamma_spline, f_mapped, hm_angle_spline->gamma_acc);
1118  }
1119 
1120  // Twist up symmetric strain
1121  status = IMRPhenomXPHMTwistUp(Mf, hlmcoprec, pWF, pPrec, ell, emmprime, &hplus, &hcross);
1122  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXPHMTwistUp failed.");
1123 
1124  if(ell == 2 && emmprime == 2 && AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
1125  {
1126  COMPLEX16 hplus_antiSym = 0.0;
1127  COMPLEX16 hcross_antiSym = 0.0;
1128  hlmcoprec_antiSym = antiSym_amp->data[idx]*cexp(I*antiSym_phi->data[idx]);
1129  pPrec->PolarizationSymmetry = -1.0;
1130  IMRPhenomXPHMTwistUp(Mf, hlmcoprec_antiSym, pWF, pPrec, ell, emmprime, &hplus_antiSym, &hcross_antiSym);
1131  pPrec->PolarizationSymmetry = 1.0;
1132  hplus += hplus_antiSym;
1133  hcross += hcross_antiSym;
1134  }
1135  }
1136 
1137  (*hptilde)->data->data[idx + offset] += hplus;
1138  (*hctilde)->data->data[idx + offset] += hcross;
1139  }
1140  else
1141  {
1142  /* Mf > Mf_max, so return 0 */
1143  (*hptilde)->data->data[idx + offset] += 0.0 + I*0.0;
1144  (*hctilde)->data->data[idx + offset] += 0.0 + I*0.0;
1145  }
1146  }
1147 
1148  if(IMRPhenomXPNRUseTunedAngles)
1149  {
1150  gsl_interp_accel_reset(hm_angle_spline->alpha_acc);
1151  gsl_interp_accel_reset(hm_angle_spline->beta_acc);
1152  gsl_interp_accel_reset(hm_angle_spline->gamma_acc);
1153  }
1154 
1155  // If we only want the coprecessing waveform, then exit
1156  // if( pWF->IMRPhenomXReturnCoPrec == 1 ) return XLAL_SUCCESS;
1157  if( pWF->IMRPhenomXReturnCoPrec == 1 ) {
1158  return XLAL_SUCCESS;
1159  }
1160 
1161  }
1162  else
1163  {
1164  /*
1165  Multibanding for the angles.
1166 
1167  - In this first release we use the same coarse grid that is used for computing the non-precessing modes.
1168  - This grid is discussed in section II-A of arXiv:2001.10897. See also section D of Precessing paper.
1169  - This grid is computed with the function XLALSimIMRPhenomXMultibandingVersion defined in LALSimIMRPhenomXHM_multiband.c.
1170  - The version of the coarse grid will be changed with the option 'MBandPrecVersion' defined in LALSimInspiralWaveformParams.c.
1171  - Currently there is only one version available and the option value for that is 0, which is the default value.
1172  */
1173 
1174  #if DEBUG == 1
1175  printf("\n****************************************************************\n");
1176  printf("\n* USING MBAND FOR ANGLES *\n");
1177  printf("\n****************************************************************\n");
1178  #endif
1179 
1180 
1181 
1182  /* Compute non-uniform coarse frequency grid as 1D array */
1183  REAL8Sequence *coarseFreqs;
1184  XLALSimIMRPhenomXPHMMultibandingGrid(&coarseFreqs, ell, emmprime, pWF, lalParams);
1185 
1186  UINT4 lenCoarseArray = coarseFreqs->length;
1187 
1188 
1189  /* Euler angles */
1190  REAL8 alpha = 0.0;
1191  REAL8 epsilon = 0.0;
1192 
1193  REAL8 cBetah = 0.0;
1194  REAL8 sBetah = 0.0;
1195 
1196  /* Variables to store the Euler angles in the coarse frequency grid. */
1197  REAL8 *valpha = (REAL8*)XLALMalloc(lenCoarseArray * sizeof(REAL8));
1198  REAL8 *vepsilon = (REAL8*)XLALMalloc(lenCoarseArray * sizeof(REAL8));
1199  REAL8 *vbetah = (REAL8*)XLALMalloc(lenCoarseArray * sizeof(REAL8));
1200 
1201  if(IMRPhenomXPNRUseTunedAngles)
1202  {
1203  REAL8 Mf_high = 0.0;
1204  REAL8 Mf_low = 0.0;
1205  REAL8 fCut = pWF->fCut;
1206 
1207  if ((ell==2)&&(emmprime==2))
1208  {
1209  /* the frequency parameters don't matter here */
1210  Mf_RD_lm = 0.0;
1211  }
1212  else
1213  {
1214  /* Get the (l,m) RD frequency */
1215 
1216  Mf_RD_lm = IMRPhenomXHM_GenerateRingdownFrequency(ell, emmprime, pWF);
1217 
1218  status = IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies(&Mf_low, &Mf_high, emmprime, Mf_RD_22, Mf_RD_lm, pPrec);
1219  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies failed.\n");
1220  }
1221 
1222  UINT4 PNRtoggleInspiralScaling = pPrec->PNRInspiralScaling;
1223  #if DEBUG == 1
1224  // Save the hlm mode into a file
1225  FILE *fileangle0101;
1226  char fileSpec0101[40];
1227 
1228  sprintf(fileSpec0101, "angles_pnr_MB_%i%i.dat", ell, emmprime);
1229 
1230  fileangle0101 = fopen(fileSpec0101,"w");
1231 
1232  fprintf(fileangle0101,"#Mf fHz alpha beta gamma\n");
1233 
1234  fprintf(fileangle0101,"#Mf_low = %.16e\n",Mf_low);
1235  fprintf(fileangle0101,"#Mf_high = %.16e\n",Mf_high);
1236  fprintf(fileangle0101,"#Mf_RD_22 = %.16e\n",Mf_RD_22);
1237  fprintf(fileangle0101,"#Mf_RD_lm = %.16e\n",Mf_RD_lm);
1238 
1239  #endif
1240 
1241  for(UINT4 j=0; j<lenCoarseArray; j++)
1242  {
1243  REAL8 Mf = coarseFreqs->data[j];
1244  REAL8 Mf_mapped = IMRPhenomX_PNR_LinearFrequencyMap(Mf, ell, emmprime, Mf_low, Mf_high, Mf_RD_22, Mf_RD_lm, PNRtoggleInspiralScaling);
1245  REAL8 f_mapped = XLALSimIMRPhenomXUtilsMftoHz(Mf_mapped, pWF->Mtot);
1246 
1247  /* add in security to avoid frequency extrapolation */
1248  f_mapped = (f_mapped > fCut) ? fCut : f_mapped;
1249 
1250  double beta = gsl_spline_eval(hm_angle_spline->beta_spline, f_mapped, hm_angle_spline->beta_acc);
1251 
1252  valpha[j] = gsl_spline_eval(hm_angle_spline->alpha_spline, f_mapped, hm_angle_spline->alpha_acc) - pPrec->alpha_offset;
1253  vepsilon[j] = -1.0 * gsl_spline_eval(hm_angle_spline->gamma_spline, f_mapped, hm_angle_spline->gamma_acc) - pPrec->epsilon_offset;
1254  vbetah[j] = beta / 2.0;
1255 
1256  #if DEBUG == 1
1257 
1258  fprintf(fileangle0101,"%.16e\t%.16e\t%.16e\t%.16e\t%.16e\n",Mf,f_mapped,valpha[j],beta,vepsilon[j]);
1259 
1260  #endif
1261  }
1262 
1263 
1264  #if DEBUG == 1
1265  fclose(fileangle0101);
1266  #endif
1267 
1268  gsl_interp_accel_reset(hm_angle_spline->alpha_acc);
1269  gsl_interp_accel_reset(hm_angle_spline->beta_acc);
1270  gsl_interp_accel_reset(hm_angle_spline->gamma_acc);
1271 
1272  }
1273  else
1274  {
1275  switch(pPrec->IMRPhenomXPrecVersion)
1276  {
1277  case 101:
1278  case 102:
1279  case 103:
1280  case 104:
1281  {
1282  /* Use NNLO PN Euler angles */
1283  /* Evaluate angles in coarse freq grid */
1284  for(UINT4 j=0; j<lenCoarseArray; j++)
1285  {
1286  REAL8 Mf = coarseFreqs->data[j];
1287 
1288  /* This function already add the offsets to the angles. */
1289  Get_alpha_beta_epsilon(&alpha, &cBetah, &sBetah, &epsilon, emmprime, Mf, pPrec, pWF);
1290 
1291  valpha[j] = alpha;
1292  vepsilon[j] = epsilon;
1293  vbetah[j] = acos(cBetah);
1294  }
1295  break;
1296  }
1297  case 220:
1298  case 221:
1299  case 222:
1300  case 223:
1301  case 224:
1302  {
1303  /* Use MSA Euler angles. */
1304  /* Evaluate angles in coarse freq grid */
1305  for(UINT4 j=0; j<lenCoarseArray; j++)
1306  {
1307  /* Get Euler angles. */
1308  REAL8 Mf = coarseFreqs->data[j];
1309  const REAL8 v = cbrt (LAL_PI * Mf * (2.0 / emmprime) );
1310  const vector vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWF,pPrec);
1311  REAL8 cos_beta = 0.0;
1312 
1313  /* Get the offset for the Euler angles alpha and epsilon. */
1314  REAL8 alpha_offset_mprime = 0, epsilon_offset_mprime = 0;
1315  Get_alpha_epsilon_offset(&alpha_offset_mprime, &epsilon_offset_mprime, emmprime, pPrec);
1316 
1317  valpha[j] = vangles.x - alpha_offset_mprime;
1318  vepsilon[j] = vangles.y - epsilon_offset_mprime;
1319  cos_beta = vangles.z;
1320 
1321  status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1322  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1323 
1324  vbetah[j] = acos(cBetah);
1325  }
1326  break;
1327  }
1328 
1329  case 310:
1330  case 311:
1331  case 320:
1332  case 321:
1333 
1334  {
1335 
1336  /* Get the offset for the Euler angles alpha and epsilon. */
1337  REAL8 alpha_offset_mprime = pPrec->alpha_ref- pPrec->alpha0;
1338  REAL8 epsilon_offset_mprime = -pPrec->gamma_ref-pPrec->epsilon0;
1339 
1340  REAL8 cos_beta=0., gamma=0., alpha_i=0.;
1341  REAL8 Mf;
1342  int success;
1343 
1344 
1345  /* Evaluate angles in coarse freq grid */
1346  for(UINT4 j=0; j<lenCoarseArray; j++)
1347  {
1348 
1349  success = 0;
1350  Mf = coarseFreqs->data[j]*(2.0/emmprime);
1351 
1352 
1353  if(Mf< pPrec->ftrans_MRD)
1354  {
1355  success = gsl_spline_eval_e(pPrec->alpha_spline, Mf, pPrec->alpha_acc,&alpha_i);
1356  success = success + gsl_spline_eval_e(pPrec->cosbeta_spline, Mf, pPrec->cosbeta_acc,&cos_beta);
1357  success = success + gsl_spline_eval_e(pPrec->gamma_spline, Mf, pPrec->gamma_acc, &gamma);
1358 
1359  XLAL_CHECK(success == XLAL_SUCCESS, XLAL_EFUNC, "%s: Failed to interpolate Euler angles at f=%.7f. \n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mf,pWF->Mtot));
1360  }
1361 
1362  else {
1363 
1364  if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321 ){
1365 
1366  alpha_i=alphaMRD(Mf,pPrec->alpha_params);
1367  cos_beta=cos(betaMRD(Mf,pWF,pPrec->beta_params));
1368 
1369  if(j>0)
1370  {
1371  REAL8 dMf=(coarseFreqs->data[j]-coarseFreqs->data[j-1])* (2.0 / emmprime);
1372  REAL8 deltagamma=0.;
1373  success = gamma_from_alpha_cosbeta(&deltagamma, Mf,dMf,pWF,pPrec);
1374  if(success!=XLAL_SUCCESS) gamma = pPrec->gamma_in;
1375  else gamma = pPrec->gamma_in+deltagamma;
1376 
1377  }
1378 
1379  else
1380 
1381  {
1382  success = gsl_spline_eval_e(pPrec->gamma_spline, Mf, pPrec->gamma_acc,&gamma);
1383  if(success!=XLAL_SUCCESS) gamma = pPrec->gamma_in;
1384  }
1385 
1386  }
1387 
1388  else{
1389 
1390  alpha_i=pPrec->alpha_ftrans;
1391  cos_beta=pPrec->cosbeta_ftrans;
1392  gamma=pPrec->gamma_ftrans;
1393 
1394  }
1395 
1396  }
1397 
1398  pPrec->gamma_in = gamma;
1399 
1400  // make sure |cos(beta)| does not exceed 1 due to roundoff errors
1401  if(fabs(cos_beta)>1)
1402  cos_beta=copysign(1.0, cos_beta);
1403 
1404  valpha[j]= alpha_i- alpha_offset_mprime;
1405  vepsilon[j] = -gamma - epsilon_offset_mprime;
1406 
1407 
1408  status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1409  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1410  vbetah[j] = acos(cBetah);
1411 
1412  }
1413 
1414 
1415  break;
1416  }
1417 
1418 
1419 
1420  default:
1421  {
1422  XLAL_ERROR(XLAL_EINVAL,"Error: IMRPhenomXPrecVersion not recognized. Recommended default is 223.\n");
1423  break;
1424  }
1425  }
1426  }
1427 
1428  /*
1429  We have the three Euler angles evaluated in the coarse frequency grid.
1430  Now we have to carry out the iterative linear interpolation for the complex exponential of each Euler angle. This follows the procedure of eq. 2.32 in arXiv:2001.10897..
1431  The result will be three arrays of complex exponential evaluated in the finefreqs.
1432  */
1433  UINT4 fine_count = 0, ratio;
1434  REAL8 Omega_alpha, Omega_epsilon, Omega_betah, Qalpha, Qepsilon, Qbetah;
1435  REAL8 Mfhere, Mfnext, evaldMf;
1436  Mfnext = coarseFreqs->data[0];
1437  evaldMf = XLALSimIMRPhenomXUtilsHztoMf(pWF->deltaF, pWF->Mtot);
1438 
1439  /*
1440  Number of points where the waveform will be computed.
1441  It is the same for all the modes and could be computed outside the loop, it is here for clarity since it is not used anywhere else.
1442  */
1443  size_t iStop = (size_t) (pWF->f_max_prime / pWF->deltaF) + 1 - offset;
1444 
1445  UINT4 length_fine_grid = iStop + 3; // This is just to reserve memory, add 3 points of buffer.
1446 
1447  COMPLEX16 *cexp_i_alpha = (COMPLEX16*)XLALMalloc(length_fine_grid * sizeof(COMPLEX16));
1448  COMPLEX16 *cexp_i_epsilon = (COMPLEX16*)XLALMalloc(length_fine_grid * sizeof(COMPLEX16));
1449  COMPLEX16 *cexp_i_betah = (COMPLEX16*)XLALMalloc(length_fine_grid * sizeof(COMPLEX16));
1450 
1451 
1452  #if DEBUG == 1
1453  printf("\n\nLENGTHS fine grid estimate, coarseFreqs->length = %i %i\n", length_fine_grid, lenCoarseArray);
1454  printf("fine_count, htildelm->length, offset = %i %i %i\n", fine_count, htildelm->data->length, offset);
1455  #endif
1456 
1457  /* Loop over the coarse freq points */
1458  for(UINT4 j = 0; j<lenCoarseArray-1 && fine_count < iStop; j++)
1459  {
1460  Mfhere = Mfnext;
1461  Mfnext = coarseFreqs->data[j+1];
1462 
1463  Omega_alpha = (valpha[j + 1] - valpha[j]) /(Mfnext - Mfhere);
1464  Omega_epsilon = (vepsilon[j + 1] - vepsilon[j])/(Mfnext - Mfhere);
1465  Omega_betah = (vbetah[j + 1] - vbetah[j]) /(Mfnext - Mfhere);
1466 
1467  cexp_i_alpha[fine_count] = cexp(I*valpha[j]);
1468  cexp_i_epsilon[fine_count] = cexp(I*vepsilon[j]);
1469  cexp_i_betah[fine_count] = cexp(I*vbetah[j]);
1470 
1471  Qalpha = cexp(I*evaldMf*Omega_alpha);
1472  Qepsilon = cexp(I*evaldMf*Omega_epsilon);
1473  Qbetah = cexp(I*evaldMf*Omega_betah);
1474 
1475  fine_count++;
1476 
1477  REAL8 dratio = (Mfnext-Mfhere)/evaldMf;
1478  UINT4 ceil_ratio = ceil(dratio);
1479  UINT4 floor_ratio = floor(dratio);
1480 
1481  /* Make sure the rounding is done correctly. */
1482  if(fabs(dratio-ceil_ratio) < fabs(dratio-floor_ratio))
1483  {
1484  ratio = ceil_ratio;
1485  }
1486  else
1487  {
1488  ratio = floor_ratio;
1489  }
1490 
1491  /* Compute complex exponential in fine points between two coarse points */
1492  /* This loop carry out the eq. 2.32 in arXiv:2001.10897 */
1493  for(UINT4 kk = 1; kk < ratio && fine_count < iStop; kk++){
1494  cexp_i_alpha[fine_count] = Qalpha*cexp_i_alpha[fine_count-1];
1495  cexp_i_epsilon[fine_count] = Qepsilon*cexp_i_epsilon[fine_count-1];
1496  cexp_i_betah[fine_count] = Qbetah*cexp_i_betah[fine_count-1];
1497  fine_count++;
1498  }
1499  }// Loop over coarse grid
1500 
1501  /*
1502  Now we have the complex exponentials of the three Euler angles alpha, beta, epsilon evaluated in the fine frequency grid.
1503  Next step is do the twisting up with these.
1504  */
1505 
1506  #if DEBUG == 1
1507  printf("fine_count, htildelm->length, offset = %i %i %i\n", fine_count, htildelm->data->length, offset);
1508  #endif
1509 
1510 
1511  /************** TWISTING UP in the fine grid *****************/
1512  for (UINT4 idx = 0; idx < fine_count; idx++)
1513  {
1514  double Mf = pWF->M_sec * (idx + offset)*pWF->deltaF;
1515 
1516  hlmcoprec = htildelm->data->data[idx + offset]; /* Co-precessing waveform */
1517 
1518  COMPLEX16 hplus = 0.0; /* h_+ */
1519  COMPLEX16 hcross = 0.0; /* h_x */
1520 
1521  pPrec->cexp_i_alpha = cexp_i_alpha[idx];
1522  pPrec->cexp_i_epsilon = cexp_i_epsilon[idx];
1523  pPrec->cexp_i_betah = cexp_i_betah[idx];
1524 
1525  if(pPrec->precessing_tag==3) pPrec->gamma_in = 0.;
1526 
1527  status = IMRPhenomXPHMTwistUp(Mf, hlmcoprec, pWF, pPrec, ell, emmprime, &hplus, &hcross);
1528  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXPHMTwistUp failed.");
1529 
1530  if(ell == 2 && emmprime == 2 && AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
1531  {
1532  COMPLEX16 hplus_antiSym = 0.0;
1533  COMPLEX16 hcross_antiSym = 0.0;
1534  hlmcoprec_antiSym = antiSym_amp->data[idx] * cexp(I*antiSym_phi->data[idx]);
1535  pPrec->PolarizationSymmetry = -1.0;
1536  IMRPhenomXPHMTwistUp(Mf, hlmcoprec_antiSym, pWF, pPrec, ell, emmprime, &hplus_antiSym, &hcross_antiSym);
1537  pPrec->PolarizationSymmetry = 1.0;
1538  hplus += hplus_antiSym;
1539  hcross += hcross_antiSym;
1540  }
1541 
1542  (*hptilde)->data->data[idx + offset] += hplus ;
1543  (*hctilde)->data->data[idx + offset] += hcross ;
1544 
1545  }
1546 
1547  XLALDestroyREAL8Sequence(coarseFreqs);
1548  LALFree(valpha);
1549  LALFree(vepsilon);
1550  LALFree(vbetah);
1551  LALFree(cexp_i_alpha);
1552  LALFree(cexp_i_epsilon);
1553  LALFree(cexp_i_betah);
1554  }// End of Multibanding-specific.
1555 
1557  }//Loop over emmprime
1558  }//Loop over ell
1559 
1560  if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
1561  {
1562  XLALDestroyREAL8Sequence(antiSym_amp);
1563  XLALDestroyREAL8Sequence(antiSym_phi);
1564  }
1565 
1566  if (IMRPhenomXPNRUseTunedAngles){
1567  gsl_spline_free(hm_angle_spline->alpha_spline);
1568  gsl_spline_free(hm_angle_spline->beta_spline);
1569  gsl_spline_free(hm_angle_spline->gamma_spline);
1570 
1571  gsl_interp_accel_free(hm_angle_spline->alpha_acc);
1572  gsl_interp_accel_free(hm_angle_spline->beta_acc);
1573  gsl_interp_accel_free(hm_angle_spline->gamma_acc);
1574 
1575  LALFree(hm_angle_spline);
1576  }
1577 
1578  // Free memory used to hold non-precesing XHM struct
1580  // Cleaning up
1581  LALFree(pPrec->pWF22AS);
1582  }
1583 
1585  XLALFree(hlms);
1586  /*
1587  Loop over h+ and hx to rotate waveform by 2 \zeta.
1588  See discussion in Appendix C: Frame Transformation and Polarization Basis.
1589  The formula for \zeta is given by eq. C26.
1590  */
1591  if(fabs(pPrec->zeta_polarization) > 0.0)
1592  {
1593  COMPLEX16 PhPpolp, PhPpolc;
1594  REAL8 cosPolFac, sinPolFac;
1595 
1596  cosPolFac = cos(2.0 * pPrec->zeta_polarization);
1597  sinPolFac = sin(2.0 * pPrec->zeta_polarization);
1598 
1599  for (UINT4 i = offset; i < (*hptilde)->data->length; i++)
1600  {
1601  PhPpolp = (*hptilde)->data->data[i];
1602  PhPpolc = (*hctilde)->data->data[i];
1603 
1604  (*hptilde)->data->data[i] = cosPolFac * PhPpolp + sinPolFac * PhPpolc;
1605  (*hctilde)->data->data[i] = cosPolFac * PhPpolc - sinPolFac * PhPpolp;
1606  }
1607  }
1608 
1609  /* Free memory */
1611  XLALDestroyValue(ModeArray);
1612  XLALDestroyREAL8Sequence(freqs);
1613 
1614 
1615  if(pPrec->precessing_tag==3)
1616  {
1617  LALFree(pPrec->alpha_params);
1618  LALFree(pPrec->beta_params);
1619 
1620  gsl_spline_free(pPrec->alpha_spline);
1621  gsl_spline_free(pPrec->cosbeta_spline);
1622  gsl_spline_free(pPrec->gamma_spline);
1623 
1624  gsl_interp_accel_free(pPrec->alpha_acc);
1625  gsl_interp_accel_free(pPrec->gamma_acc);
1626  gsl_interp_accel_free(pPrec->cosbeta_acc);
1627 
1628  }
1629 
1630 
1631  #if DEBUG == 1
1632  printf("\n******Leaving IMRPhenomXPHM_hplushcross*****\n");
1633  #endif
1634 
1635  return XLAL_SUCCESS;
1636 }
1637 
1638 
1639 
1640 /*
1641  Core function of XLALSimIMRPhenomXPHMFromModes and XLALSimIMRPhenomXPHMFrequencySequence.
1642  Returns hptilde, hctilde for positive frequencies.
1643  The default non-precessing modes twisted up are 2|2|, 2|1|, 3|3|, 3|2| and 4|4|.
1644  It returns also the contribution of the corresponding negatives modes.
1645  It returns the same result than IMRPhenomXPHM_hplushcross but here it calls the individual precessing modes
1646  and then sum them all. It is therefor slower and does not include Multibanding for the angles.
1647  It can be evaulated in a non-uniform frequency grid. Assume positive frequencies.
1648 */
1650  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency domain h+ GW strain */
1651  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency domain hx GW strain */
1652  const REAL8Sequence *freqs_In, /**< Frequency array to evaluate the model. (fmin, fmax) for equally spaced grids. */
1653  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
1654  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
1655  LALDict *lalParams /**< LAL Dictionary Structure */
1656 )
1657 {
1658 
1659  if (pWF->f_max_prime <= pWF->fMin)
1660  XLAL_ERROR(XLAL_EDOM, "(fCut = %g Hz) <= f_min = %g\n", pWF->f_max_prime, pWF->fMin);
1661 
1662  /* Set LIGOTimeGPS */
1663  LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO; // = {0,0}
1664 
1665  lalParams = IMRPhenomXPHM_setup_mode_array(lalParams);
1666  LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(lalParams);
1667 
1668  /* At this point ModeArray should contain the list of modes
1669  and therefore if NULL then something is wrong and abort. */
1670  if (ModeArray == NULL)
1671  {
1672  XLAL_ERROR(XLAL_EDOM, "ModeArray is NULL when it shouldn't be. Aborting.\n");
1673  }
1674 
1675  INT4 status = 0; //Variable to check correc functions calls
1676 
1677 
1678  /* Build the frequency array and initialize hctilde to the length of freqs. */
1679  REAL8Sequence *freqs;
1680  UINT4 offset = SetupWFArrays(&freqs, hptilde, freqs_In, pWF, ligotimegps_zero);
1681 
1682  /* Initialize hctilde according to hptilde. */
1683  size_t npts = (*hptilde)->data->length;
1684  *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &ligotimegps_zero, (*hptilde)->f0, pWF->deltaF, &lalStrainUnit, npts);
1685  XLAL_CHECK (*hctilde, XLAL_ENOMEM, "Failed to allocated waveform COMPLEX16FrequencySeries of length %zu.", npts);
1686  memset((*hctilde)->data->data, 0, npts * sizeof(COMPLEX16));
1687  XLALUnitMultiply(&((*hctilde)->sampleUnits), &((*hctilde)->sampleUnits), &lalSecondUnit);
1688 
1689  /* Initialize useful powers of pi for the higher modes internal code. */
1691  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
1692 
1693  if(pPrec->precessing_tag==3){
1694  status=IMRPhenomX_Initialize_Euler_Angles(pWF,pPrec,lalParams);
1695  XLAL_CHECK(status==XLAL_SUCCESS, XLAL_EDOM, "%s: Error in IMRPhenomX_Initialize_Euler_Angles.\n",__func__);
1696  }
1697 
1698  /* Loop over precessing modes */
1699  for(UINT4 ell = 2; ell <= 4; ell++)
1700  {
1701  for(INT4 emm = -1*ell; emm <= (INT4)ell; emm++)
1702  {
1703  #if DEBUG == 1
1704  printf("\n*****************************************************************\n");
1705  printf(" Precessing mode (%i%i) ", ell, emm);
1706  printf("*******************************************************************\n");
1707  #endif
1708 
1709  COMPLEX16FrequencySeries *hlmpos = NULL;
1710  COMPLEX16FrequencySeries *hlmneg = NULL;
1711 
1712  /* We now call one single precessing mode. */
1713  status = IMRPhenomXPHM_OneMode(&hlmpos, &hlmneg, freqs, pWF, pPrec, ell, emm, lalParams);
1714  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_OneMode failed to generate IMRPhenomXHM waveform.");
1715 
1716  if (!(hlmpos)){ XLAL_ERROR(XLAL_EFUNC);}
1717  if (!(hlmneg)){ XLAL_ERROR(XLAL_EFUNC);}
1718 
1719 
1720  /* hlmpos and hlmneg are recomputed every time in the loop. Check that they always come out with the same length. */
1721  XLAL_CHECK ( ((*hptilde)->data->length == hlmpos->data->length) && ((*hptilde)->data->length == hlmpos->data->length)
1722  && (hlmpos->data->length == hlmneg->data->length),
1723  XLAL_EBADLEN,
1724  "Inconsistent lengths between frequency series hlmpos (%d), hlmneg (%d), hptilde (%d) and hctilde (%d).",
1725  hlmpos->data->length, hlmneg->data->length, (*hptilde)->data->length, (*hctilde)->data->length
1726  );
1727 
1728  /*
1729  The precessing modes hlm that we just computed are in the J-frame.
1730  For computing hplus and hcross we have to sum them all with Ylm(thetaJN, 0) since the J-frame is aligned
1731  such that the line of sight N is in the x-z plane of the J-frame.
1732  See appendix C of Precessing Paper, in particular eq. C8.
1733  */
1734  COMPLEX16 Ylm = XLALSpinWeightedSphericalHarmonic(pPrec->thetaJN, 0, -2, ell, emm);
1735  COMPLEX16 Ylmstar = conj(Ylm);
1736 
1737  for(UINT4 i = offset; i < (hlmpos)->data->length; i++)
1738  {
1739  (*hptilde)->data->data[i] += 0.5*(hlmpos->data->data[i] * Ylm + conj(hlmneg->data->data[i]) * Ylmstar);
1740  (*hctilde)->data->data[i] += I*0.5*(hlmpos->data->data[i] * Ylm - conj(hlmneg->data->data[i]) * Ylmstar);
1741  }
1742 
1745  }
1746  }// End loop over precessing modes
1747 
1748  /*
1749  Loop over h+ and hx to rotate waveform by 2 \zeta.
1750  See discussion in Appendix C: Frame Transformation and Polarization Basis.
1751  The formula for \zeta is given by eq. C24.
1752  */
1753  if(fabs(pPrec->zeta_polarization) > 0)
1754  {
1755  COMPLEX16 PhPpolp, PhPpolc;
1756  REAL8 cosPolFac, sinPolFac;
1757 
1758  cosPolFac = cos(2.0 * pPrec->zeta_polarization);
1759  sinPolFac = sin(2.0 * pPrec->zeta_polarization);
1760 
1761  for (UINT4 i = offset; i < (*hptilde)->data->length; i++)
1762  {
1763  PhPpolp = (*hptilde)->data->data[i];
1764  PhPpolc = (*hctilde)->data->data[i];
1765 
1766  (*hptilde)->data->data[i] = cosPolFac * PhPpolp + sinPolFac * PhPpolc;
1767  (*hctilde)->data->data[i] = cosPolFac * PhPpolc - sinPolFac * PhPpolp;
1768  }
1769  }
1770 
1771  /* Free memory */
1772  XLALDestroyValue(ModeArray);
1773  XLALDestroyREAL8Sequence(freqs);
1774 
1775  if(pPrec->precessing_tag==3)
1776  {
1777  LALFree(pPrec->alpha_params);
1778  LALFree(pPrec->beta_params);
1779 
1780  gsl_spline_free(pPrec->alpha_spline);
1781  gsl_spline_free(pPrec->cosbeta_spline);
1782  gsl_spline_free(pPrec->gamma_spline);
1783 
1784  gsl_interp_accel_free(pPrec->alpha_acc);
1785  gsl_interp_accel_free(pPrec->gamma_acc);
1786  gsl_interp_accel_free(pPrec->cosbeta_acc);
1787 
1788  }
1789 
1790 
1791  #if DEBUG == 1
1792  printf("\n******Leaving IMRPhenomXPHM_hplushcross_from_modes*****\n");
1793  #endif
1794 
1795  return XLAL_SUCCESS;
1796 }
1797 
1798 
1799 /*
1800  Core twisting up routine to get hptilde and hctilde.
1801  Twist one h_lmprime waveform in the precessing L-frame to the inertial J-frame for one frequency point
1802  as described in section III of Precessing paper.
1803  The explicit formula used implemented in this function correspond to eqs. E18, E19 in Precessing paper.
1804  This function is used inside a loop over frequencies inside a loop over mprime >0 up to l.
1805 */
1807  const REAL8 Mf, /**< Frequency (Hz) */
1808  const COMPLEX16 hlmprime, /**< Underlying aligned-spin IMRPhenomXHM waveform. The loop is with mprime positive, but the mode has to be the negative one for positive frequencies.*/
1809  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
1810  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
1811  INT4 l, /**< First index of the non-precessing (l,mprime) mode */
1812  INT4 mprime, /**< Second index of the non-precessing (l,mprime) mode */
1813  COMPLEX16 *hp, /**< [out] h_+ polarization \f$\tilde h_+\f$ */
1814  COMPLEX16 *hc /**< [out] h_x polarization \f$\tilde h_x\f$ */
1815 )
1816 {
1817  XLAL_CHECK(hp != NULL, XLAL_EFAULT);
1818  XLAL_CHECK(hc != NULL, XLAL_EFAULT);
1819 
1820  /* Euler angles */
1821  double alpha = 0.0;
1822  double epsilon = 0.0;
1823 
1824  double cBetah = 0.0;
1825  double sBetah = 0.0;
1826 
1827  COMPLEX16 cexp_i_alpha, cexp_i_epsilon = 1;
1828 
1829  if(pPrec->MBandPrecVersion == 0) /* No multibanding for angles */
1830  {
1831  if(pPrec->IMRPhenomXPNRUseTunedAngles)
1832  {
1833  alpha = pPrec->alphaPNR - pPrec->alpha_offset;
1834  epsilon = -1.0 * pPrec->gammaPNR - pPrec->epsilon_offset;
1835  REAL8 cos_beta = cos(pPrec->betaPNR);
1836 
1837  INT4 status = 0;
1838  status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1839  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1840  }
1841  else
1842  {
1843  switch(pPrec->IMRPhenomXPrecVersion)
1844  {
1845  case 101: /* Post-Newtonian Euler angles. Single spin approximantion. See sections IV-B and IV-C in Precessing paper. */
1846  case 102: /* The different number 10i means different PN order. */
1847  case 103:
1848  case 104:
1849  {
1850  Get_alpha_beta_epsilon(&alpha, &cBetah, &sBetah, &epsilon, mprime, Mf, pPrec, pWF);
1851  break;
1852  }
1853  case 220: /* Use MSA angles. See section IV-D in Precessing paper. */
1854  case 221:
1855  case 222:
1856  case 223:
1857  case 224:
1858  {
1859  /* Get Euler angles. */
1860  const double v = cbrt (LAL_PI * Mf * (2.0 / mprime) );
1861  const vector vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWF,pPrec);
1862  double cos_beta = 0.0;
1863 
1864  /* Get the offset for the Euler angles alpha and epsilon. */
1865  REAL8 alpha_offset_mprime = 0, epsilon_offset_mprime = 0;
1866  Get_alpha_epsilon_offset(&alpha_offset_mprime, &epsilon_offset_mprime, mprime, pPrec);
1867 
1868  alpha = vangles.x - alpha_offset_mprime;
1869  epsilon = vangles.y - epsilon_offset_mprime;
1870  cos_beta = vangles.z;
1871 
1872  INT4 status = 0;
1873  status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1874  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1875 
1876  break;
1877  }
1878 
1879  case 310:
1880  case 311:
1881  case 320:
1882  case 321:
1883 
1884  {
1885  /* Get Euler angles. */
1886  int success = XLAL_SUCCESS;
1887  REAL8 Mfprime = Mf * (2.0 / mprime);
1888  REAL8 cos_beta=0., gamma=0., alpha_i=0.;
1889 
1890 
1891  if(Mfprime< pPrec->ftrans_MRD)
1892  {
1893  success = success + gsl_spline_eval_e(pPrec->cosbeta_spline, Mfprime, pPrec->cosbeta_acc,&cos_beta);
1894  success = success + gsl_spline_eval_e(pPrec->gamma_spline, Mfprime, pPrec->gamma_acc, &gamma);
1895  success = success + gsl_spline_eval_e(pPrec->alpha_spline, Mfprime, pPrec->alpha_acc,&alpha_i);
1896 
1897  XLAL_CHECK(success == XLAL_SUCCESS, XLAL_EFUNC, "%s: Failed to evaluate angles at f=%.7f for (l,m)=(%d,%d). Got alpha=%.4f,cosbeta=%.4f,gamma=%.4f.\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mfprime,pWF->Mtot),l,mprime,alpha_i,cos_beta,gamma);
1898 
1899  }
1900 
1901 
1902 
1903  else {
1904 
1905  if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321)
1906  {
1907 
1908  alpha_i=alphaMRD(Mfprime,pPrec->alpha_params);
1909  cos_beta=cos(betaMRD(Mfprime,pWF,pPrec->beta_params));
1910  // if gamma was not previously initialised, try to evaluate it with the precomputed spline
1911  if(pPrec->gamma_in==0.){
1912  success = gsl_spline_eval_e(pPrec->gamma_spline, Mfprime, pPrec->gamma_acc, &gamma);
1913  XLAL_CHECK(success == XLAL_SUCCESS, XLAL_EFUNC, "%s: Failed to evaluate gamma at f=%.7f for (l,m)=(%d,%d).\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mfprime,pWF->Mtot),l,mprime);
1914  }
1915  else{
1916  REAL8 deltagamma=0.;
1917  success = gamma_from_alpha_cosbeta(&deltagamma, Mfprime, pWF->deltaMF*2./mprime,pWF,pPrec);
1918  XLAL_CHECK(success == XLAL_SUCCESS, XLAL_EFUNC, "%s: Failed to evaluate gamma at f=%.7f for (l,m)=(%d,%d).\n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mfprime,pWF->Mtot),l,mprime);
1919  gamma =pPrec->gamma_in+deltagamma;
1920 
1921  }
1922 
1923  }
1924  else{
1925  // just repeat the last cached value for the Euler angles
1926  alpha_i = pPrec->alpha_ftrans;
1927  cos_beta = pPrec->cosbeta_ftrans;
1928  gamma = pPrec->gamma_ftrans;
1929 
1930 
1931  }
1932  }
1933 
1934 
1935  pPrec->gamma_in = gamma;
1936 
1937  REAL8 alpha_offset_mprime = pPrec->alpha_ref- pPrec->alpha0;
1938  REAL8 epsilon_offset_mprime = -pPrec->gamma_ref-pPrec->epsilon0;
1939 
1940  alpha = alpha_i - alpha_offset_mprime;
1941  epsilon = -gamma - epsilon_offset_mprime;
1942 
1943  INT4 status = 0;
1944  status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1945  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1946  break;
1947  }
1948 
1949 
1950 
1951  default:
1952  {
1953  XLAL_ERROR(XLAL_EINVAL,"Error. IMRPhenomXPrecVersion not recognized. Recommended default is 223.\n");
1954  break;
1955  }
1956  }
1957  }
1958  cexp_i_alpha = cexp(+I*alpha);
1959 
1960  #if DEBUG == 1
1961  // Used for writing angles to file in debug mode.
1962  cexp_i_epsilon = cexp(+I*epsilon);
1963  pPrec->cexp_i_betah = cBetah + I*sBetah;
1964  #endif
1965 
1966  } // End of no multibanding
1967  else{ /* For Multibanding */
1968  cexp_i_alpha = pPrec->cexp_i_alpha;
1969  cexp_i_epsilon = pPrec->cexp_i_epsilon;
1970  cBetah = (pPrec->cexp_i_betah + 1./pPrec->cexp_i_betah)*0.5;
1971  sBetah = (pPrec->cexp_i_betah - 1./pPrec->cexp_i_betah)*0.5/I;
1972  } // End of Multibanding-specific
1973 
1974  /* Useful powers of the Wigner coefficients */
1975  REAL8 cBetah2 = cBetah * cBetah;
1976  REAL8 cBetah3 = cBetah * cBetah2;
1977  REAL8 cBetah4 = cBetah * cBetah3;
1978  REAL8 cBetah5 = cBetah * cBetah4;
1979  REAL8 cBetah6 = cBetah * cBetah5;
1980  REAL8 cBetah7 = cBetah * cBetah6;
1981  REAL8 cBetah8 = cBetah * cBetah7;
1982 
1983  REAL8 sBetah2 = sBetah * sBetah;
1984  REAL8 sBetah3 = sBetah * sBetah2;
1985  REAL8 sBetah4 = sBetah * sBetah3;
1986  REAL8 sBetah5 = sBetah * sBetah4;
1987  REAL8 sBetah6 = sBetah * sBetah5;
1988  REAL8 sBetah7 = sBetah * sBetah6;
1989  REAL8 sBetah8 = sBetah * sBetah7;
1990 
1991  /*
1992  The following expressions for the Wigner-d coefficients correspond to those in appendix A of the Precessing paper.
1993  They are the same expressions used in IMRPhenomXPHMTwistUpOneMode.
1994  */
1995 
1996  COMPLEX16 hp_sum = 0;
1997  COMPLEX16 hc_sum = 0;
1998 
1999  /* Sum over l = 2 modes */
2000  if (l == 2 && mprime == 2){
2001  /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2002  /* Precompute powers of e^{i m alpha} */
2003  COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2004 
2005  COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2006  COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2007 
2008  COMPLEX16 cexp_im_alpha_l2[5] = {cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha};
2009 
2010  COMPLEX16 Y2mA[5] = {pPrec->Y2m2, pPrec->Y2m1, pPrec->Y20, pPrec->Y21, pPrec->Y22};
2011 
2012  // d^2_{-2,2} d^2_{-1,2} d^2_{0,2} d^2_{1,2} d^2_{2,2}
2013  COMPLEX16 d22[5] = {sBetah4, 2.0*cBetah*sBetah3, pPrec->sqrt6*sBetah2*cBetah2, 2.0*cBetah3*sBetah, cBetah4};
2014  // d^2_{-2,-2} d^2_{-1,-2} d^2_{0,-2} d^2_{1,-2} d^2_{2,-2}
2015  COMPLEX16 d2m2[5] = {d22[4], -d22[3], d22[2], -d22[1], d22[0]}; /* Exploit symmetry d^2_{-m,-2} = (-1)^m d^2_{-m,2}. See eq. A2 of Precessing paper */
2016 
2017  REAL8 polarizationSymmetry = pPrec->PolarizationSymmetry;
2018 
2019  for(int m=-2; m<=2; m++)
2020  {
2021  /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper */
2022  COMPLEX16 A2m2emm = cexp_im_alpha_l2[-m+2] * d2m2[m+2] * Y2mA[m+2];
2023  COMPLEX16 A22emmstar = cexp_im_alpha_l2[m+2] * d22[m+2] * conj(Y2mA[m+2]);
2024 
2025  hp_sum += A2m2emm + polarizationSymmetry * A22emmstar;
2026  hc_sum += I*(A2m2emm - polarizationSymmetry * A22emmstar);
2027  }
2028  }
2029 
2030  if (l == 2 && mprime == 1){
2031  /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2032  /* Precompute powers of e^{i m alpha} */
2033  COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2034 
2035  COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2036  COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2037 
2038  COMPLEX16 cexp_im_alpha_l2[5] = {cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha};
2039 
2040  COMPLEX16 Y2mA[5] = {pPrec->Y2m2, pPrec->Y2m1, pPrec->Y20, pPrec->Y21, pPrec->Y22};
2041 
2042  // d^2_{-2,1} d^2_{-1,1} d^2_{0,1} d^2_{1,1} d^2_{2,1}
2043  COMPLEX16 d21[5] = {2.0*cBetah*sBetah3, 3.0*cBetah2*sBetah2 - sBetah4, pPrec->sqrt6*(cBetah3*sBetah - cBetah*sBetah3), cBetah2*(cBetah2 - 3.0*sBetah2), -2.0*cBetah3*sBetah};
2044  // d^2_{-2,-1} d^2_{-1,-1} d^2_{0,-1} d^2_{1,-1} d^2_{2,-1}
2045  COMPLEX16 d2m1[5] = {-d21[4], d21[3], -d21[2], d21[1], -d21[0]}; /* Exploit symmetry d^2_{-m,-1} = -(-1)^m d^2_{m,1}. See eq. A2 of Precessing paper. */
2046 
2047 
2048  for(int m=-2; m<=2; m++)
2049  {
2050  /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper. */
2051  COMPLEX16 A2m1emm = cexp_im_alpha_l2[-m+2] * d2m1[m+2] * Y2mA[m+2];
2052  COMPLEX16 A21emmstar = cexp_im_alpha_l2[m+2] * d21[m+2] * conj(Y2mA[m+2]);
2053  hp_sum += A2m1emm + A21emmstar;
2054  hc_sum += I*(A2m1emm - A21emmstar);
2055  }
2056 
2057  }
2058 
2059  /* Sum over l = 3 modes */
2060  if (l == 3 && mprime == 3){
2061  /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2062  /* Precompute powers of e^{i m alpha} */
2063  COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2064  COMPLEX16 cexp_3i_alpha = cexp_i_alpha * cexp_2i_alpha;
2065 
2066  COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2067  COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2068  COMPLEX16 cexp_m3i_alpha = cexp_mi_alpha * cexp_m2i_alpha;
2069 
2070  COMPLEX16 cexp_im_alpha_l3[7] = {cexp_m3i_alpha, cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha, cexp_3i_alpha};
2071 
2072  COMPLEX16 Y3mA[7] = {pPrec->Y3m3, pPrec->Y3m2, pPrec->Y3m1, pPrec->Y30, pPrec->Y31, pPrec->Y32, pPrec->Y33};
2073 
2074  // d^3_{-3,3} d^3_{-2,3} d^3_{-1,3} d^3_{0,3} d^3_{1,3} d^3_{2,3} d^3_{3,3}
2075  COMPLEX16 d33[7] = {sBetah6, pPrec->sqrt6*cBetah*sBetah5, pPrec->sqrt15*cBetah2*sBetah4, 2.0*pPrec->sqrt5*cBetah3*sBetah3, pPrec->sqrt15*cBetah4*sBetah2, pPrec->sqrt6*cBetah5*sBetah, cBetah6};
2076  // d^3_{-3,-3} d^3_{-2,-3} d^3_{-1,-3} d^3_{0,-3} d^3_{1,-3} d^3_{2,-3} d^3_{3,-3}
2077  COMPLEX16 d3m3[7] = {d33[6], -d33[5], d33[4], -d33[3], d33[2], -d33[1], d33[0]}; /* Exploit symmetry d^3_{-m,-3} = -(-1)^m d^3_{m,3}. See eq. A2 of Precessing paper. */
2078 
2079  for(int m=-3; m<=3; m++)
2080  {
2081  /* Transfer functions */
2082  COMPLEX16 A3m3emm = cexp_im_alpha_l3[-m+3] * d3m3[m+3] * Y3mA[m+3];
2083  COMPLEX16 A33emmstar = cexp_im_alpha_l3[m+3] * d33[m+3] * conj(Y3mA[m+3]);
2084  hp_sum += A3m3emm - A33emmstar;
2085  hc_sum += I*(A3m3emm + A33emmstar);
2086  }
2087  }
2088 
2089  /* Sum over l = 3 modes */
2090  if (l == 3 && mprime == 2){
2091  /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2092  /* Precompute powers of e^{i m alpha} */
2093  COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2094  COMPLEX16 cexp_3i_alpha = cexp_i_alpha * cexp_2i_alpha;
2095 
2096  COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2097  COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2098  COMPLEX16 cexp_m3i_alpha = cexp_mi_alpha * cexp_m2i_alpha;
2099 
2100  COMPLEX16 cexp_im_alpha_l3[7] = {cexp_m3i_alpha, cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha, cexp_3i_alpha};
2101 
2102  COMPLEX16 Y3mA[7] = {pPrec->Y3m3, pPrec->Y3m2, pPrec->Y3m1, pPrec->Y30, pPrec->Y31, pPrec->Y32, pPrec->Y33};
2103 
2104  // d^3_{-3,2} d^3_{-2,2} d^3_{-1,21} d^3_{0,2} d^3_{1,2} d^3_{2,2} d^3_{3,2}
2105  COMPLEX16 d32[7] = {pPrec->sqrt6*cBetah*sBetah5, sBetah4*(5.0*cBetah2 - sBetah2), pPrec->sqrt10*sBetah3*(2.0*cBetah3 - cBetah*sBetah2), pPrec->sqrt30*cBetah2*(cBetah2 - sBetah2)*sBetah2, pPrec->sqrt10*cBetah3*(cBetah2*sBetah - 2.0*sBetah3), cBetah4*(cBetah2 - 5.0*sBetah2), -1.0*pPrec->sqrt6*cBetah5*sBetah};
2106  // d^3_{-3,-2} d^3_{-2,-2} d^3_{-1,-2} d^3_{0,-2} d^3_{1,-2} d^3_{2,-2} d^3_{3,-2}
2107  COMPLEX16 d3m2[7] = {-d32[6], d32[5], -d32[4], d32[3], -d32[2], d32[1], -d32[0]}; /* Exploit symmetry d^3_{-m,-2} = (-1)^m d^3_{m,2}. See eq. A2 of Precessing paper. */
2108 
2109 
2110  for(int m=-3; m<=3; m++)
2111  {
2112  /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper. */
2113  COMPLEX16 A3m2emm = cexp_im_alpha_l3[-m+3] * d3m2[m+3] * Y3mA[m+3];
2114  COMPLEX16 A32emmstar = cexp_im_alpha_l3[m+3] * d32[m+3] * conj(Y3mA[m+3]);
2115  hp_sum += A3m2emm - A32emmstar;
2116  hc_sum += I*(A3m2emm + A32emmstar);
2117  }
2118 
2119  }
2120 
2121  /* Sum over l = 4 modes */
2122  if (l == 4 && mprime == 4){
2123  /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2124  /* Precompute powers of e^{i m alpha} */
2125  COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2126  COMPLEX16 cexp_3i_alpha = cexp_i_alpha * cexp_2i_alpha;
2127  COMPLEX16 cexp_4i_alpha = cexp_i_alpha * cexp_3i_alpha;
2128 
2129  COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2130  COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2131  COMPLEX16 cexp_m3i_alpha = cexp_mi_alpha * cexp_m2i_alpha;
2132  COMPLEX16 cexp_m4i_alpha = cexp_mi_alpha * cexp_m3i_alpha;
2133 
2134  COMPLEX16 cexp_im_alpha_l4[9] = {cexp_m4i_alpha, cexp_m3i_alpha, cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha, cexp_3i_alpha, cexp_4i_alpha};
2135 
2136  COMPLEX16 Y4mA[9] = {pPrec->Y4m4, pPrec->Y4m3, pPrec->Y4m2, pPrec->Y4m1, pPrec->Y40, pPrec->Y41, pPrec->Y42, pPrec->Y43, pPrec->Y44};
2137 
2138  // d^4_{-4,4} d^4_{-3,4} d^4_{-2,4} d^4_{-1,4} d^4_{0,4} d^4_{1,4} d^4_{2,4} d^4_{3,4} d^4_{44}
2139  COMPLEX16 d44[9] = {sBetah8, 2.0*pPrec->sqrt2*cBetah*sBetah7, 2.0*pPrec->sqrt7*cBetah2*sBetah6, 2.0*pPrec->sqrt14*cBetah3*sBetah5, pPrec->sqrt70*cBetah4*sBetah4, 2.0*pPrec->sqrt14*cBetah5*sBetah3, 2.0*pPrec->sqrt7*cBetah6*sBetah2, 2.0*pPrec->sqrt2*cBetah7*sBetah, cBetah8};
2140  // d^4_{4,-4} d^4_{-3,-4} d^4_{-2,-4} d^4_{-1,-4} d^4_{0,-4} d^4_{1,-4} d^4_{2,-4} d^4_{3,-4} d^4_{4-4}
2141  COMPLEX16 d4m4[9] = {d44[8], -d44[7], d44[6], -d44[5], d44[4], -d44[3], d44[2], -d44[1], d44[0]}; /* Exploit symmetry d^4_{-m,-4} = (-1)^m d^4_{m,4}. See eq. A2 of Precessing paper. */
2142 
2143  for(int m=-4; m<=4; m++)
2144  {
2145  /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper. */
2146  COMPLEX16 A4m4emm = cexp_im_alpha_l4[-m+4] * d4m4[m+4] * Y4mA[m+4];
2147  COMPLEX16 A44emmstar = cexp_im_alpha_l4[m+4] * d44[m+4] * conj(Y4mA[m+4]);
2148  hp_sum += A4m4emm + A44emmstar;
2149  hc_sum += I*(A4m4emm - A44emmstar);
2150  }
2151  }
2152 
2153  /* Sum over l = 4 modes. This is only used when twisting PhenomHM. */
2154  if (l == 4 && mprime == 3){
2155  /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2156  /* Precompute powers of e^{i m alpha} */
2157  COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2158  COMPLEX16 cexp_3i_alpha = cexp_i_alpha * cexp_2i_alpha;
2159  COMPLEX16 cexp_4i_alpha = cexp_i_alpha * cexp_3i_alpha;
2160 
2161  COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2162  COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2163  COMPLEX16 cexp_m3i_alpha = cexp_mi_alpha * cexp_m2i_alpha;
2164  COMPLEX16 cexp_m4i_alpha = cexp_mi_alpha * cexp_m3i_alpha;
2165 
2166  COMPLEX16 cexp_im_alpha_l4[9] = {cexp_m4i_alpha, cexp_m3i_alpha, cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha, cexp_3i_alpha, cexp_4i_alpha};
2167 
2168  COMPLEX16 Y4mA[9] = {pPrec->Y4m4, pPrec->Y4m3, pPrec->Y4m2, pPrec->Y4m1, pPrec->Y40, pPrec->Y41, pPrec->Y42, pPrec->Y43, pPrec->Y44};
2169 
2170  // d^4_{-4,3} d^4_{-3,3} d^4_{-2,3} d^4_{-1,3} d^4_{0,3} d^4_{1,3} d^4_{2,3} d^4_{3,3} d^4_{43}
2171  COMPLEX16 d43[9] = {2*pPrec->sqrt2*cBetah*sBetah7, 7*cBetah2*sBetah6-sBetah8, pPrec->sqrt14*(3*cBetah3*sBetah5-cBetah*sBetah7), pPrec->sqrt7*(5*cBetah4*sBetah4-3*cBetah2*sBetah6), 2*5.916079783099616*(cBetah5*sBetah3-cBetah3*sBetah5), pPrec->sqrt7*(3*cBetah6*sBetah2-5*cBetah4*sBetah4), pPrec->sqrt14*(cBetah7*sBetah-3*cBetah5*sBetah3), cBetah8-7*cBetah6*sBetah2, -2.*pPrec->sqrt2*cBetah7*sBetah};
2172  // d^4_{4,-3} d^4_{-3,-3} d^4_{-2,-3} d^4_{-1,-3} d^4_{0,-3} d^4_{1,-3} d^4_{2,-3} d^4_{3,-3} d^4_{4-3}
2173  COMPLEX16 d4m3[9] = {-d43[8], d43[7], -d43[6], d43[5], -d43[4], d43[3], -d43[2], d43[1], -d43[0]}; /* Exploit symmetry d^4_{-m,-3} = -(-1)^m d^4_{m,3}. See eq. A2 of Precessing paper. */
2174 
2175  for(int m=-4; m<=4; m++)
2176  {
2177  /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper. */
2178  COMPLEX16 A4m3emm = cexp_im_alpha_l4[-m+4] * d4m3[m+4] * Y4mA[m+4];
2179  COMPLEX16 A43emmstar = cexp_im_alpha_l4[m+4] * d43[m+4] * conj(Y4mA[m+4]);
2180  hp_sum += A4m3emm + A43emmstar;
2181  hc_sum += I*(A4m3emm - A43emmstar);
2182  }
2183  }
2184 
2185  COMPLEX16 eps_phase_hP_lmprime;
2186 
2187  if(pPrec->MBandPrecVersion == 0) // No multibanding
2188  {
2189  eps_phase_hP_lmprime = cexp(-1.*mprime*I*epsilon) * hlmprime / 2.0;
2190  }
2191  else{ // With multibanding
2192  COMPLEX16 exp_imprime_epsilon = cexp_i_epsilon;
2193  for(INT4 i=1; i<mprime; i++)
2194  {
2195  exp_imprime_epsilon *= cexp_i_epsilon;
2196  }
2197  eps_phase_hP_lmprime = 1./exp_imprime_epsilon * hlmprime / 2.0;
2198  }
2199 
2200 
2201  /* Return h_+ and h_x */
2202  *hp += eps_phase_hP_lmprime * hp_sum;
2203  *hc += eps_phase_hP_lmprime * hc_sum;
2204 
2205  #if DEBUG == 1
2206  /* Save angles in output file. */
2207  FILE *fileangle;
2208  char fileSpec[40];
2209 
2210  if(pPrec->MBandPrecVersion == 0)
2211  {
2212  sprintf(fileSpec, "angles_hphc_%i%i.dat", l, mprime);
2213  }
2214  else
2215  {
2216  sprintf(fileSpec, "angles_hphc_MB_%i%i.dat", l, mprime);
2217  }
2218  fileangle = fopen(fileSpec,"a");
2219 
2220  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(pPrec->cexp_i_betah), cimag(pPrec->cexp_i_betah));
2221  fclose(fileangle);
2222  #endif
2223 
2224  return XLAL_SUCCESS;
2225 }
2226 
2227 /* @} */
2228 /* @} */
2229 
2230 /** @addtogroup LALSimIMRPhenomX_c
2231 * @{
2232 * @name Routines for IMRPhenomXPHM
2233 * @{
2234 * @author Cecilio García Quirós, Geraint Pratten
2235 *
2236 * @brief C code for IMRPhenomXPHM phenomenological waveform model.
2237 *
2238 * This is a frequency domain precessing model based on the twisting-up of the aligned spin model with higher modes IMRPhenomXHM.
2239 * See G.Pratten et al arXiv:2004.06503 for details. Any studies that use this waveform model should include
2240 * a reference to this paper.
2241 *
2242 * @note DCC link to the paper: https://dcc.ligo.org/LIGO-P2000039. This paper will be refered in the code as the "Precessing paper".
2243 *
2244 * Waveform flags:
2245 *
2246 * All the flags for IMRPhenomXP apply here plus the following ones:
2247 *
2248 * TwistPhenomHM: option to twist-up the AS model PhenomHM instead of PhenomXHM. It is only available for the polarizations, not for individual modes.
2249 * - 0: (DEFAULT) twist-up PhenomXHM
2250 * - 1: twist-up PhenomHM
2251 *
2252 * UseModes: Determine how the polarizations hp, hc are computed.
2253 * - 0: (DEFAULT) Compute the non-precessing modes once and do the twistin up as in eq. 3.5-3.7 in the Precessing paper.
2254 * - 1: Compute first the individual precessing modes in the inertial J-frame and sum them to get the polarizations.
2255 *
2256 * ModesL0Frame: Determine in which frame the individual precessing modes are returned.
2257 * - 0: inertial J-frame (DEFAULT).
2258 * - 1: inertial L0-frame (only working near the aligned spin limit).
2259 *
2260 * PrecModes: Determine which indiviual modes are returned, the non-precessing or the precessing.
2261 * - 0: (DEFAULT) Return the precessing individual mode in the J-frame.
2262 * - 1: Return the non-precessing individual mode before the twisting-up with the modified final spin.
2263 *
2264 * Multibanding flags:
2265 *
2266 * PrecThresholdMband: Determines the accuracy and speed of the Multibanding algorithm for the Euler angles. The higher the threshold the faster is the algorithm but also less accurate.
2267 * - 0.001 (DEFAULT)
2268 * - 0: Switch off the multibanding.
2269 *
2270 * MBandPrecVersion: Determines the algorithm to build the non-uniform frequency grid for the Euler angles.
2271 * - 0: (DEFAULT) Not use multibanding. Activated to 1 when PrecThresholdMband is non-zero.
2272 * - 1: Use the same grid that for the non-precessing modes. Activated when PrecThresholdMband is non-zero.
2273 **/
2274 
2275 
2276 
2277 /*********************************************/
2278 /* */
2279 /* SINGLE-MODE PRECESSING FUNCTIONS */
2280 /* */
2281 /*********************************************/
2282 
2283 /**
2284  Function to compute one hlm precessing mode in an uniform frequency grid.
2285  By default the mode is given in the inertial J-frame. It can be transformed to the L0-frame with the option "PhenomXPHMModesL0Frame" which
2286  currently only works for cases near AS limit.
2287  It can return the co-precessing mode with the option "PhenomXPHMPrecModes": this corresponds to the XHM mode with the modified
2288  ringdown/damping frequencies of the precessing final spin. In this case only m<0 are supported, so hlmneg will be filled with zeros.
2289  Returns two frequency series, one for the positive frequencies and other for the negative frequencies since, as opposite to the
2290  aligned spin case, in the precessing case all the modes have support in the whole frequency regime.
2291  This is a wrapper of the internal core function that actually does the calculation IMRPhenomXPHM_OneMode.
2292 */
2294  COMPLEX16FrequencySeries **hlmpos, /**< [out] Frequency-domain waveform hlm inertial frame positive frequencies */
2295  COMPLEX16FrequencySeries **hlmneg, /**< [out] Frequency-domain waveform hlm inertial frame negative frequencies */
2296  const UINT4 l, /**< First index of the (l,m) precessing mode */
2297  const INT4 m, /**< Second index of the (l,m) precessing mode */
2298  REAL8 m1_SI, /**< mass of companion 1 (kg) */
2299  REAL8 m2_SI, /**< mass of companion 2 (kg) */
2300  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2301  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2302  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2303  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2304  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2305  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2306  const REAL8 distance, /**< distance of source (m) */
2307  const REAL8 inclination, /**< inclination of source (rad) */
2308  const REAL8 phiRef, /**< reference orbital phase (rad) */
2309  const REAL8 deltaF, /**< Sampling frequency (Hz) */
2310  const REAL8 f_min, /**< Starting GW frequency (Hz) */
2311  const REAL8 f_max, /**< End frequency; 0 defaults to ringdown cutoff freq */
2312  const REAL8 fRef_In, /**< Reference frequency */
2313  LALDict *lalParams /**<LAL Dictionary */
2314 )
2315 {
2316  /* Variable to check correct calls to functions. */
2317  INT4 status;
2318 
2319  /* Check if m1 > m2, swap the bodies otherwise. */
2320  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2321  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2322 
2323  #if DEBUG == 1
2324  printf("fRef_In : %e\n",fRef_In);
2325  printf("m1_SI : %e\n",m1_SI);
2326  printf("m2_SI : %e\n",m2_SI);
2327  printf("chi1_l : %e\n",chi1z);
2328  printf("chi2_l : %e\n",chi2z);
2329  printf("phiRef : %e\n",phiRef);
2330  printf("Prec V. : %d\n\n",XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams));
2331  printf("Performing sanity checks...\n");
2332  #endif
2333 
2334  /* Perform initial sanity checks */
2335  XLAL_CHECK(NULL != hlmpos, XLAL_EFAULT, "Error: hlmpos already defined. \n");
2336  XLAL_CHECK(NULL != hlmneg, XLAL_EFAULT, "Error: hlmneg already defined. \n");
2337  XLAL_CHECK(fRef_In >= 0, XLAL_EFUNC, "Error: fRef_In must be positive or set to 0 to ignore. \n");
2338  XLAL_CHECK(deltaF > 0, XLAL_EFUNC, "Error: deltaF must be positive and greater than 0. \n");
2339  XLAL_CHECK(m1_SI > 0, XLAL_EFUNC, "Error: m1 must be positive and greater than 0. \n");
2340  XLAL_CHECK(m2_SI > 0, XLAL_EFUNC, "Error: m2 must be positive and greater than 0. \n");
2341  XLAL_CHECK(f_min > 0, XLAL_EFUNC, "Error: f_min must be positive and greater than 0. \n");
2342  XLAL_CHECK(f_max >= 0, XLAL_EFUNC, "Error: f_max must be non-negative. \n");
2343  XLAL_CHECK(distance > 0, XLAL_EFUNC, "Error: Distance must be positive and greater than 0. \n");
2344 
2345  /*
2346  Perform a basic sanity check on the region of the parameter space in which model is evaluated.
2347  Behaviour is as follows, consistent with the choices for IMRPhenomXAS/IMRPhenomXHM
2348  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
2349  - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
2350  - For mass ratios > 1000: throw a hard error that model is not valid.
2351  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
2352 
2353  */
2354  REAL8 mass_ratio;
2355  if(m1_SI > m2_SI)
2356  {
2357  mass_ratio = m1_SI / m2_SI;
2358  }
2359  else
2360  {
2361  mass_ratio = m2_SI / m1_SI;
2362  }
2363  if(mass_ratio > 20.0 ) { XLAL_PRINT_WARNING("Warning: Extrapolating outside of Numerical Relativity calibration domain. NNLO angles may become pathological at large mass ratios.\n"); }
2364  if(mass_ratio > 1000. && fabs(mass_ratio - 1000) > 1e-12) { XLAL_ERROR(XLAL_EDOM, "ERROR: Model not valid at mass ratios beyond 1000.\n"); } // The 1e-12 is to avoid rounding errors
2365  if(fabs(chi1z) > 0.99 || fabs(chi2z) > 0.99) { XLAL_PRINT_WARNING("Warning: Extrapolating to extremal spins, model is not trusted.\n"); }
2366 
2367  /* If no reference frequency is given, set it to the starting gravitational wave frequency. */
2368  REAL8 fRef = (fRef_In == 0.0) ? f_min : fRef_In;
2369 
2370  /* Use an auxiliar laldict to not overwrite the input argument */
2371  LALDict *lalParams_aux;
2372  /* setup mode array */
2373  if (lalParams == NULL)
2374  {
2375  lalParams_aux = XLALCreateDict();
2376  }
2377  else{
2378  lalParams_aux = XLALDictDuplicate(lalParams);
2379  }
2380  /* Check that the modes chosen are available for the model */
2381  XLAL_CHECK(check_input_mode_array(lalParams_aux) == XLAL_SUCCESS, XLAL_EFAULT, "Not available mode chosen.\n");
2382 
2383  #if DEBUG == 1
2384  printf("\n\n **** Initializing waveform struct... **** \n\n");
2385  #endif
2386 
2387  /* Initialize the useful powers of LAL_PI */
2389  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
2390 
2391  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly. */
2392  /* We pass inclination 0 since for the individual modes is not relevant. */
2394  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2395  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1z, chi2z, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, PHENOMXDEBUG);
2396  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2397 
2398  /*
2399  Create a REAL8 frequency series.
2400  Use fLow, fHigh, deltaF to compute frequency sequence. Only pass the boundaries (fMin, fMax).
2401  */
2403  freqs->data[0] = pWF->fMin;
2404  freqs->data[1] = pWF->f_max_prime;
2405 
2406 
2407  #if DEBUG == 1
2408  printf("\n\n **** Initializing precession struct... **** \n\n");
2409  #endif
2410 
2411  /* Initialize IMRPhenomX Precession struct and check that it generated successfully. */
2413  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2414 
2415  // only relevant for SpinTaylor angles
2417  if(pflag==310||pflag==311||pflag==320||pflag==321){
2418  pPrec->M_MIN = m, pPrec->M_MAX = l;
2419  }
2420 
2421 
2423  pWF,
2424  pPrec,
2425  m1_SI,
2426  m2_SI,
2427  chi1x,
2428  chi1y,
2429  chi1z,
2430  chi2x,
2431  chi2y,
2432  chi2z,
2433  lalParams_aux,
2434  PHENOMXDEBUG
2435  );
2436  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2437 
2438  if(pPrec->precessing_tag==3){
2439 
2440  status=IMRPhenomX_Initialize_Euler_Angles(pWF,pPrec,lalParams);
2441  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_Initialize_Euler_Angles failed.\n");
2442  }
2443 
2444  /* Ensure recovering AS limit when modes are in the L0 frame. */
2446  {
2447  XLAL_PRINT_WARNING("The L0Frame option only works near the AS limit, it should not be used otherwise.");
2449  {
2450  case 0:
2451  case 5:
2452  //pWF->phi0 = pPrec->phi0_aligned;
2453  break;
2454  case 1:
2455  case 6:
2456  case 7:
2457  {
2458  //pWF->phi0 = pPrec->epsilon0 - pPrec->alpha0 + phiRef;
2459  pWF->phi0 = phiRef;
2460  break;
2461  }
2462  }
2463  }
2464 
2465 
2466  #if DEBUG == 1
2467  printf("\n\n **** Calling IMRPhenomXPHM_OneMode... **** \n\n");
2468  #endif
2469 
2470  /* We now call the core IMRPhenomXPHM_OneMode waveform generator */
2471  status = IMRPhenomXPHM_OneMode(hlmpos, hlmneg, freqs, pWF, pPrec, l, m, lalParams_aux);
2472  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_OneMode failed to generate IMRPhenomXHM waveform.");
2473 
2474  /* Tranform modes to L0-frame if requested. It only works for (near) AS cases. */
2476  {
2478  {
2479  case 0:
2480  case 5:
2481  //pWF->phi0 = pPrec->phi0_aligned;
2482  break;
2483  case 1:
2484  case 6:
2485  case 7:
2486  {
2487  COMPLEX16 shiftpos = cexp( abs(m)*I*( pPrec->epsilon0 - pPrec->alpha0) );
2488  COMPLEX16 shiftneg = 1./shiftpos;
2489 
2490  for(UINT4 i = 0; i<(*hlmpos)->data->length; i++)
2491  {
2492  (*hlmpos)->data->data[i] *= shiftpos;
2493  (*hlmneg)->data->data[i] *= shiftneg;
2494  }
2495  break;
2496  }
2497  }
2498  }
2499 
2500  #if DEBUG == 1
2501  printf("\n\n **** Call to IMRPhenomXPHM_OneMode complete. **** \n\n");
2502  #endif
2503 
2504  /* Resize hptilde, hctilde */
2505  REAL8 lastfreq;
2506  if (pWF->f_max_prime < pWF->fMax)
2507  {
2508  /* The user has requested a higher f_max than Mf = fCut.
2509  Resize the frequency series to fill with zeros beyond the cutoff frequency. */
2510  lastfreq = pWF->fMax;
2511  XLAL_PRINT_WARNING("The input f_max = %.2f Hz is larger than the internal cutoff of Mf=0.3 (%.2f Hz). Array will be filled with zeroes between these two frequencies.\n", pWF->fMax, pWF->f_max_prime);
2512  }
2513  else{ // We have to look for a power of 2 anyway.
2514  lastfreq = pWF->f_max_prime;
2515  }
2516  // We want to have the length be a power of 2 + 1
2517  size_t n_full = NextPow2(lastfreq / deltaF) + 1;
2518  size_t n = (*hlmpos)->data->length;
2519 
2520  /* Resize the COMPLEX16 frequency series */
2521  *hlmpos = XLALResizeCOMPLEX16FrequencySeries(*hlmpos, 0, n_full);
2522  XLAL_CHECK (*hlmpos, XLAL_ENOMEM, "Failed to resize hlmpos COMPLEX16FrequencySeries of length %zu (for internal fCut=%f) to new length %zu (for user-requested f_max=%f).", n, pWF->fCut, n_full, pWF->fMax );
2523 
2524  /* Resize the COMPLEX16 frequency series */
2525  *hlmneg = XLALResizeCOMPLEX16FrequencySeries(*hlmneg, 0, n_full);
2526  XLAL_CHECK (*hlmneg, XLAL_ENOMEM, "Failed to resize hlmneg COMPLEX16FrequencySeries of length %zu (for internal fCut=%f) to new length %zu (for user-requested f_max=%f).", n, pWF->fCut, n_full, pWF->fMax );
2527 
2528 
2529  if(pPrec->precessing_tag==3)
2530  {
2531  LALFree(pPrec->alpha_params);
2532  LALFree(pPrec->beta_params);
2533 
2534  gsl_spline_free(pPrec->alpha_spline);
2535  gsl_spline_free(pPrec->cosbeta_spline);
2536  gsl_spline_free(pPrec->gamma_spline);
2537 
2538  gsl_interp_accel_free(pPrec->alpha_acc);
2539  gsl_interp_accel_free(pPrec->gamma_acc);
2540  gsl_interp_accel_free(pPrec->cosbeta_acc);
2541  }
2542 
2543 
2544  /* Free memory */
2545  LALFree(pWF);
2546  LALFree(pPrec);
2547  XLALDestroyREAL8Sequence(freqs);
2548  XLALDestroyDict(lalParams_aux);
2549 
2550 
2551 
2552  return XLAL_SUCCESS;
2553 }
2554 
2555 /**
2556  Function to compute one hlm precessing mode on a custom frequency grid.
2557  Equivalent options and behaviour to that of XLALSimIMRPhenomXPHMOneMode.
2558 */
2560  COMPLEX16FrequencySeries **hlmpos, /**< [out] Frequency-domain waveform hlm inertial frame positive frequencies */
2561  COMPLEX16FrequencySeries **hlmneg, /**< [out] Frequency-domain waveform hlm inertial frame negative frequencies */
2562  const REAL8Sequence *freqs, /**< Input Frequency series [Hz] */
2563  const UINT4 l, /**< First index of the (l,m) precessing mode */
2564  const INT4 m, /**< Second index of the (l,m) precessing mode */
2565  REAL8 m1_SI, /**< mass of companion 1 (kg) */
2566  REAL8 m2_SI, /**< mass of companion 2 (kg) */
2567  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2568  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2569  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2570  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2571  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2572  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2573  const REAL8 distance, /**< distance of source (m) */
2574  const REAL8 inclination, /**< inclination of source (rad) */
2575  const REAL8 phiRef, /**< reference orbital phase (rad) */
2576  const REAL8 fRef_In, /**< Reference frequency */
2577  LALDict *lalParams /**<LAL Dictionary */
2578 )
2579 {
2580  /* Variable to check correct calls to functions. */
2581  INT4 status;
2582 
2583  /* Check if m1 > m2, swap the bodies otherwise. */
2584  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2585  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2586 
2587  #if DEBUG == 1
2588  printf("fRef_In : %e\n",fRef_In);
2589  printf("m1_SI : %e\n",m1_SI);
2590  printf("m2_SI : %e\n",m2_SI);
2591  printf("chi1_l : %e\n",chi1z);
2592  printf("chi2_l : %e\n",chi2z);
2593  printf("phiRef : %e\n",phiRef);
2594  printf("Prec V. : %d\n\n",XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams));
2595  printf("Performing sanity checks...\n");
2596  #endif
2597 
2598  /* Perform initial sanity checks */
2599  XLAL_CHECK(NULL != hlmpos, XLAL_EFAULT, "Error: hlmpos already defined. \n");
2600  XLAL_CHECK(NULL != hlmneg, XLAL_EFAULT, "Error: hlmneg already defined. \n");
2601  XLAL_CHECK(fRef_In >= 0, XLAL_EFUNC, "Error: fRef_In must be positive or set to 0 to ignore. \n");
2602  XLAL_CHECK(m1_SI > 0, XLAL_EFUNC, "Error: m1 must be positive and greater than 0. \n");
2603  XLAL_CHECK(m2_SI > 0, XLAL_EFUNC, "Error: m2 must be positive and greater than 0. \n");
2604  XLAL_CHECK(distance > 0, XLAL_EFUNC, "Error: Distance must be positive and greater than 0. \n");
2605 
2606  /* Get minimum and maximum frequencies. */
2607  REAL8 f_min = freqs->data[0];
2608  REAL8 f_max = freqs->data[freqs->length - 1];
2609 
2610  /*
2611  Perform a basic sanity check on the region of the parameter space in which model is evaluated.
2612  Behaviour is as follows, consistent with the choices for IMRPhenomXAS/IMRPhenomXHM
2613  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
2614  - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
2615  - For mass ratios > 1000: throw a hard error that model is not valid.
2616  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
2617  */
2618  REAL8 mass_ratio;
2619  if(m1_SI > m2_SI)
2620  {
2621  mass_ratio = m1_SI / m2_SI;
2622  }
2623  else
2624  {
2625  mass_ratio = m2_SI / m1_SI;
2626  }
2627  if(mass_ratio > 20.0 ) { XLAL_PRINT_WARNING("Warning: Extrapolating outside of Numerical Relativity calibration domain. NNLO angles may become pathological at large mass ratios.\n"); }
2628  if(mass_ratio > 1000. && fabs(mass_ratio - 1000) > 1e-12) { XLAL_ERROR(XLAL_EDOM, "ERROR: Model not valid at mass ratios beyond 1000.\n"); } // The 1e-12 is to avoid rounding errors
2629  if(fabs(chi1z) > 0.99 || fabs(chi2z) > 0.99) { XLAL_PRINT_WARNING("Warning: Extrapolating to extremal spins, model is not trusted.\n"); }
2630 
2631  /* If no reference frequency is given, set it to the starting gravitational wave frequency. */
2632  REAL8 fRef = (fRef_In == 0.0) ? f_min : fRef_In;
2633 
2634  /* Use an auxiliar laldict to not overwrite the input argument */
2635  LALDict *lalParams_aux;
2636  /* setup mode array */
2637  if (lalParams == NULL)
2638  {
2639  lalParams_aux = XLALCreateDict();
2640  }
2641  else{
2642  lalParams_aux = XLALDictDuplicate(lalParams);
2643  }
2644 
2645  /* Check that the modes chosen are available for the model */
2646  XLAL_CHECK(check_input_mode_array(lalParams_aux) == XLAL_SUCCESS, XLAL_EFAULT, "Not available mode chosen.\n");
2647 
2648  #if DEBUG == 1
2649  printf("\n\n **** Initializing waveform struct... **** \n\n");
2650  #endif
2651 
2652  /* Initialize the useful powers of LAL_PI */
2654  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
2655 
2656  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly. */
2657  /* We pass inclination 0 since for the individual modes is not relevant. */
2659  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2660  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1z, chi2z, 0.0, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, PHENOMXDEBUG);
2661  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2662 
2663 
2664  #if DEBUG == 1
2665  printf("\n\n **** Initializing precession struct... **** \n\n");
2666  #endif
2667 
2668  /* Initialize IMRPhenomX Precession struct and check that it generated successfully. */
2670  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2671 
2672  // only relevant for SpinTaylor angles
2674  if(pflag==310||pflag==311||pflag==320||pflag==321){
2675  pPrec->M_MIN = m, pPrec->M_MAX = l;
2676  }
2677 
2679  pWF,
2680  pPrec,
2681  m1_SI,
2682  m2_SI,
2683  chi1x,
2684  chi1y,
2685  chi1z,
2686  chi2x,
2687  chi2y,
2688  chi2z,
2689  lalParams_aux,
2690  PHENOMXDEBUG
2691  );
2692  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2693 
2694  if(pPrec->precessing_tag==3){
2695 
2696  status=IMRPhenomX_Initialize_Euler_Angles(pWF,pPrec,lalParams);
2697  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_Initialize_Euler_Angles failed.\n");
2698  }
2699 
2700 
2701  /* Ensure recovering AS limit when modes are in the L0 frame. */
2703  {
2704  XLAL_PRINT_WARNING("The L0Frame option only works near the AS limit, it should not be used otherwise.");
2706  {
2707  case 0:
2708  case 5:
2709  //pWF->phi0 = pPrec->phi0_aligned;
2710  break;
2711  case 1:
2712  case 6:
2713  case 7:
2714  {
2715  //pWF->phi0 = pPrec->epsilon0 - pPrec->alpha0 + phiRef;
2716  pWF->phi0 = phiRef;
2717  break;
2718  }
2719  }
2720  }
2721 
2722  #if DEBUG == 1
2723  printf("\n\n **** Calling IMRPhenomXPHM_OneMode... **** \n\n");
2724  #endif
2725 
2726  // Ensure that multibanding is *always* off when calling a custom grid
2728 
2729  /* We now call the core IMRPhenomXPHM_OneMode waveform generator */
2730  status = IMRPhenomXPHM_OneMode(hlmpos, hlmneg, freqs, pWF, pPrec, l, m, lalParams_aux);
2731  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_OneMode failed to generate IMRPhenomXHM waveform.");
2732 
2733  /* Tranform modes to L0-frame if requested. It only works for (near) AS cases. */
2735  {
2737  {
2738  case 0:
2739  case 5:
2740  //pWF->phi0 = pPrec->phi0_aligned;
2741  break;
2742  case 1:
2743  case 6:
2744  case 7:
2745  {
2746  COMPLEX16 shiftpos = cexp( abs(m)*I*( pPrec->epsilon0 - pPrec->alpha0) );
2747  COMPLEX16 shiftneg = 1./shiftpos;
2748 
2749  for(UINT4 i = 0; i<(*hlmpos)->data->length; i++)
2750  {
2751  (*hlmpos)->data->data[i] *= shiftpos;
2752  (*hlmneg)->data->data[i] *= shiftneg;
2753  }
2754  break;
2755  }
2756  }
2757  }
2758 
2759  #if DEBUG == 1
2760  printf("\n\n **** Call to IMRPhenomXPHM_OneMode complete. **** \n\n");
2761  #endif
2762 
2763  //CHECK ME
2764 
2765  if(pPrec->precessing_tag==3)
2766  {
2767  LALFree(pPrec->alpha_params);
2768  LALFree(pPrec->beta_params);
2769 
2770  gsl_spline_free(pPrec->alpha_spline);
2771  gsl_spline_free(pPrec->cosbeta_spline);
2772  gsl_spline_free(pPrec->gamma_spline);
2773 
2774  gsl_interp_accel_free(pPrec->alpha_acc);
2775  gsl_interp_accel_free(pPrec->gamma_acc);
2776  gsl_interp_accel_free(pPrec->cosbeta_acc);
2777 
2778  }
2779 
2780  /* Free memory */
2781  LALFree(pWF);
2782  LALFree(pPrec);
2783  XLALDestroyDict(lalParams_aux);
2784 
2785  return XLAL_SUCCESS;
2786 }
2787 /** @}
2788 * @} **/
2789 
2790 
2791 /**
2792  Core funciton to compute an individual precessing mode hlm in the inertial J-frame.
2793  Returns two frequency series, one for the positive frequencies and other for the negative frequencies.
2794  It can be evaluated in a non-uniform frequency grid through the argument REAL8Seuqnce *freqs_In. This is in fact done when Calling
2795  XLALSimIMRPhenomXPHMFrequencySequence with the option of 'UseModes' activated.
2796 */
2798  COMPLEX16FrequencySeries **hlmpos, /**< [out] Frequency domain hlm GW strain inertial frame positive frequencies */
2799  COMPLEX16FrequencySeries **hlmneg, /**< [out] Frequency domain hlm GW strain inertial frame negative frequencies */
2800  const REAL8Sequence *freqs_In, /**< Input frequency grid (Hz) */
2801  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
2802  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
2803  UINT4 ell, /**< l index of the (l,m) precessing mode */
2804  INT4 m, /**< m index of the (l,m) precessing mode */
2805  LALDict *lalParams /**< LAL Dictionary Structure */
2806 )
2807 {
2808  if (pWF->f_max_prime <= pWF->fMin)
2809  XLAL_ERROR(XLAL_EDOM, "(fCut = %g Hz) <= f_min = %g\n", pWF->f_max_prime, pWF->fMin);
2810 
2811  /* Set LIGOTimeGPS */
2812  LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO; // = {0,0}
2813 
2814  REAL8 deltaF = pWF->deltaF;
2815 
2816  lalParams = IMRPhenomXPHM_setup_mode_array(lalParams);
2817  LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(lalParams);
2818 
2819  /* At this point ModeArray should contain the list of modes
2820  and therefore if NULL then something is wrong and abort. */
2821  if (ModeArray == NULL)
2822  {
2823  XLAL_ERROR(XLAL_EDOM, "ModeArray is NULL when it shouldn't be. Aborting.\n");
2824  }
2825 
2826  /* Check that the co-precessing ModeArray has at least one ell mode. If not, twisting-up is not possible. */
2827  bool mode_arrays_consistent = false;
2828  INT4 emm = -(INT4)ell;
2829  while (mode_arrays_consistent == false && emm<=(INT4)ell){
2830  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ell, emm) ==1){
2831  mode_arrays_consistent = true;
2832  }
2833  emm++;
2834  }
2835  if(mode_arrays_consistent == false){
2836  XLAL_ERROR(XLAL_EDOM, "ModeArrays are not consistent. The (%i,%i) mode in the inertial J-frame requires at least one mode with l=%i in the ModeArray (L-frame) option.\n", ell, emm-1, ell);
2837  }
2838 
2839  INT4 status = 0; //Variable to check correct functions calls.
2840 
2841  /* Build the frequency array and initialize hctilde to the length of freqs. */
2842  REAL8Sequence *freqs;
2843  UINT4 offset = SetupWFArrays(&freqs, hlmpos, freqs_In, pWF, ligotimegps_zero);
2844 
2845  /* Initialize hlmneg according to hlmpos. */
2846  size_t npts = (*hlmpos)->data->length;
2847  *hlmneg = XLALCreateCOMPLEX16FrequencySeries("hlmneg: FD waveform", &(*hlmpos)->epoch, (*hlmpos)->f0, pWF->deltaF, &lalStrainUnit, npts);
2848  XLAL_CHECK (*hlmneg, XLAL_ENOMEM, "Failed to allocated waveform COMPLEX16FrequencySeries of length %zu.", npts);
2849  memset((*hlmneg)->data->data, 0, npts * sizeof(COMPLEX16));
2850  XLALUnitMultiply(&((*hlmneg)->sampleUnits), &((*hlmneg)->sampleUnits), &lalSecondUnit);
2851 
2852  /* Variable to store the strain of only one (negative) mode: h_l-mprime */
2853  COMPLEX16FrequencySeries *htilde22 = NULL;
2854 
2855  /*
2856  Take input/default value for the threshold of the Multibanding for the hlms modes.
2857  If = 0 then do not use Multibanding. Default value defined in XLALSimInspiralWaveformParams.c.
2858  If the input freqs_In is non-uniform the Multibanding has been already switched off.
2859  */
2861 
2862  /* Initialize the power of pi for the HM internal functions. */
2864  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
2865 
2866  UINT4 n_coprec_modes = 0;
2867 
2868  /* Set up code for using PNR tuned angles */
2869  int IMRPhenomXPNRUseTunedAngles = pPrec->IMRPhenomXPNRUseTunedAngles;
2870  int AntisymmetricWaveform = pPrec->IMRPhenomXAntisymmetricWaveform;
2871 
2872  IMRPhenomX_PNR_angle_spline *hm_angle_spline = NULL;
2873  REAL8 Mf_RD_22 = pWF->fRING;
2874  REAL8 Mf_RD_lm = 0.0;
2875 
2876  if (IMRPhenomXPNRUseTunedAngles){
2877 
2878  /* Allocate the spline interpolant struct */
2880  if (!hm_angle_spline)
2881  {
2882  XLAL_ERROR(XLAL_EFUNC, "hm_angle_spline struct allocation failed in LALSimIMRPhenomXPHM.c.");
2883  }
2884 
2885  /* Populate interpolant structs for the (2,2) angles */
2886  status = IMRPhenomX_PNR_GeneratePNRAngleInterpolants(hm_angle_spline, pWF, pPrec, lalParams);
2887  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_GeneratePNRAngleInterpolants failed.\n");
2888 
2889  /* Here we assign the reference values of alpha and gamma to their values in the precession struct */
2890  /* NOTE: the contribution from pPrec->alpha0 is assigned in IMRPhenomX_PNR_RemapThetaJSF */
2891  pPrec->alpha_offset = gsl_spline_eval(hm_angle_spline->alpha_spline, pWF->fRef, hm_angle_spline->alpha_acc);
2892  /* NOTE: the sign is flipped between gamma and epsilon */
2893  pPrec->epsilon_offset = -gsl_spline_eval(hm_angle_spline->gamma_spline, pWF->fRef, hm_angle_spline->gamma_acc) - pPrec->epsilon0;
2894 
2895  /* Remap the J-frame sky location to use beta instead of ThetaJN */
2896  REAL8 betaPNR_ref = gsl_spline_eval(hm_angle_spline->beta_spline, pWF->fRef, hm_angle_spline->beta_acc);
2897  status = IMRPhenomX_PNR_RemapThetaJSF(betaPNR_ref, pWF, pPrec, lalParams);
2898  XLAL_CHECK(
2899  XLAL_SUCCESS == status,
2900  XLAL_EFUNC,
2901  "Error: IMRPhenomX_PNR_RemapThetaJSF failed in IMRPhenomX_PNR_GeneratePNRAngles.");
2902 
2903  }
2904 
2905  REAL8Sequence *antiSym_amp = NULL;
2906  REAL8Sequence *antiSym_phi = NULL;
2907 
2908  if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
2909  {
2910  antiSym_amp = XLALCreateREAL8Sequence(freqs->length);
2911  antiSym_phi = XLALCreateREAL8Sequence(freqs->length);
2912  }
2913 
2914 
2915  /***** Loop over non-precessing modes ******/
2916  for (UINT4 emmprime = 1; emmprime <= ell; emmprime++)
2917  {
2918  /* Loop over only positive mprime is intentional.
2919  The single mode function returns the negative mode h_l-mprime, and the positive
2920  is added automatically in during the twisting up in IMRPhenomXPHMTwistUpOneMode.
2921  First check if (l,m) mode is 'activated' in the ModeArray.
2922  If activated then generate the mode, else skip this mode.
2923  */
2924  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ell, emmprime) != 1)
2925  { /* skip mode */
2926  continue;
2927  } /* else: generate mode */
2928 
2929  if(XLALSimInspiralWaveformParamsLookupPhenomXPHMPrecModes(lalParams) == 1 && (INT4)emmprime!=-m)
2930  {
2931  continue;
2932  }
2933 
2934  n_coprec_modes++;
2935 
2936  #if DEBUG == 1
2937  printf("\n*************************************************\n Non-precessing Mode %i%i\n************************************",ell, emmprime);
2938  #endif
2939 
2940  /* Variable to store the strain of only one (negative) mode: h_l-mprime */
2941  COMPLEX16FrequencySeries *htildelm = NULL;
2942 
2943  /* Compute non-precessing mode */
2944  if (thresholdMB == 0){ // No multibanding
2945  if(ell == 2 && emmprime == 2)
2946  {
2947  status = IMRPhenomXASGenerateFD(&htildelm, freqs_In, pWF, lalParams);
2948  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXASGenerateFD failed to generate IMRPhenomXHM waveform.");
2949  }
2950  else
2951  {
2952  status = IMRPhenomXHMGenerateFDOneMode(&htildelm, freqs_In, pWF, ell, emmprime, lalParams);
2953  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMGenerateFDOneMode failed to generate IMRPhenomXHM waveform.");
2954  }
2955  }
2956  else{ // With multibanding
2957  if(ell==3 && emmprime==2){ // mode with mode-mixing
2958  status = IMRPhenomXHMMultiBandOneModeMixing(&htildelm, htilde22, pWF, ell, emmprime, lalParams);
2959  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMMultiBandOneModeMixing failed to generate IMRPhenomXHM waveform.");
2960  }
2961  else{ // modes without mode-mixing including 22 mode
2962  status = IMRPhenomXHMMultiBandOneMode(&htildelm, pWF, ell, emmprime, lalParams);
2963  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMMultiBandOneMode failed to generate IMRPhenomXHM waveform.");
2964  }
2965 
2966  /* IMRPhenomXHMMultiBandOneMode* functions set pWF->deltaF=0 internally, we put it back here. */
2967  pWF->deltaF = deltaF;
2968 
2969  /* If the 22 and 32 modes are active, we recycle the 22 mode for the mixing in the 32 and it is passed to IMRPhenomXHMMultiBandOneModeMixing.
2970  The 22 mode is always computed first than the 32, we store the 22 mode in the variable htilde22. */
2971  if(ell==2 && emmprime==2 && XLALSimInspiralModeArrayIsModeActive(ModeArray, 3, 2)==1){
2972  htilde22 = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &(ligotimegps_zero), 0.0, pWF->deltaF, &lalStrainUnit, htildelm->data->length);
2973  for(UINT4 idx = 0; idx < htildelm->data->length; idx++){
2974  htilde22->data->data[idx] = htildelm->data->data[idx];
2975  }
2976  }
2977  }
2978 
2979  if(ell==2 && emmprime==2)
2980  {
2981  if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
2982  {
2983  IMRPhenomX_PNR_GenerateAntisymmetricWaveform(antiSym_amp,antiSym_phi,freqs,pWF,pPrec,lalParams);
2984  }
2985  }
2986 
2987 
2988  if (!(htildelm)){ XLAL_ERROR(XLAL_EFUNC);}
2989 
2990 
2991  /* htildelm is recomputed every time in the loop. Check that it always comes out with the same length */
2992  XLAL_CHECK ( ((*hlmpos)->data->length==htildelm->data->length)
2993  && ((*hlmneg)->data->length==htildelm->data->length),
2994  XLAL_EBADLEN,
2995  "Inconsistent lengths between frequency series htildelm (%d), hlmpos (%d) and hlmneg (%d).",
2996  htildelm->data->length, (*hlmpos)->data->length, (*hlmneg)->data->length
2997  );
2998 
2999  /* Skip twisting-up if the non-precessing mode is zero. */
3000  if((pWF->q == 1) && (pWF->chi1L == pWF->chi2L) && (emmprime % 2 != 0))
3001  {
3003  continue;
3004  }
3005  /*
3006  TWISTING UP
3007  Transform modes from the precessing L-frame to inertial J-frame.
3008  */
3009 
3010  /* Variable to store the non-precessing waveform in one frequency point. */
3011  COMPLEX16 hlmcoprec;
3012  COMPLEX16 hlmcoprec_antiSym =0.0;
3013 
3015  {
3016  for (UINT4 idx = 0; idx < freqs->length; idx++)
3017  {
3018  hlmcoprec = htildelm->data->data[idx + offset]; /* Co-precessing waveform */
3019  if(m < 0) (*hlmpos)->data->data[idx + offset] = hlmcoprec; // Positive frequencies. Freqs do 0, df, 2df, ...., fmax
3020  if(m > 0) (*hlmneg)->data->data[idx + offset] = hlmcoprec; // Negative frequencies. Freqs do 0, -df, -2df, ...., -fmax
3021  }
3022  }
3023  else{
3024 
3025  /* set PNR variables if needed */
3026  REAL8 Mf_high = 0.0;
3027  REAL8 Mf_low = 0.0;
3028  UINT4 PNRtoggleInspiralScaling = pPrec->PNRInspiralScaling;
3029 
3030  // Precompute PNR transition frequencies if needed
3031  if (IMRPhenomXPNRUseTunedAngles){
3032 
3033  if((ell==2)&&(emmprime==2))
3034  {
3035  /* the frequency parameters don't matter here */
3036  Mf_RD_lm = 0.0;
3037  }
3038  else
3039  {
3040  /* Get the (l,m) RD frequency */
3041 
3042  Mf_RD_lm = IMRPhenomXHM_GenerateRingdownFrequency(ell, emmprime, pWF);
3043 
3044  /* Set the frequency interpolation transitions */
3045  status = IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies(&Mf_low, &Mf_high, emmprime, Mf_RD_22, Mf_RD_lm, pPrec);
3046  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies failed.\n");
3047  }
3048  }
3049 
3050  for (UINT4 idx = 0; idx < freqs->length; idx++)
3051  {
3052  REAL8 Mf = pWF->M_sec * freqs->data[idx];
3053  hlmcoprec = htildelm->data->data[idx + offset]; /* Co-precessing waveform */
3054 
3055  /***** construct h(2,2) or h(2,-2) in co-precessing frame with antisymmetric contribution *****/
3056  if(ell == 2 && emmprime == 2 && AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
3057  {
3058  hlmcoprec_antiSym = antiSym_amp->data[idx]*cexp(I*antiSym_phi->data[idx]);
3059  }
3060 
3061  COMPLEX16Sequence *hlm;
3062  hlm = XLALCreateCOMPLEX16Sequence(2);
3063 
3064  if(IMRPhenomXPNRUseTunedAngles)
3065  {
3066  REAL8 Mf_mapped = IMRPhenomX_PNR_LinearFrequencyMap(Mf, ell, emmprime, Mf_low, Mf_high, Mf_RD_22, Mf_RD_lm, PNRtoggleInspiralScaling);
3067  REAL8 f_mapped = XLALSimIMRPhenomXUtilsMftoHz(Mf_mapped, pWF->Mtot);
3068 
3069  pPrec->alphaPNR = gsl_spline_eval(hm_angle_spline->alpha_spline, f_mapped, hm_angle_spline->alpha_acc);
3070  pPrec->betaPNR = gsl_spline_eval(hm_angle_spline->beta_spline, f_mapped, hm_angle_spline->beta_acc);
3071  pPrec->gammaPNR = gsl_spline_eval(hm_angle_spline->gamma_spline, f_mapped, hm_angle_spline->gamma_acc);
3072  }
3073 
3074 
3075 
3076  // Twist up
3077  IMRPhenomXPHMTwistUpOneMode(Mf, hlmcoprec, hlmcoprec_antiSym, pWF, pPrec, ell, emmprime, m, hlm);
3078 
3079 
3080  (*hlmpos)->data->data[idx + offset] += hlm->data[0]; // Positive frequencies. Freqs do 0, df, 2df, ...., fmax
3081  (*hlmneg)->data->data[idx + offset] += hlm->data[1]; // Negative frequencies. Freqs do 0, -df, -2df, ...., -fmax
3083  }
3084 
3085  if(IMRPhenomXPNRUseTunedAngles)
3086  {
3087  gsl_interp_accel_reset(hm_angle_spline->alpha_acc);
3088  gsl_interp_accel_reset(hm_angle_spline->beta_acc);
3089  gsl_interp_accel_reset(hm_angle_spline->gamma_acc);
3090  }
3091  }
3092 
3094 
3095  }// End Loop over emmprime
3096  if (IMRPhenomXPNRUseTunedAngles){
3097  gsl_spline_free(hm_angle_spline->alpha_spline);
3098  gsl_spline_free(hm_angle_spline->beta_spline);
3099  gsl_spline_free(hm_angle_spline->gamma_spline);
3100 
3101  gsl_interp_accel_free(hm_angle_spline->alpha_acc);
3102  gsl_interp_accel_free(hm_angle_spline->beta_acc);
3103  gsl_interp_accel_free(hm_angle_spline->gamma_acc);
3104 
3105  LALFree(hm_angle_spline);
3106  }
3107 
3108 
3109  if(n_coprec_modes == 0)
3110  {
3111  XLAL_PRINT_ERROR("For computing the mode (%i,%i) in the inertial J-frame we need at least one l=%i mode activated in the co-precessing L-frame. \nConsider activate some l=%i modes in L-frame with the ModeArray option of the LAL dictionary. \nWe filled the (%i,%i) mode with zeroes." , ell, m, ell, ell, ell, m);
3112  }
3113 
3114  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_OneMode failed to generate IMRPhenomXPHM waveform.");
3115 
3116 /* Free memory */
3119 XLALDestroyValue(ModeArray);
3120 
3121 if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
3122 {
3123  XLALDestroyREAL8Sequence(antiSym_amp);
3124  XLALDestroyREAL8Sequence(antiSym_phi);
3125 }
3126 
3127 
3128 #if DEBUG == 1
3129 printf("\n******Leaving IMRPhenomXPHM_OneMode*****\n");
3130 #endif
3131 
3132 return XLAL_SUCCESS;
3133 }
3134 
3135 
3136 /*
3137  Core twisting up routine to get one single precessing mode.
3138  Twist the waveform in the precessing L-frame to the inertial J-frame for one frequency point.
3139  This function will be inside a loop of frequencies insid a loop over the non-precessing modes.
3140  It carries out the operation specified in eqs. E3-E4 in the Precessing paper.
3141 */
3143  const REAL8 Mf, /**< Frequency (Mf geometric units) */
3144  const COMPLEX16 hlmprime, /**< Underlying aligned-spin IMRPhenomXHM waveform. The loop is with mprime positive, but the mode has to be the negative one for positive frequencies.*/
3145  const COMPLEX16 hlmprime_antisym, /**< antisymmetric waveform in the co-precessing frame */
3146  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
3147  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
3148  UINT4 l, /**< l index of the (l,m) (non-)precessing mode */
3149  UINT4 mprime, /**< second index of the (l,mprime) non-precessing mode */
3150  INT4 m, /**< second index of the (l,m) precessing mode */
3151  COMPLEX16Sequence *hlminertial /**< [out] hlm in the inertial J-frame for one frequency point, precessing waveform */
3152 )
3153 {
3154  XLAL_CHECK(hlminertial != NULL, XLAL_EFAULT);
3155 
3156  /* Euler angles */
3157  double alpha = 0.0;
3158  double epsilon = 0.0;
3159 
3160  double cBetah = 0.0;
3161  double sBetah = 0.0;
3162 
3163  if(pPrec->IMRPhenomXPNRUseTunedAngles)
3164  {
3165  alpha = pPrec->alphaPNR - pPrec->alpha_offset;
3166  epsilon = -1.0 * pPrec->gammaPNR - pPrec->epsilon_offset;
3167  REAL8 cos_beta = cos(pPrec->betaPNR);
3168 
3169  INT4 status = 0;
3170  status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
3171  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
3172  }
3173  else
3174  {
3175  switch(pPrec->IMRPhenomXPrecVersion)
3176  {
3177  case 101: /* Post-Newtonian Euler angles. Single spin approximantion. See sections IV-B and IV-C in Precessing paper. */
3178  case 102: /* The different number 10i means different PN order. */
3179  case 103:
3180  case 104:
3181  {
3182  /* NNLO PN Euler Angles */
3183  Get_alpha_beta_epsilon(&alpha, &cBetah, &sBetah, &epsilon, mprime, Mf, pPrec, pWF);
3184  break;
3185  }
3186  case 220:
3187  case 221:
3188  case 222:
3189  case 223:
3190  case 224:
3191  {
3192  /* ~~~~~ Euler Angles from Chatziioannou et al, PRD 95, 104004, (2017) ~~~~~ */
3193  const double v = cbrt(LAL_PI * Mf * (2.0/mprime) );
3194  const vector vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWF,pPrec);
3195  double cos_beta = 0.0;
3196 
3197  REAL8 alpha_offset_mprime = 0, epsilon_offset_mprime = 0;
3198  Get_alpha_epsilon_offset(&alpha_offset_mprime, &epsilon_offset_mprime, mprime, pPrec);
3199 
3200  alpha = vangles.x - alpha_offset_mprime;
3201  epsilon = vangles.y - epsilon_offset_mprime;
3202  cos_beta = vangles.z;
3203 
3204  INT4 status = 0;
3205  status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
3206  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
3207 
3208  break;
3209  }
3210  case 310:
3211  case 311:
3212  case 320:
3213  case 321:
3214 
3215  {
3216  /* Get Euler angles. */
3217  int success = XLAL_SUCCESS;
3218  REAL8 cos_beta=0., gamma=0., alpha_i=0.;
3219 
3220  REAL8 alpha_offset_mprime = pPrec->alpha_ref- pPrec->alpha0;
3221  REAL8 epsilon_offset_mprime = -pPrec->gamma_ref-pPrec->epsilon0;
3222 
3223  REAL8 Mfprime = Mf * (2.0 / mprime);
3224 
3225  if(Mfprime< pPrec->ftrans_MRD)
3226  {
3227  success = success + gsl_spline_eval_e(pPrec->cosbeta_spline, Mfprime, pPrec->cosbeta_acc,&cos_beta);
3228  success = success + gsl_spline_eval_e(pPrec->gamma_spline, Mfprime, pPrec->gamma_acc, &gamma);
3229  success = success + gsl_spline_eval_e(pPrec->alpha_spline, Mfprime, pPrec->alpha_acc,&alpha_i);
3230 
3231  XLAL_CHECK(success == XLAL_SUCCESS, XLAL_EFUNC, "%s: Failed to interpolate angles at f=%.7f, got alpha_i=%.4f, cosbeta=%.4f, gamma=%.4f: \n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mfprime,pWF->Mtot),alpha_i,cos_beta,gamma);
3232 
3233  pPrec->gamma_in = gamma;
3234 
3235  }
3236 
3237 
3238 
3239  else {
3240 
3241  if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321 )
3242  {
3243 
3244  alpha_i=alphaMRD(Mfprime,pPrec->alpha_params);
3245  cos_beta=cos(betaMRD(Mfprime,pWF,pPrec->beta_params));
3246  REAL8 deltagamma=0.;
3247  success = gamma_from_alpha_cosbeta(&deltagamma, Mfprime, pWF->deltaMF*2./mprime,pWF,pPrec);
3248  if(success!=XLAL_SUCCESS) gamma = pPrec->gamma_in;
3249  else gamma =pPrec->gamma_in+deltagamma;
3250  pPrec->gamma_in = gamma;
3251 
3252  }
3253  else{
3254  // just repeat the last cached value for the Euler angles
3255  alpha_i = pPrec->alpha_ftrans;
3256  cos_beta = pPrec->cosbeta_ftrans;
3257  gamma = pPrec->gamma_ftrans;
3258 
3259 
3260  }
3261  }
3262 
3263 
3264 
3265 
3266  alpha = alpha_i- alpha_offset_mprime;
3267  epsilon = -gamma - epsilon_offset_mprime;
3268 
3269  INT4 status = 0;
3270  status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
3271  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
3272  break;
3273  }
3274 
3275  default:
3276  {
3277  XLAL_ERROR(XLAL_EINVAL,"Error. IMRPhenomXPrecVersion not recognized. Recommended default is 223.\n");
3278  break;
3279  }
3280  }
3281  }
3282 
3283 
3284  /* Useful powers of the Wigner coefficients */
3285  REAL8 cBetah2 = cBetah * cBetah;
3286  REAL8 cBetah3 = cBetah * cBetah2;
3287  REAL8 cBetah4 = cBetah * cBetah3;
3288  REAL8 cBetah5 = cBetah * cBetah4;
3289  REAL8 cBetah6 = cBetah * cBetah5;
3290  REAL8 cBetah7 = cBetah * cBetah6;
3291  REAL8 cBetah8 = cBetah * cBetah7;
3292 
3293  REAL8 sBetah2 = sBetah * sBetah;
3294  REAL8 sBetah3 = sBetah * sBetah2;
3295  REAL8 sBetah4 = sBetah * sBetah3;
3296  REAL8 sBetah5 = sBetah * sBetah4;
3297  REAL8 sBetah6 = sBetah * sBetah5;
3298  REAL8 sBetah7 = sBetah * sBetah6;
3299  REAL8 sBetah8 = sBetah * sBetah7;
3300 
3301  /*
3302  The following expressions for the Wigner-d coefficients correspond to those in appendix A of the Precessing paper.
3303  They are the same expressions used in IMRPhenomXPHMTwistUp.
3304  */
3305 
3306  COMPLEX16 hlm = 0, hlmneg = 0;
3307  INT4 minus1l = 1;
3308 
3309  /* Sum over l = 2 modes */
3310  if (l == 2 && mprime == 2){
3311  // d^2_{-2,2} d^2_{-1,2} d^2_{0,2} d^2_{1,2} d^2_{2,2}
3312  COMPLEX16 d22[5] = {sBetah4, 2.0*cBetah*sBetah3, pPrec->sqrt6*sBetah2*cBetah2, 2.0*cBetah3*sBetah, cBetah4};
3313  // d^2_{-2,-2} d^2_{-1,-2} d^2_{0,-2} d^2_{1,-2} d^2_{2,-2}
3314  COMPLEX16 d2m2[5] = {d22[4], -d22[3], d22[2], -d22[1], d22[0]}; /* Exploit symmetry d^2_{-m,-2} = (-1)^m d^2_{-m,2}. See eq. A2 of Precessing paper */
3315 
3316  /* See eqs. E3-E4 in Precessing paper. */
3317  COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3318  hlm += cexp_im_alpha * d2m2[m+2];
3319  hlmneg += cexp_im_alpha * d22[m+2];
3320  }
3321 
3322  /* Case (l, mprime) = (2, 1) */
3323  if (l == 2 && mprime == 1){
3324  // d^2_{-2,1} d^2_{-1,1} d^2_{0,1} d^2_{1,1} d^2_{2,1}
3325  COMPLEX16 d21[5] = {2.0*cBetah*sBetah3, 3.0*sBetah2*cBetah2 - sBetah4, pPrec->sqrt6*(cBetah3*sBetah - cBetah*sBetah3), cBetah2*(cBetah2 - 3.0*sBetah2), -2.0*cBetah3*sBetah};
3326  // d^2_{-2,-1} d^2_{-1,-1} d^2_{0,-1} d^2_{1,-1} d^2_{2,-1}
3327  COMPLEX16 d2m1[5] = {-d21[4], d21[3], -d21[2], d21[1], -d21[0]}; /* Exploit symmetry d^2_{-m,-1} = -(-1)^m d^2_{m,1}. See eq. A2 of Precessing paper. */
3328 
3329  /* See eqs. E3-E4 in Precessing paper. */
3330  COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3331  hlm += cexp_im_alpha * d2m1[m+2];
3332  hlmneg += cexp_im_alpha * d21[m+2];
3333  }
3334 
3335  /* Case (l, mprime) = (3, 3) */
3336  if (l == 3 && mprime == 3){
3337  minus1l = -1;
3338  // d^3_{-3,3} d^3_{-2,3} d^3_{-1,3} d^3_{0,3} d^3_{1,3} d^3_{2,3} d^3_{3,3}
3339  COMPLEX16 d33[7] = {sBetah6, pPrec->sqrt6*cBetah*sBetah5, pPrec->sqrt15*cBetah2*sBetah4, 2.0*pPrec->sqrt5*cBetah3*sBetah3, pPrec->sqrt15*cBetah4*sBetah2, pPrec->sqrt6*cBetah5*sBetah, cBetah6};
3340  // d^3_{-3,-3} d^3_{-2,-3} d^3_{-1,-3} d^3_{0,-3} d^3_{1,-3} d^3_{2,-3} d^3_{3,-3}
3341  COMPLEX16 d3m3[7] = {d33[6], -d33[5], d33[4], -d33[3], d33[2], -d33[1], d33[0]}; /* Exploit symmetry d^3_{-m,-3} = -(-1)^m d^3_{m,3}. See eq. A2 of Precessing paper. */
3342 
3343  /* See eqs. E3-E4 in Precessing paper. */
3344  COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3345  hlm += cexp_im_alpha * d3m3[m+3];
3346  hlmneg += cexp_im_alpha * d33[m+3];
3347  }
3348 
3349  /* Case (l, mprime) = (3, 2) */
3350  if (l == 3 && mprime == 2){
3351  minus1l = -1;
3352  // d^3_{-3,2} d^3_{-2,2} d^3_{-1,21} d^3_{0,2} d^3_{1,2} d^3_{2,2} d^3_{3,2}
3353  COMPLEX16 d32[7] = {pPrec->sqrt6*cBetah*sBetah5, sBetah4*(5.0*cBetah2 - sBetah2), pPrec->sqrt10*sBetah3*(2.0*cBetah3 - cBetah*sBetah2), pPrec->sqrt30*cBetah2*(cBetah2 - sBetah2)*sBetah2, pPrec->sqrt10*cBetah3*(cBetah2*sBetah - 2.0*sBetah3), cBetah4*(cBetah2 - 5.0*sBetah2), -1.0*pPrec->sqrt6*cBetah5*sBetah};
3354  // d^3_{-3,-2} d^3_{-2,-2} d^3_{-1,-2} d^3_{0,-2} d^3_{1,-2} d^3_{2,-2} d^3_{3,-2}
3355  COMPLEX16 d3m2[7] = {-d32[6], d32[5], -d32[4], d32[3], -d32[2], d32[1], -d32[0]}; /* Exploit symmetry d^3_{-m,-2} = (-1)^m d^3_{m,2}. See eq. A2 of Precessing paper. */
3356 
3357  /* See eqs. E3-E4 in Precessing paper. */
3358  COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3359  hlm += cexp_im_alpha * d3m2[m+3];
3360  hlmneg += cexp_im_alpha * d32[m+3];
3361  }
3362 
3363  /* Case (l, mprime) = (4, 4) */
3364  if (l == 4 && mprime == 4){
3365  // d^4_{-4,4} d^4_{-3,4} d^4_{-2,4} d^4_{-1,4} d^4_{0,4} d^4_{1,4} d^4_{2,4} d^4_{3,4} d^4_{44}
3366  COMPLEX16 d44[9] = {sBetah8, 2.0*pPrec->sqrt2*cBetah*sBetah7, 2.0*pPrec->sqrt7*cBetah2*sBetah6, 2.0*pPrec->sqrt14*cBetah3*sBetah5, pPrec->sqrt70*cBetah4*sBetah4, 2.0*pPrec->sqrt14*cBetah5*sBetah3, 2.0*pPrec->sqrt7*cBetah6*sBetah2, 2.0*pPrec->sqrt2*cBetah7*sBetah, cBetah8};
3367  // d^4_{4,-4} d^4_{-3,-4} d^4_{-2,-4} d^4_{-1,-4} d^4_{0,-4} d^4_{1,-4} d^4_{2,-4} d^4_{3,-4} d^4_{4-4}
3368  COMPLEX16 d4m4[9] = {d44[8], -d44[7], d44[6], -d44[5], d44[4], -d44[3], d44[2], -d44[1], d44[0]}; /* Exploit symmetry d^4_{-m,-4} = (-1)^m d^4_{m,4}. See eq. A2 of Precessing paper. */
3369 
3370  /* See eqs. E3-E4 in Precessing paper. */
3371  COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3372  hlm += cexp_im_alpha * d4m4[m+4];
3373  hlmneg += cexp_im_alpha * d44[m+4];
3374  }
3375 
3376  COMPLEX16 eps_phase_hP_lmprime;
3377  COMPLEX16 eps_phase_hP_lmprime_neg;
3378 
3379  /* See eqs. E3-E4 in Precessing paper. */
3380  COMPLEX16 exp_imprime_epsilon = cexp(mprime*I*epsilon);
3381 
3382  eps_phase_hP_lmprime = 1./exp_imprime_epsilon * (hlmprime + hlmprime_antisym);
3383  eps_phase_hP_lmprime_neg = exp_imprime_epsilon * minus1l * conj(hlmprime - hlmprime_antisym);
3384 
3385  /* Return h_lminertail */
3386  (hlminertial)->data[0] = eps_phase_hP_lmprime * hlm;
3387  (hlminertial)->data[1] = eps_phase_hP_lmprime_neg * hlmneg;
3388 
3389 
3390  return XLAL_SUCCESS;
3391 }
3392 
3393 /** Function to obtain a SphHarmFrequencySeries with the individual modes h_lm.
3394  By default it returns all the modes available in the model, positive and negatives.
3395  With the mode array option in the LAL dictionary, the user can specify a custom mode array.
3396  The modes are computed in the inertial J-frame, so the mode array option does not refers to
3397  the modes in the co-precessing frame conversely to the functions for the polarizations XLALSimIMRPhenomXPHM.
3398  This function is to be used by ChooseFDModes.
3399 */
3401  SphHarmFrequencySeries **hlms, /**< [out] list with single modes h_lm in the J-frame */
3402  REAL8 m1_SI, /**< mass of companion 1 (kg) */
3403  REAL8 m2_SI, /**< mass of companion 2 (kg) */
3404  REAL8 S1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
3405  REAL8 S1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
3406  REAL8 S1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
3407  REAL8 S2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
3408  REAL8 S2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
3409  REAL8 S2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
3410  REAL8 deltaF, /**< frequency spacing (Hz) */
3411  REAL8 f_min, /**< starting GW frequency (Hz) */
3412  REAL8 f_max, /**< ending GW frequency (Hz) */
3413  REAL8 f_ref, /**< reference GW frequency (Hz) */
3414  REAL8 phiRef, /**< phase shift at reference frequency */
3415  REAL8 distance, /**< distance of source (m) */
3416  REAL8 inclination, /**< inclination of source (rad) */
3417  LALDict *lalParams /**< LAL dictionary with extra options */
3418 )
3419 {
3420  LALValue *ModeArrayJframe = NULL; // Modes in the precessing J-frame. Specified through the new LAL dictionary option "ModeArrayJframe"
3421 
3422  /* Use an auxiliar laldict to not overwrite the input argument */
3423  LALDict *lalParams_aux;
3424  /* setup mode array */
3425  if (lalParams == NULL)
3426  {
3427  lalParams_aux = XLALCreateDict();
3428  }
3429  else{
3430  lalParams_aux = XLALDictDuplicate(lalParams);
3431  }
3432 
3433  /* Check that the co-precessing modes chosen are available for the model */
3434  XLAL_CHECK(check_input_mode_array(lalParams_aux) == XLAL_SUCCESS, XLAL_EFAULT, "Not available co-precessing mode chosen.\n");
3435 
3436 
3437  /* Read mode array from LAL dictionary */
3438  ModeArrayJframe = XLALSimInspiralWaveformParamsLookupModeArrayJframe(lalParams_aux);
3439 
3440  /* If input LAL dictionary does not have mode array, use all the modes available for XPHM (l<=4) */
3441  if(ModeArrayJframe == NULL)
3442  {
3444  ModeArrayJframe = XLALSimInspiralModeArrayActivateAllModesAtL(ModeArrayJframe, 3);
3445  ModeArrayJframe = XLALSimInspiralModeArrayActivateAllModesAtL(ModeArrayJframe, 4);
3446  }
3447  else{
3448  /* Check that the modes chosen are available for the model */
3449  XLAL_CHECK(check_input_mode_array_Jframe(ModeArrayJframe) == XLAL_SUCCESS, XLAL_EFAULT, "Not available mode chosen. l must be lower than %i\n", L_MAX);
3450  }
3451 
3452 
3453  INT4 length = 0;
3454  /***** Loop over modes ******/
3455  for (UINT4 ell = 2; ell <= LAL_SIM_L_MAX_MODE_ARRAY; ell++)
3456  {
3457  for (INT4 emm = -(INT4)ell; emm <= (INT4)ell; emm++)
3458  {
3459  if(XLALSimInspiralModeArrayIsModeActive(ModeArrayJframe, ell, emm) !=1)
3460  {
3461  /* Skip mode if user did not specified it. */
3462  continue;
3463  }
3464  //Variable to store the strain of only one (positive/negative) mode: h_lm
3465  COMPLEX16FrequencySeries *hlmpos = NULL;
3466  COMPLEX16FrequencySeries *hlmneg = NULL;
3467  COMPLEX16FrequencySeries *hlmall = NULL;
3468 
3469  /* Compute precessing single mode */
3470  XLALSimIMRPhenomXPHMOneMode(&hlmpos, &hlmneg, ell, emm, m1_SI, m2_SI, S1x, S1y, S1z, S2x, S2y, S2z, distance, inclination, phiRef, deltaF, f_min, f_max, f_ref, lalParams_aux);
3471 
3472  if (!(hlmpos) || !hlmneg){ XLAL_ERROR(XLAL_EFUNC);}
3473 
3474  length = hlmpos->data->length-1;
3475 
3476  hlmall = XLALCreateCOMPLEX16FrequencySeries("hlmall: precessing FD mode", &(hlmpos->epoch), hlmpos->f0, hlmpos->deltaF, &(hlmpos->sampleUnits), 2*length+1);
3477 
3478  for(INT4 i=0; i<=length; i++)
3479  {
3480  hlmall->data->data[i+length] = hlmpos->data->data[i];
3481  hlmall->data->data[i] = hlmneg->data->data[length-i];
3482  }
3483 
3484 
3485  // Add single mode to list
3486  *hlms = XLALSphHarmFrequencySeriesAddMode(*hlms, hlmall, ell, emm);
3487 
3488  // Free memory
3492  }
3493  } /* End loop over modes */
3494 
3495 
3496  /* Add frequency array to SphHarmFrequencySeries */
3497  REAL8Sequence *freqs = XLALCreateREAL8Sequence(2*length+1);
3498  for (INT4 i = -length; i<=length; i++)
3499  {
3500  freqs->data[i+length] = i*deltaF;
3501  }
3502  XLALSphHarmFrequencySeriesSetFData(*hlms, freqs);
3503 
3504  /* Free memory */
3505  XLALDestroyDict(lalParams_aux);
3506  XLALDestroyValue(ModeArrayJframe);
3507 
3508 
3509  return XLAL_SUCCESS;
3510 
3511 }
3512 
3513 
3514 /*********************************************/
3515 /* */
3516 /* AUXILIARY FUNCTIONS */
3517 /* */
3518 /*********************************************/
3519 
3520 
3521 /** Wrapper function for setup ModeArray of modes in the precessing frame to be twisted up. */
3522 LALDict *IMRPhenomXPHM_setup_mode_array(LALDict *lalParams)
3523 {
3524  /* setup ModeArray */
3525  INT4 lalParams_In = 0;
3526  if (lalParams == NULL)
3527  {
3528  lalParams_In = 1;
3529  lalParams = XLALCreateDict();
3530  }
3531  LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(lalParams);
3532 
3533  /* If the mode array is empty, populate using a default choice of modes */
3534  if (ModeArray == NULL)
3535  {
3536  /* Default behaviour */
3537  XLAL_PRINT_INFO("Using default non-precessing modes for IMRPhenomXPHM: 2|2|, 2|1|, 3|3|, 3|2|, 4|4|.\n");
3538  ModeArray = XLALSimInspiralCreateModeArray();
3539 
3540  /* IMRPhenomXHM has the following calibrated modes. 22 mode taken from IMRPhenomXAS */
3541  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 2);
3542  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 1);
3543  XLALSimInspiralModeArrayActivateMode(ModeArray, 3, 3);
3544  XLALSimInspiralModeArrayActivateMode(ModeArray, 3, 2);
3545  XLALSimInspiralModeArrayActivateMode(ModeArray, 4, 4);
3546  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, -2);
3547  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, -1);
3548  XLALSimInspiralModeArrayActivateMode(ModeArray, 3, -3);
3549  XLALSimInspiralModeArrayActivateMode(ModeArray, 3, -2);
3550  XLALSimInspiralModeArrayActivateMode(ModeArray, 4, -4);
3551  XLALSimInspiralWaveformParamsInsertModeArray(lalParams, ModeArray);
3552  }
3553  else {XLAL_PRINT_INFO("Using custom non-precessing modes for PhenomXPHM.\n"); }
3554 
3555  XLALDestroyValue(ModeArray);
3556  if(lalParams_In == 1)
3557  {
3558  XLALDestroyDict(lalParams);
3559  }
3560 
3561  return lalParams;
3562 }
3563 
3564 
3565 /* Function to check if the input mode array in the J-frame contains unsupported modes */
3566 INT4 check_input_mode_array_Jframe(LALValue *ModeArrayJframe){
3567  /* Check if the input array has a too high l. */
3568  for(INT4 ell=2; ell<=LAL_SIM_L_MAX_MODE_ARRAY; ell++)
3569  {
3570  for(INT4 emm=0; emm<=ell; emm++)
3571  {
3572  if(XLALSimInspiralModeArrayIsModeActive(ModeArrayJframe, ell, emm) == 1 && ell>L_MAX){
3573  XLALDestroyValue(ModeArrayJframe);
3574  return XLAL_FAILURE;
3575  }
3576  }
3577  }
3578  return XLAL_SUCCESS;
3579 }
3580 
3581 /*
3582  Return the offset at reference frequency for alpha and epsilon Euler angles for a particular non-precessing mode.
3583  E.g.: alpha_offset_mprime = alpha(2*pi*MfRef/mprime) - alpha0. Used for Pv2 and Pv3 angles.
3584 */
3586  REAL8 *alpha_offset_mprime, /**< [out] Offset alpha angle at reference frequency */
3587  REAL8 *epsilon_offset_mprime, /**< [out] Offset epsilon angle at reference frequency */
3588  INT4 mprime, /**< Second index of the non-precesssing mode (l, mprime) */
3589  IMRPhenomXPrecessionStruct *pPrec /**< IMRPhenomXP Precessing structure*/
3590 )
3591 {
3592  /* The angles are evaluated at frequency 2*pi*MfRef/mprime so the offset depend on mprime. */
3593  switch(mprime)
3594  {
3595  case 1:
3596  {
3597  *alpha_offset_mprime = pPrec->alpha_offset_1;
3598  *epsilon_offset_mprime = pPrec->epsilon_offset_1;
3599  break;
3600  }
3601  case 2:
3602  {
3603  *alpha_offset_mprime = pPrec->alpha_offset; // These variable was already used in the XP code,
3604  *epsilon_offset_mprime = pPrec->epsilon_offset; // that is why we did not add the _2 here.
3605  break;
3606  }
3607  case 3:
3608  {
3609  *alpha_offset_mprime = pPrec->alpha_offset_3;
3610  *epsilon_offset_mprime = pPrec->epsilon_offset_3;
3611  break;
3612  }
3613  case 4:
3614  {
3615  *alpha_offset_mprime = pPrec->alpha_offset_4;
3616  *epsilon_offset_mprime = pPrec->epsilon_offset_4;
3617  break;
3618  }
3619  default:
3620  {
3621  XLAL_ERROR(XLAL_EINVAL,"Error: mprime not supported, check available non-precessing modes.\n");
3622  break;
3623  }
3624  }
3625 
3626  return XLAL_SUCCESS;
3627 }
3628 
3629 
3630 /* Return the 3 Post-Newtonian Euler angles evaluated at frequency (2*pi*Mf/mprime). */
3631 /* See equations C19/C20 and G7/G8 in the Precessing paper for the expressions of alpha/epsilon. beta is computed accoring to eq. 4.4. */
3633  REAL8 *alpha, /**< [out] Azimuthal angle of L w.r.t J */
3634  REAL8 *cBetah, /**< [out] Cosine of polar angle between L and J */
3635  REAL8 *sBetah, /**< [out] Sine of polar angle between L and J */
3636  REAL8 *epsilon, /**< [out] Minus the third Euler angle (-gamma) describing L w.r.t J, fixed by minimal rotation condition */
3637  INT4 mprime, /**< Second index of the non-precesssing mode (l, mprime) */
3638  REAL8 Mf, /**< Frequency geometric units */
3639  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precessing structure*/
3640  IMRPhenomXWaveformStruct *pWF /**< IMRPhenomX Waveform structure*/
3641 )
3642 {
3643  double omega = LAL_PI * Mf *2./mprime;
3644  double logomega = log(omega);
3645  double omega_cbrt = cbrt(omega);
3646  double omega_cbrt2 = omega_cbrt * omega_cbrt;
3647 
3648  REAL8 alpha_offset_mprime = 0., epsilon_offset_mprime = 0.;
3649 
3650  Get_alpha_epsilon_offset(&alpha_offset_mprime, &epsilon_offset_mprime, mprime, pPrec);
3651 
3652  *alpha = (
3653  pPrec->alpha1 / omega
3654  + pPrec->alpha2 / omega_cbrt2
3655  + pPrec->alpha3 / omega_cbrt
3656  + pPrec->alpha4L * logomega
3657  + pPrec->alpha5 * omega_cbrt
3658  - alpha_offset_mprime
3659  );
3660 
3661  *epsilon = (
3662  pPrec->epsilon1 / omega
3663  + pPrec->epsilon2 / omega_cbrt2
3664  + pPrec->epsilon3 / omega_cbrt
3665  + pPrec->epsilon4L * logomega
3666  + pPrec->epsilon5 * omega_cbrt
3667  - epsilon_offset_mprime
3668  );
3669 
3670  INT4 status = 0;
3671  status = IMRPhenomXWignerdCoefficients(cBetah, sBetah, omega_cbrt, pWF, pPrec);
3672  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients failed.");
3673 
3674  return status;
3675 }
3676 
3677 
3678 /*
3679  Non-uniform/coarse frequency grid for the Multibanding of the angles.
3680 
3681  - In this first release we use the same coarse grid that is used for computing the non-precessing modes.
3682  - This grid is discussed in section II of arXiv:2001.10897. See also section D of Precessing paper.
3683  - This grid is computed with the function XLALSimIMRPhenomXMultibandingVersion defined in LALSimIMRPhenomXHM_multiband.c.
3684  - The version of the coarse grid will be changed with the option 'MBandPrecVersion' defined in LALSimInspiralWaveformParams.c.
3685  - Currently there is only one version available and the option value for that is 0, which is the default value.
3686 */
3688  REAL8Sequence **coarseFreqs, /**<[out] Non-uniform coarse frequency grid (1D array) */
3689  UINT4 ell, /**< First index non-precessing mode */
3690  UINT4 emmprime, /**< Second index non-precessing mode */
3691  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct*/
3692  LALDict *lalParams) /**< LAL dictionary */
3693 {
3694  /* This function is basically a copy of the first part of IMRPhenomXHMMultiBandOneMode and IMRPhenomXHMMultiBandOneModeMixing. */
3695 
3696  /* Create non-uniform grid for each mode. */
3698 
3699  /* Compute the coarse frequency array. It is stored in a list of grids. */
3700  size_t iStart = (size_t) (pWF->fMin / pWF->deltaF);
3701 
3702  /* Final grid spacing, adimensional (NR) units */
3703  REAL8 evaldMf = XLALSimIMRPhenomXUtilsHztoMf(pWF->deltaF, pWF->Mtot);
3704 
3705  /* Variable for the Multibanding criteria. See eq. 2.8-2.9 of arXiv:2001.10897. */
3706  REAL8 dfpower = 11./6.;
3707  REAL8 dfcoefficient = 8. * sqrt(3./5.) * LAL_PI * powers_of_lalpi.m_one_sixth * sqrt(2.)*cbrt(2) /(cbrt(emmprime)*emmprime) * sqrt(thresholdMB * pWF->eta);
3708 
3709  /* Variables for the coarse frequency grid */
3710  REAL8 Mfmin = XLALSimIMRPhenomXUtilsHztoMf(iStart*pWF->deltaF, pWF->Mtot);
3711  REAL8 Mfmax = XLALSimIMRPhenomXUtilsHztoMf(pWF->f_max_prime, pWF->Mtot);
3712  REAL8 MfMECO, MfLorentzianEnd;
3713  REAL8 dfmerger = 0., dfringdown = 0.;
3714  UINT4 lengthallGrids = 20;
3715 
3718 
3719  //populate coefficients of 22 mode, to rotate to spherical
3722  IMRPhenomXGetPhaseCoefficients(pWF, pPhase22);
3723 
3724  /* Allocate and initialize the PhenomXHM lm amplitude coefficients struct */
3727 
3728  if(ell == 2 && emmprime ==2){
3729  MfMECO = pWF->fMECO;
3730  #if DEBUG == 1
3731  printf("\nfRING = %e\n",pWF->fRING);
3732  printf("fDAMP = %e\n",pWF->fDAMP);
3733  printf("alphaL22 = %.16e", pPhase22->cLovfda/pWF->eta);
3734  #endif
3735  MfLorentzianEnd = pWF->fRING + 2*pWF->fDAMP;
3737  dfmerger = deltaF_mergerBin(pWF->fDAMP, pPhase22->cLovfda/pWF->eta, thresholdMB);
3738  dfringdown = deltaF_ringdownBin(pWF->fDAMP, pPhase22->cLovfda/pWF->eta, pAmp22->gamma2/(pAmp22->gamma3*pWF->fDAMP),thresholdMB);
3739  }
3740  else{
3741  // allocate qnm struct
3742  QNMFits *qnms = (QNMFits *) XLALMalloc(sizeof(QNMFits));
3744  // Populate pWFHM
3745  IMRPhenomXHM_SetHMWaveformVariables(ell, emmprime, pWFHM, pWF, qnms, lalParams);
3746  LALFree(qnms);
3747 
3748  /* Allocate and initialize the PhenomXHM lm phase and amp coefficients struct */
3751 
3752  if(pWFHM->MixingOn == 1){
3753  /* Get coefficients for Amplitude and phase */
3754  GetSpheroidalCoefficients(pPhase, pPhase22, pWFHM, pWF);
3756  }
3757 
3758  /* Get coefficients for Amplitude and phase */
3759  IMRPhenomXHM_GetAmplitudeCoefficients(pAmp, pPhase, pAmp22, pPhase22, pWFHM, pWF);
3760  IMRPhenomXHM_GetPhaseCoefficients(pAmp, pPhase, pAmp22, pPhase22, pWFHM, pWF,lalParams);
3761 
3762  MfMECO = pWFHM->fMECOlm;
3763  MfLorentzianEnd = pWFHM->fRING + 2*pWFHM->fDAMP;
3764  #if DEBUG == 1
3765  printf("\nfRING = %e\n",pWFHM->fRING);
3766  printf("fDAMP = %e\n",pWFHM->fDAMP);
3767  printf("alphaL = %.16e", pPhase->alphaL);
3768  #endif
3769  deltaF_MergerRingdown(&dfmerger, &dfringdown, thresholdMB, pWFHM, pAmp, pPhase);
3770  }
3771  LALFree(pWFHM);
3772  LALFree(pAmp);
3773  LALFree(pPhase);
3774  LALFree(pAmp22);
3775  LALFree(pPhase22);
3776 
3777  UINT4 nGridsUsed = XLALSimIMRPhenomXMultibandingGrid(Mfmin, MfMECO, MfLorentzianEnd, Mfmax, evaldMf, dfpower, dfcoefficient, allGrids, dfmerger, dfringdown);
3778  if (allGrids == NULL)
3779  {
3780  #if DEBUG == 1
3781  printf("Malloc of allGrids failed!\n");
3782  printf("nGridsUsed = %i\n", nGridsUsed);
3783  #endif
3784  return -1;
3785  }
3786 
3787  /* Number of fine frequencies per coarse interval in every coarse grid */
3788  /* Actual number of subgrids to be used. We allocated more than needed. */
3789  UINT4 actualnumberofGrids = 0;
3790  /* Length of coarse frequency array */
3791  UINT4 lenCoarseArray = 0;
3792 
3793  /* Transform the coarse frequency array to 1D array. */
3794  // Take only the subgrids needed
3795  for(UINT4 kk = 0; kk < nGridsUsed; kk++){
3796  lenCoarseArray = lenCoarseArray + allGrids[kk].Length;
3797  actualnumberofGrids++;
3798 
3799  #if DEBUG == 1
3800  printf("\nkk = %i\n",kk);
3801  printf("xStart: %.16e\n", allGrids[kk].xStart);
3802  printf("xEnd: %.16e\n", allGrids[kk].xEndRequested);
3803  printf("Length: %i\n", allGrids[kk].Length);
3804  printf("deltax: %.16e\n", allGrids[kk].deltax);
3805  printf("evaldMf: %.16e\n", evaldMf);
3806  printf("xMax: %.16e\n", allGrids[kk].xMax);
3807  printf("Last grid.xMax = %.16f\n", allGrids[actualnumberofGrids-1].xMax);
3808  #endif
3809 
3810  if(allGrids[kk].xMax + evaldMf >= Mfmax){
3811  break;
3812  }
3813  }
3814 
3815  // Add extra points to the coarse grid if the last freq is lower than Mfmax
3816  while(allGrids[actualnumberofGrids-1].xMax < Mfmax){
3817  allGrids[actualnumberofGrids-1].xMax = allGrids[actualnumberofGrids-1].xMax + allGrids[actualnumberofGrids-1].deltax;
3818  allGrids[actualnumberofGrids-1].Length = allGrids[actualnumberofGrids-1].Length + 1;
3819  lenCoarseArray++;
3820  }
3821 
3822  #if DEBUG == 1
3823  printf("\nactualnumberofGrids = %i\n", actualnumberofGrids);
3824  printf("lenCoarseArray = %i\n", lenCoarseArray);
3825  printf("Last grid.xMax = %.16f", allGrids[actualnumberofGrids-1].xMax);
3826  #endif
3827 
3828  // Transform coarse frequency array to 1D vector
3829  /* Allocate memory for frequency array and terminate if this fails */
3830  (*coarseFreqs) = XLALCreateREAL8Sequence(lenCoarseArray);
3831  if (!(*coarseFreqs)) { XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");}
3832 
3833  UINT4 lencoarseFreqs = 0;
3834 
3835  for(UINT4 kk = 0; kk < actualnumberofGrids; kk++){
3836  for(INT4 ll = 0; ll < allGrids[kk].Length; ll++){
3837  (*coarseFreqs)->data[lencoarseFreqs] = (allGrids[kk].xStart + allGrids[kk].deltax*ll);
3838  lencoarseFreqs++;
3839  }
3840  }
3841 
3842  /* End of coarse frequency array. */
3843 
3844  #if DEBUG == 1
3845  printf("\n******** Coarse frequencies array done ********* \n");
3846  printf("\nlencoarseFreqs, coarse[0], coarse[-1], Mfmax = %i %.16e %.16e %.16e\n",lencoarseFreqs, XLALSimIMRPhenomXUtilsMftoHz((*coarseFreqs)->data[0],pWF->Mtot), XLALSimIMRPhenomXUtilsMftoHz((*coarseFreqs)->data[lencoarseFreqs-1], pWF->Mtot), XLALSimIMRPhenomXUtilsMftoHz(Mfmax,pWF->Mtot));
3847  #endif
3848 
3849 
3850  LALFree(allGrids);
3851  return actualnumberofGrids;
3852 }
3853 
3854 /* @} */
3855 /* @} */
void XLALDestroyDict(LALDict *dict)
LALDict * XLALDictDuplicate(LALDict *old)
LALDict * XLALCreateDict(void)
#define LALFree(p)
int XLALSimIMRPhenomHMGethlmModes(SphHarmFrequencySeries **hlms, REAL8Sequence *freqs, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, const REAL8 phiRef, const REAL8 deltaF, REAL8 f_ref, LALDict *extraParams)
static size_t NextPow2(const size_t n)
IMRPhenomX_UsefulPowers powers_of_lalpi
int IMRPhenomXASGenerateFD(COMPLEX16FrequencySeries **htilde22, const REAL8Sequence *freqs_In, IMRPhenomXWaveformStruct *pWF, LALDict *lalParams)
int IMRPhenomX_PNR_GenerateAntisymmetricWaveform(REAL8Sequence *antisymamp, REAL8Sequence *antisymphase, const REAL8Sequence *freqs, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
INT4 IMRPhenomXHM_PNR_SetPhaseAlignmentParams(INT4 ell, INT4 emm, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
REAL8 IMRPhenomX_PNR_LinearFrequencyMap(REAL8 Mf, REAL8 ell, REAL8 emm, REAL8 Mf_lower, REAL8 Mf_upper, REAL8 Mf_RD_22, REAL8 Mf_RD_lm, UINT4 INSPIRAL)
Computes a linear frequency map for the HM PNR angles based on the linear frequency mapping used orig...
INT4 IMRPhenomX_PNR_RemapThetaJSF(REAL8 beta_ref, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
This code recomputes the skymapped locations in the J-frame using the new value of beta computed from...
INT4 IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies(REAL8 *Mf_low, REAL8 *Mf_high, REAL8 emmprime, REAL8 Mf_RD_22, REAL8 Mf_RD_lm, IMRPhenomXPrecessionStruct *pPrec)
Compute the transition frequencies for the HM PNR angle mapping.
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)
int IMRPhenomXGetAmplitudeCoefficients(IMRPhenomXWaveformStruct *pWF, IMRPhenomXAmpCoefficients *pAmp)
int IMRPhenomX_Initialize_Powers(IMRPhenomX_UsefulPowers *p, REAL8 number)
int IMRPhenomXGetPhaseCoefficients(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPhaseCoefficients *pPhase)
INT4 check_input_mode_array(LALDict *lalParams)
int IMRPhenomXWignerdCoefficients(REAL8 *cos_beta_half, REAL8 *sin_beta_half, const REAL8 v, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
int gamma_from_alpha_cosbeta(double *gamma, double Mf, double deltaMf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
vector IMRPhenomX_Return_phi_zeta_costhetaL_MSA(const double v, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
Wrapper to generate , and at a given frequency.
double alphaMRD(double Mf, PhenomXPalphaMRD *alpha_params)
int IMRPhenomX_Initialize_Euler_Angles(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
Wrapper of IMRPhenomX_SpinTaylorAnglesSplinesAll: fmin and fmax are determined by the function based ...
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:
double betaMRD(double Mf, UNUSED IMRPhenomXWaveformStruct *pWF, PhenomXPbetaMRD *beta_params)
int SetupWFArrays(REAL8Sequence **freqs, COMPLEX16FrequencySeries **htildelm, const REAL8Sequence *freqs_In, IMRPhenomXWaveformStruct *pWF, LIGOTimeGPS ligotimegps_zero)
int IMRPhenomXHMGenerateFDOneMode(COMPLEX16FrequencySeries **htildelm, const REAL8Sequence *freqs_In, IMRPhenomXWaveformStruct *pWF, UINT4 ell, UINT4 emm, LALDict *lalParams)
IMRPhenomX_UsefulPowers powers_of_lalpiHM
void IMRPhenomXHM_SetHMWaveformVariables(int ell, int emm, IMRPhenomXHMWaveformStruct *wf, IMRPhenomXWaveformStruct *wf22, QNMFits *qnms, LALDict *LALParams)
void IMRPhenomXHM_GetAmplitudeCoefficients(IMRPhenomXHMAmpCoefficients *pAmp, IMRPhenomXHMPhaseCoefficients *pPhase, IMRPhenomXAmpCoefficients *pAmp22, IMRPhenomXPhaseCoefficients *pPhase22, IMRPhenomXHMWaveformStruct *pWFHM, IMRPhenomXWaveformStruct *pWF22)
void IMRPhenomXHM_GetPhaseCoefficients(IMRPhenomXHMAmpCoefficients *pAmp, IMRPhenomXHMPhaseCoefficients *pPhase, IMRPhenomXAmpCoefficients *pAmp22, IMRPhenomXPhaseCoefficients *pPhase22, IMRPhenomXHMWaveformStruct *pWFHM, IMRPhenomXWaveformStruct *pWF22, UNUSED LALDict *lalParams)
void GetSpheroidalCoefficients(IMRPhenomXHMPhaseCoefficients *pPhase, IMRPhenomXPhaseCoefficients *pPhase22, IMRPhenomXHMWaveformStruct *pWFHM, IMRPhenomXWaveformStruct *pWF22)
void IMRPhenomXHM_FillAmpFitsArray(IMRPhenomXHMAmpCoefficients *pAmp)
void IMRPhenomXHM_FillPhaseFitsArray(IMRPhenomXHMPhaseCoefficients *pPhase)
REAL8 IMRPhenomXHM_GenerateRingdownFrequency(UINT4 ell, UINT4 emm, IMRPhenomXWaveformStruct *wf22)
void IMRPhenomXHM_Initialize_QNMs(QNMFits *qnms)
static double deltaF_ringdownBin(REAL8 fdamp, REAL8 alpha4, REAL8 LAMBDA, REAL8 abserror)
INT4 XLALSimIMRPhenomXMultibandingGrid(REAL8 fstartIn, REAL8 fend, REAL8 MfLorentzianEnd, REAL8 Mfmax, REAL8 evaldMf, REAL8 dfpower, REAL8 dfcoefficient, IMRPhenomXMultiBandingGridStruct *allGrids, REAL8 dfmerger, REAL8 dfringdown)
INT4 deltaF_MergerRingdown(REAL8 *dfmerger, REAL8 *dfringdown, REAL8 resTest, IMRPhenomXHMWaveformStruct *pWFHM, IMRPhenomXHMAmpCoefficients *pAmp, IMRPhenomXHMPhaseCoefficients *pPhase)
static double deltaF_mergerBin(REAL8 fdamp, REAL8 alpha4, REAL8 abserror)
int IMRPhenomXHMMultiBandOneModeMixing(COMPLEX16FrequencySeries **htildelm, COMPLEX16FrequencySeries *htilde22, IMRPhenomXWaveformStruct *pWF, UINT4 ell, UINT4 emm, LALDict *lalParams)
int IMRPhenomXHMMultiBandOneMode(COMPLEX16FrequencySeries **htildelm, IMRPhenomXWaveformStruct *pWF, UINT4 ell, UINT4 emm, LALDict *lalParams)
static int IMRPhenomXPHM_hplushcross_from_modes(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, const REAL8Sequence *freqs_In, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
LALDict * IMRPhenomXPHM_setup_mode_array(LALDict *lalParams)
Wrapper function for setup ModeArray of modes in the precessing frame to be twisted up.
int XLALSimIMRPhenomXPHMModes(SphHarmFrequencySeries **hlms, REAL8 m1_SI, REAL8 m2_SI, REAL8 S1x, REAL8 S1y, REAL8 S1z, REAL8 S2x, REAL8 S2y, REAL8 S2z, REAL8 deltaF, REAL8 f_min, REAL8 f_max, REAL8 f_ref, REAL8 phiRef, REAL8 distance, REAL8 inclination, LALDict *lalParams)
Function to obtain a SphHarmFrequencySeries with the individual modes h_lm.
static int IMRPhenomXPHM_OneMode(COMPLEX16FrequencySeries **hlmpos, COMPLEX16FrequencySeries **hlmneg, const REAL8Sequence *freqs_In, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, UINT4 ell, INT4 m, LALDict *lalParams)
Core funciton to compute an individual precessing mode hlm in the inertial J-frame.
INT4 XLALSimIMRPhenomXPHMMultibandingGrid(REAL8Sequence **coarseFreqs, UINT4 ell, UINT4 emmprime, IMRPhenomXWaveformStruct *pWF, LALDict *lalParams)
static int IMRPhenomXPHMTwistUpOneMode(const REAL8 Mf, const COMPLEX16 hlmprime, const COMPLEX16 hlmprime_antisym, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, UINT4 l, UINT4 mprime, INT4 m, COMPLEX16Sequence *hlminertial)
#define PHENOMXDEBUG
INT4 check_input_mode_array_Jframe(LALValue *ModeArrayJframe)
static double Get_alpha_epsilon_offset(REAL8 *alpha_offset_mprime, REAL8 *epsilon_offset_mprime, INT4 mprime, IMRPhenomXPrecessionStruct *pPrec)
static int IMRPhenomXPHMTwistUp(const REAL8 Mf, const COMPLEX16 hHM, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, INT4 ell, INT4 emmprime, COMPLEX16 *hp, COMPLEX16 *hc)
static int IMRPhenomXPHM_hplushcross(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, const REAL8Sequence *freqs_In, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
Core function of XLALSimIMRPhenomXPHM and XLALSimIMRPhenomXPHMFrequencySequence.
#define L_MAX
#define DEBUG
static int Get_alpha_beta_epsilon(REAL8 *alpha, REAL8 *cBetah, REAL8 *sBetah, REAL8 *epsilon, INT4 mprime, REAL8 Mf, IMRPhenomXPrecessionStruct *pPrec, IMRPhenomXWaveformStruct *pWF)
INT4 XLALIMRPhenomXPCheckMassesAndSpins(REAL8 *m1, REAL8 *m2, REAL8 *chi1x, REAL8 *chi1y, REAL8 *chi1z, REAL8 *chi2x, REAL8 *chi2y, REAL8 *chi2z)
Check if m1 > m2.
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.
static double beta(const double a, const double b, const sysq *system)
Internal function that computes the spin-orbit couplings.
int XLALSimInspiralWaveformParamsInsertModeArray(LALDict *params, LALValue *value)
LALValue * XLALSimInspiralWaveformParamsLookupModeArrayJframe(LALDict *params)
LALValue * XLALSimInspiralWaveformParamsLookupModeArray(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPHMModesL0Frame(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPhenomXPHMThresholdMband(LALDict *params, REAL8 value)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(LALDict *params)
REAL8 XLALSimInspiralWaveformParamsLookupPhenomXPHMThresholdMband(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPConvention(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPhenomXHMThresholdMband(LALDict *params, REAL8 value)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPHMUseModes(LALDict *params)
REAL8 XLALSimInspiralWaveformParamsLookupPhenomXHMThresholdMband(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPNRUseTunedAngles(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPHMTwistPhenomHM(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPHMPrecModes(LALDict *params)
void XLALDestroyValue(LALValue *value)
#define fprintf
int l
Definition: bh_qnmode.c:135
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
sigmaKerr data[0]
COMPLEX16FrequencySeries * XLALResizeCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series, int first, size_t length)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
#define LAL_PI
#define LAL_PC_SI
#define LIGOTIMEGPSZERO
double complex COMPLEX16
double REAL8
uint32_t UINT4
int32_t INT4
void * XLALMalloc(size_t n)
void XLALFree(void *p)
int XLALSimIMRPhenomXPHMFromModes(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, REAL8 distance, REAL8 inclination, REAL8 phiRef, REAL8 f_min, REAL8 f_max, REAL8 deltaF, REAL8 fRef_In, LALDict *lalParams)
Returns hptilde and hctilde of the multimode precessing waveform for positive frequencies in an equal...
int XLALSimIMRPhenomXPHMFrequencySequence(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, const REAL8Sequence *freqs, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, REAL8 distance, REAL8 inclination, REAL8 phiRef, REAL8 fRef_In, LALDict *lalParams)
Returns hptilde and hctilde as a complex frequency series with entries exactly at the frequencies spe...
int XLALSimIMRPhenomXPHMFrequencySequenceOneMode(COMPLEX16FrequencySeries **hlmpos, COMPLEX16FrequencySeries **hlmneg, const REAL8Sequence *freqs, const UINT4 l, const INT4 m, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, const REAL8 distance, const REAL8 inclination, const REAL8 phiRef, const REAL8 fRef_In, LALDict *lalParams)
Function to compute one hlm precessing mode on a custom frequency grid.
int XLALSimIMRPhenomXPHM(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, REAL8 distance, REAL8 inclination, REAL8 phiRef, REAL8 f_min, REAL8 f_max, REAL8 deltaF, REAL8 fRef_In, LALDict *lalParams)
Returns hptilde and hctilde of the multimode precessing waveform for positive frequencies in an equal...
int XLALSimIMRPhenomXPHMOneMode(COMPLEX16FrequencySeries **hlmpos, COMPLEX16FrequencySeries **hlmneg, const UINT4 l, const INT4 m, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, const REAL8 distance, const REAL8 inclination, const REAL8 phiRef, const REAL8 deltaF, const REAL8 f_min, const REAL8 f_max, const REAL8 fRef_In, LALDict *lalParams)
Function to compute one hlm precessing mode in an uniform frequency grid.
int IMRPhenomX_PNR_GeneratePNRAngleInterpolants(IMRPhenomX_PNR_angle_spline *angle_spline, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalparams)
Generate the tuned precession angles outlined in arXiv:2107.08876 specifically populate the angle int...
#define LAL_SIM_L_MAX_MODE_ARRAY
Maximum L spherical harmonic mode that is supported in Mode Array.
LALValue * XLALSimInspiralCreateModeArray(void)
Create a LALValue pointer to store the mode array.
int XLALSimInspiralModeArrayIsModeActive(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralModeArrayActivateMode(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralModeArrayActivateAllModesAtL(LALValue *modes, unsigned l)
COMPLEX16FrequencySeries * XLALSphHarmFrequencySeriesGetMode(SphHarmFrequencySeries *ts, UINT4 l, INT4 m)
Get the time series of a waveform's (l,m) spherical harmonic mode from a SphHarmFrequencySeries linke...
SphHarmFrequencySeries * XLALSphHarmFrequencySeriesAddMode(SphHarmFrequencySeries *appended, const COMPLEX16FrequencySeries *inmode, UINT4 l, INT4 m)
Prepend a node to a linked list of SphHarmFrequencySeries, or create a new head.
void XLALDestroySphHarmFrequencySeries(SphHarmFrequencySeries *ts)
Delete list from current pointer to the end of the list.
void XLALSphHarmFrequencySeriesSetFData(SphHarmFrequencySeries *ts, REAL8Sequence *fdata)
Set the tdata member for all nodes in the list.
static const INT4 m
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
void XLALDestroyCOMPLEX16Sequence(COMPLEX16Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
COMPLEX16Sequence * XLALCreateCOMPLEX16Sequence(size_t length)
COMPLEX16 XLALSpinWeightedSphericalHarmonic(REAL8 theta, REAL8 phi, int s, int l, int m)
const LALUnit lalStrainUnit
const LALUnit lalSecondUnit
LALUnit * XLALUnitMultiply(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
#define XLAL_PRINT_INFO(...)
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
#define XLAL_PRINT_WARNING(...)
#define XLAL_PRINT_ERROR(...)
XLAL_EBADLEN
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EDOM
XLAL_EINVAL
XLAL_FAILURE
string status
double alpha
Definition: sgwb.c:106
COMPLEX16Sequence * data
COMPLEX16 * data
gsl_spline * beta_spline
beta cubic spline
gsl_interp_accel * beta_acc
beta cubic spline accelerator
gsl_interp_accel * alpha_acc
alpha cubic spline accelerator
gsl_interp_accel * gamma_acc
gamma cubic spline accelerator
gsl_spline * gamma_spline
gamma cubic spline
gsl_spline * alpha_spline
alpha cubic spline
REAL8 epsilon_offset_3
offset passed to modes.
PhenomXPbetaMRD * beta_params
Parameters needed for analytical MRD continuation of cosbeta.
REAL8 alpha_offset_4
offset passed to modes.
REAL8 zeta_polarization
Angle to rotate the polarizations.
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 alpha_offset_1
offset passed to modes.
IMRPhenomXWaveformStruct * pWF22AS
REAL8 gamma_ftrans
Record value of gamma at end of inspiral, used in IMRPhenomXPHMTwistUp and IMRPhenomXPHMTwistUpOneMod...
INT4 IMRPhenomXPrecVersion
Flag to set version of Euler angles used.
REAL8 gamma_in
Record last value of gamma, used in IMRPhenomXPHMTwistUp and IMRPhenomXPHMTwistUpOneMode.
UINT4 PNRInspiralScaling
Enforce inpsiral scaling for HM angles outside of calibration window.
PhenomXPalphaMRD * alpha_params
Parameters needed for analytical MRD continuation of alpha.
REAL8 epsilon_offset_1
offset passed to modes.
REAL8 * data
double f_min
Definition: unicorn.c:22
double f_max
Definition: unicorn.c:23