Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-8a6b96f
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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"
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 */
58static 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
87LALDict *IMRPhenomXPHM_setup_mode_array(LALDict *lalParams);
88
89
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
108static 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) */
120static 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. */
133static 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
160This is the default function used when calling ChooseFDWaveform.
161
162It computes the non-precessing modes just once and do the twisting up according to eqs. 3.5-3.7 in the Precessing paper.
163
164It 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. */
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 */
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
290 (fRef >= pWF->fMin)&&(fRef <= pWF->f_max_prime),
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
373This function is equivalent to XLALSimIMRPhenomXPHM, gives the same result but it is computed by first calling
374all the precessing indiviudal modes in the J-frame and then sum them all with Ylm(thetaJN, 0) since the J-frame is aligned
375such 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
377This function is slower since it has to compute the non-precessing modes again for every precessing mode.
378
379It 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. */
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 */
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
495 (fRef >= pWF->fMin)&&(fRef <= pWF->f_max_prime),
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
651 (fRef >= f_min_In)&&(fRef <= f_max_In),
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. */
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 if(isnan(betaPNR_ref) || isinf(betaPNR_ref)) XLAL_ERROR(XLAL_EDOM, "Error in %s: gsl_spline_eval for beta returned invalid value.\n",__func__);
860 status = IMRPhenomX_PNR_RemapThetaJSF(betaPNR_ref, pWF, pPrec, lalParams);
864 "Error: IMRPhenomX_PNR_RemapThetaJSF failed in IMRPhenomX_PNR_GeneratePNRAngles.");
865 }
866
867 /******************************************************/
868 /******** Antisymmetric waveform generated here ********/
869 /******************************************************/
870 REAL8Sequence *antiSym_amp = NULL;
871 REAL8Sequence *antiSym_phi = NULL;
872
873 if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
874 {
875 antiSym_amp = XLALCreateREAL8Sequence(freqs->length);
876 antiSym_phi = XLALCreateREAL8Sequence(freqs->length);
877 }
878
879
880
881 /***** Loop over non-precessing modes ******/
882 for (UINT4 ell = 2; ell <= L_MAX; ell++)
883 {
884 for (UINT4 emmprime = 1; emmprime <= ell; emmprime++)
885 {
886 /* Loop over only positive mprime is intentional.
887 The single mode function returns the negative mode h_l-mprime, and the positive
888 is added automatically in during the twisting up in IMRPhenomXPHMTwistUp.
889 First check if (l,m) mode is 'activated' in the ModeArray.
890 If activated then generate the mode, else skip this mode.
891 */
892 if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ell, emmprime) != 1)
893 { /* skip mode */
894 continue;
895 } /* else: generate mode */
896
897 /* Skip twisting-up if the non-precessing mode is zero. */
898 if((pWF->q == 1) && (pWF->chi1L == pWF->chi2L) && (emmprime % 2 != 0))
899 {
900 continue;
901 }
902
903 /* Compute and store phase alignment quantities for each
904 non-precessing (ell,emm) multipole moment. Note that the
905 (2,2) moment is handled separately within XAS routines. */
906 if (
907 pWF->APPLY_PNR_DEVIATIONS && pWF->IMRPhenomXPNRForceXHMAlignment && (ell != 2) && (emmprime != 2)
908 )
909 {
910 /* Compute and store phase alignment quantities for each
911 non-precessing (ell,emm) multipole moment */
912 IMRPhenomXHM_PNR_SetPhaseAlignmentParams(ell,emmprime,pWF,pPrec,lalParams);
913 }
914
915 #if DEBUG == 1
916 printf("\n\n*********************************\n*Non-precessing Mode %i%i \n******************************\n",ell, emmprime);
917 // Save the hlm mode into a file
918 FILE *fileangle;
919 char fileSpec[40];
920
921 if(pPrec->MBandPrecVersion == 0)
922 {
923 sprintf(fileSpec, "angles_hphc_%i%i.dat", ell, emmprime);
924 }
925 else
926 {
927 sprintf(fileSpec, "angles_hphc_MB_%i%i.dat", ell, emmprime);
928 }
929 printf("\nOutput angle file: %s\r\n", fileSpec);
930 fileangle = fopen(fileSpec,"w");
931
932 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);
933 fprintf(fileangle,"#fHz cexp_i_alpha(re im) cexp_i_epsilon(re im) cexp_i_betah(re im)\n");
934
935 fclose(fileangle);
936 #endif
937
938 /* Variable to store the strain of only one (negative) mode: h_l-mprime */
939 COMPLEX16FrequencySeries *htildelm = NULL;
940
942 {
943 INT4 minus1l = 1;
944 if(ell % 2 !=0) minus1l = -1;
945 COMPLEX16FrequencySeries *htildelmPhenomHM = NULL;
946 /* Initialize the htilde frequency series */
947 htildelm = XLALCreateCOMPLEX16FrequencySeries("htildelm: FD waveform", &ligotimegps_zero, 0, pWF->deltaF, &lalStrainUnit, npts);
948 /* Check that frequency series generated okay */
949 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);
950 memset((htildelm)->data->data, 0, npts * sizeof(COMPLEX16));
951 XLALUnitMultiply(&((htildelm)->sampleUnits), &((htildelm)->sampleUnits), &lalSecondUnit);
952
953 htildelmPhenomHM = XLALSphHarmFrequencySeriesGetMode(*hlms, ell, emmprime);
954 for(UINT4 idx = 0; idx < freqs->length; idx++)
955 {
956 htildelm->data->data[idx+offset] = minus1l * htildelmPhenomHM->data->data[idx] * pWF->amp0;
957 }
958 //XLALDestroyCOMPLEX16FrequencySeries(htildelmPhenomHM);
959 }
960 else
961 {
962 /* Compute non-precessing mode */
963 if (thresholdMB == 0){ // No multibanding
964 if(ell == 2 && emmprime == 2)
965 {
966 status = IMRPhenomXASGenerateFD(&htildelm, freqs, pWF, lalParams);
967 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXASGenerateFD failed to generate IMRPhenomXHM waveform.");
968 }
969 else
970 {
971 status = IMRPhenomXHMGenerateFDOneMode(&htildelm, freqs, pWF, ell, emmprime, lalParams);
972 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMGenerateFDOneMode failed to generate IMRPhenomXHM waveform.");
973 }
974 }
975 else{ // With multibanding
976 if(ell==3 && emmprime==2){ // mode with mode-mixing
977 status = IMRPhenomXHMMultiBandOneModeMixing(&htildelm, htilde22, pWF, ell, emmprime, lalParams);
978 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMMultiBandOneModeMixing failed to generate IMRPhenomXHM waveform.");
979 }
980 else{ // modes without mode-mixing including 22 mode
981 status = IMRPhenomXHMMultiBandOneMode(&htildelm, pWF, ell, emmprime, lalParams);
982 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMMultiBandOneMode failed to generate IMRPhenomXHM waveform.");
983 }
984
985 /* IMRPhenomXHMMultiBandOneMode* functions set pWF->deltaF=0 internally, we put it back here. */
986 pWF->deltaF = deltaF;
987
988 /* 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.
989 The 22 mode is always computed first than the 32, we store the 22 mode in the variable htilde22. */
990 if(ell==2 && emmprime==2 && XLALSimInspiralModeArrayIsModeActive(ModeArray, 3, 2)==1){
991 htilde22 = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &(ligotimegps_zero), 0.0, pWF->deltaF, &lalStrainUnit, htildelm->data->length);
992 for(UINT4 idx = 0; idx < htildelm->data->length; idx++){
993 htilde22->data->data[idx] = htildelm->data->data[idx];
994 }
995 }
996 }
997 }
998
999 if(ell==2 && emmprime==2)
1000 {
1001 if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
1002 {
1003 IMRPhenomX_PNR_GenerateAntisymmetricWaveform(antiSym_amp,antiSym_phi,freqs,pWF,pPrec,lalParams);
1004 }
1005 }
1006
1007 if (!(htildelm)){ XLAL_ERROR(XLAL_EFUNC); }
1008
1009 /*
1010 For very special cases of deltaF, it can happen that building htildelm with 'freqs_In' or with 'freqs' gives different lengths.
1011 In case that happens we resize here to the correct length. We could also have called GenerateFD passing freqs_In,
1012 but in that ways we would be computing the uniform frequency array twice.
1013 Alsom doing as here we cover the multibanding and PhenomHM cases.
1014 */
1015 if(htildelm->data->length != npts)
1016 {
1017 htildelm = XLALResizeCOMPLEX16FrequencySeries(htildelm, 0, npts);
1018 XLAL_CHECK (htildelm, XLAL_ENOMEM, "Failed to resize hlm COMPLEX16FrequencySeries" );
1019 }
1020
1021 /* htildelm is recomputed every time in the loop. Check that it always comes out with the same length */
1022 XLAL_CHECK ( ((*hptilde)->data->length==htildelm->data->length)
1023 && ((*hctilde)->data->length==htildelm->data->length),
1025 "Inconsistent lengths between frequency series htildelm (%d), hptilde (%d) and hctilde (%d).",
1026 htildelm->data->length, (*hptilde)->data->length, (*hctilde)->data->length
1027 );
1028
1029 /*
1030 TWISTING UP
1031 Transform modes from the precessing L-frame to inertial J-frame.
1032 */
1033
1034
1035 /* Variable to store the non-precessing waveform in one frequency point. */
1036 COMPLEX16 hlmcoprec=0.0;
1037 COMPLEX16 hlmcoprec_antiSym=0.0;
1038
1039 /* No Multibanding for the angles. */
1040 if(pPrec->MBandPrecVersion == 0)
1041 {
1042 #if DEBUG == 1
1043 printf("\n****************************************************************\n");
1044 printf("\n* NOT USING MBAND FOR ANGLES %i *\n", offset);
1045 printf("\n****************************************************************\n");
1046 #endif
1047
1048 // Let the people know if twisting up will not take place
1049 if( pWF->IMRPhenomXReturnCoPrec == 1 )
1050 {
1051 #if DEBUG == 1
1052 printf("\n** We will not twist up the HM waveforms **\n");
1053 #endif
1054 }
1055
1056 /* set variables for PNR angles if needed */
1057 REAL8 Mf_high = 0.0;
1058 REAL8 Mf_low = 0.0;
1059 UINT4 PNRtoggleInspiralScaling = pPrec->PNRInspiralScaling;
1060
1061 // set PNR transition frequencies if needed
1062 if (IMRPhenomXPNRUseTunedAngles)
1063 {
1064
1065 if((ell==2)&&(emmprime==2))
1066 {
1067 /* the frequency parameters don't matter in this case */
1068 Mf_RD_lm = 0.0;
1069 }
1070 else
1071 {
1072 /* Get the (l,m) RD frequency */
1073
1074 Mf_RD_lm = IMRPhenomXHM_GenerateRingdownFrequency(ell, emmprime, pWF);
1075
1076 status = IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies(&Mf_low, &Mf_high, emmprime, Mf_RD_22, Mf_RD_lm, pPrec);
1077 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies failed.\n");
1078 }
1079 }
1080
1081 if(pPrec->precessing_tag==3)
1082 pPrec->gamma_in = 0.;
1083
1084 for (UINT4 idx = 0; idx < freqs->length; idx++)
1085 {
1086 double Mf = pWF->M_sec * freqs->data[idx];
1087
1088 /* Do not generate waveform above Mf_max (default Mf = 0.3) */
1089 if(Mf <= (pWF->f_max_prime * pWF->M_sec))
1090 {
1091 hlmcoprec = htildelm->data->data[idx + offset]; /* Co-precessing waveform for one freq point */
1092 COMPLEX16 hplus = 0.0; /* h_+ */
1093 COMPLEX16 hcross = 0.0; /* h_x */
1094
1095 if( pWF->IMRPhenomXReturnCoPrec == 1 )
1096 {
1097 // Do not twist up
1098 hplus = 0.5 * hlmcoprec;
1099 hcross = -0.5 * I * hlmcoprec;
1100
1101 //
1102 if( pWF->PhenomXOnlyReturnPhase ){
1103 // Set hplus to phase (as will be stored in hlmcoprec) and hcross to zero
1104 hplus = hlmcoprec; // NOTE that here hlmcoprec = waveform_phase (assuming one multipole moment)
1105 hcross = 0;
1106 }
1107
1108 }
1109 else
1110 {
1111 if(IMRPhenomXPNRUseTunedAngles)
1112 {
1113 REAL8 Mf_mapped = IMRPhenomX_PNR_LinearFrequencyMap(Mf, ell, emmprime, Mf_low, Mf_high, Mf_RD_22, Mf_RD_lm, PNRtoggleInspiralScaling);
1114 REAL8 f_mapped = XLALSimIMRPhenomXUtilsMftoHz(Mf_mapped, pWF->Mtot);
1115
1116 pPrec->alphaPNR = gsl_spline_eval(hm_angle_spline->alpha_spline, f_mapped, hm_angle_spline->alpha_acc);
1117 pPrec->betaPNR = gsl_spline_eval(hm_angle_spline->beta_spline, f_mapped, hm_angle_spline->beta_acc);
1118 pPrec->gammaPNR = gsl_spline_eval(hm_angle_spline->gamma_spline, f_mapped, hm_angle_spline->gamma_acc);
1119 }
1120
1121 // Twist up symmetric strain
1122 status = IMRPhenomXPHMTwistUp(Mf, hlmcoprec, pWF, pPrec, ell, emmprime, &hplus, &hcross);
1123 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXPHMTwistUp failed.");
1124
1125 if(ell == 2 && emmprime == 2 && AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
1126 {
1127 COMPLEX16 hplus_antiSym = 0.0;
1128 COMPLEX16 hcross_antiSym = 0.0;
1129 hlmcoprec_antiSym = antiSym_amp->data[idx]*cexp(I*antiSym_phi->data[idx]);
1130 pPrec->PolarizationSymmetry = -1.0;
1131 IMRPhenomXPHMTwistUp(Mf, hlmcoprec_antiSym, pWF, pPrec, ell, emmprime, &hplus_antiSym, &hcross_antiSym);
1132 pPrec->PolarizationSymmetry = 1.0;
1133 hplus += hplus_antiSym;
1134 hcross += hcross_antiSym;
1135 }
1136 }
1137
1138 (*hptilde)->data->data[idx + offset] += hplus;
1139 (*hctilde)->data->data[idx + offset] += hcross;
1140 }
1141 else
1142 {
1143 /* Mf > Mf_max, so return 0 */
1144 (*hptilde)->data->data[idx + offset] += 0.0 + I*0.0;
1145 (*hctilde)->data->data[idx + offset] += 0.0 + I*0.0;
1146 }
1147 }
1148
1149 if(IMRPhenomXPNRUseTunedAngles)
1150 {
1151 gsl_interp_accel_reset(hm_angle_spline->alpha_acc);
1152 gsl_interp_accel_reset(hm_angle_spline->beta_acc);
1153 gsl_interp_accel_reset(hm_angle_spline->gamma_acc);
1154 }
1155
1156 // If we only want the coprecessing waveform, then exit
1157 // if( pWF->IMRPhenomXReturnCoPrec == 1 ) return XLAL_SUCCESS;
1158 if( pWF->IMRPhenomXReturnCoPrec == 1 ) {
1159 return XLAL_SUCCESS;
1160 }
1161
1162 }
1163 else
1164 {
1165 /*
1166 Multibanding for the angles.
1167
1168 - In this first release we use the same coarse grid that is used for computing the non-precessing modes.
1169 - This grid is discussed in section II-A of arXiv:2001.10897. See also section D of Precessing paper.
1170 - This grid is computed with the function XLALSimIMRPhenomXMultibandingVersion defined in LALSimIMRPhenomXHM_multiband.c.
1171 - The version of the coarse grid will be changed with the option 'MBandPrecVersion' defined in LALSimInspiralWaveformParams.c.
1172 - Currently there is only one version available and the option value for that is 0, which is the default value.
1173 */
1174
1175 #if DEBUG == 1
1176 printf("\n****************************************************************\n");
1177 printf("\n* USING MBAND FOR ANGLES *\n");
1178 printf("\n****************************************************************\n");
1179 #endif
1180
1181
1182
1183 /* Compute non-uniform coarse frequency grid as 1D array */
1184 REAL8Sequence *coarseFreqs;
1185 XLALSimIMRPhenomXPHMMultibandingGrid(&coarseFreqs, ell, emmprime, pWF, lalParams);
1186
1187 UINT4 lenCoarseArray = coarseFreqs->length;
1188
1189
1190 /* Euler angles */
1191 REAL8 alpha = 0.0;
1192 REAL8 epsilon = 0.0;
1193
1194 REAL8 cBetah = 0.0;
1195 REAL8 sBetah = 0.0;
1196
1197 /* Variables to store the Euler angles in the coarse frequency grid. */
1198 REAL8 *valpha = (REAL8*)XLALMalloc(lenCoarseArray * sizeof(REAL8));
1199 REAL8 *vepsilon = (REAL8*)XLALMalloc(lenCoarseArray * sizeof(REAL8));
1200 REAL8 *vbetah = (REAL8*)XLALMalloc(lenCoarseArray * sizeof(REAL8));
1201
1202 if(IMRPhenomXPNRUseTunedAngles)
1203 {
1204 REAL8 Mf_high = 0.0;
1205 REAL8 Mf_low = 0.0;
1206 REAL8 fCut = pWF->fCut;
1207
1208 if ((ell==2)&&(emmprime==2))
1209 {
1210 /* the frequency parameters don't matter here */
1211 Mf_RD_lm = 0.0;
1212 }
1213 else
1214 {
1215 /* Get the (l,m) RD frequency */
1216
1217 Mf_RD_lm = IMRPhenomXHM_GenerateRingdownFrequency(ell, emmprime, pWF);
1218
1219 status = IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies(&Mf_low, &Mf_high, emmprime, Mf_RD_22, Mf_RD_lm, pPrec);
1220 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies failed.\n");
1221 }
1222
1223 UINT4 PNRtoggleInspiralScaling = pPrec->PNRInspiralScaling;
1224 #if DEBUG == 1
1225 // Save the hlm mode into a file
1226 FILE *fileangle0101;
1227 char fileSpec0101[40];
1228
1229 sprintf(fileSpec0101, "angles_pnr_MB_%i%i.dat", ell, emmprime);
1230
1231 fileangle0101 = fopen(fileSpec0101,"w");
1232
1233 fprintf(fileangle0101,"#Mf fHz alpha beta gamma\n");
1234
1235 fprintf(fileangle0101,"#Mf_low = %.16e\n",Mf_low);
1236 fprintf(fileangle0101,"#Mf_high = %.16e\n",Mf_high);
1237 fprintf(fileangle0101,"#Mf_RD_22 = %.16e\n",Mf_RD_22);
1238 fprintf(fileangle0101,"#Mf_RD_lm = %.16e\n",Mf_RD_lm);
1239
1240 #endif
1241
1242 for(UINT4 j=0; j<lenCoarseArray; j++)
1243 {
1244 REAL8 Mf = coarseFreqs->data[j];
1245 REAL8 Mf_mapped = IMRPhenomX_PNR_LinearFrequencyMap(Mf, ell, emmprime, Mf_low, Mf_high, Mf_RD_22, Mf_RD_lm, PNRtoggleInspiralScaling);
1246 REAL8 f_mapped = XLALSimIMRPhenomXUtilsMftoHz(Mf_mapped, pWF->Mtot);
1247
1248 /* add in security to avoid frequency extrapolation */
1249 f_mapped = (f_mapped > fCut) ? fCut : f_mapped;
1250
1251 double beta = gsl_spline_eval(hm_angle_spline->beta_spline, f_mapped, hm_angle_spline->beta_acc);
1252
1253 valpha[j] = gsl_spline_eval(hm_angle_spline->alpha_spline, f_mapped, hm_angle_spline->alpha_acc) - pPrec->alpha_offset;
1254 vepsilon[j] = -1.0 * gsl_spline_eval(hm_angle_spline->gamma_spline, f_mapped, hm_angle_spline->gamma_acc) - pPrec->epsilon_offset;
1255 vbetah[j] = beta / 2.0;
1256
1257 #if DEBUG == 1
1258
1259 fprintf(fileangle0101,"%.16e\t%.16e\t%.16e\t%.16e\t%.16e\n",Mf,f_mapped,valpha[j],beta,vepsilon[j]);
1260
1261 #endif
1262 }
1263
1264
1265 #if DEBUG == 1
1266 fclose(fileangle0101);
1267 #endif
1268
1269 gsl_interp_accel_reset(hm_angle_spline->alpha_acc);
1270 gsl_interp_accel_reset(hm_angle_spline->beta_acc);
1271 gsl_interp_accel_reset(hm_angle_spline->gamma_acc);
1272
1273 }
1274 else
1275 {
1276 switch(pPrec->IMRPhenomXPrecVersion)
1277 {
1278 case 101:
1279 case 102:
1280 case 103:
1281 case 104:
1282 {
1283 /* Use NNLO PN Euler angles */
1284 /* Evaluate angles in coarse freq grid */
1285 for(UINT4 j=0; j<lenCoarseArray; j++)
1286 {
1287 REAL8 Mf = coarseFreqs->data[j];
1288
1289 /* This function already add the offsets to the angles. */
1290 Get_alpha_beta_epsilon(&alpha, &cBetah, &sBetah, &epsilon, emmprime, Mf, pPrec, pWF);
1291
1292 valpha[j] = alpha;
1293 vepsilon[j] = epsilon;
1294 vbetah[j] = acos(cBetah);
1295 }
1296 break;
1297 }
1298 case 220:
1299 case 221:
1300 case 222:
1301 case 223:
1302 case 224:
1303 {
1304 /* Use MSA Euler angles. */
1305 /* Evaluate angles in coarse freq grid */
1306 for(UINT4 j=0; j<lenCoarseArray; j++)
1307 {
1308 /* Get Euler angles. */
1309 REAL8 Mf = coarseFreqs->data[j];
1310 const REAL8 v = cbrt (LAL_PI * Mf * (2.0 / emmprime) );
1311 const vector vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWF,pPrec);
1312 REAL8 cos_beta = 0.0;
1313
1314 /* Get the offset for the Euler angles alpha and epsilon. */
1315 REAL8 alpha_offset_mprime = 0, epsilon_offset_mprime = 0;
1316 Get_alpha_epsilon_offset(&alpha_offset_mprime, &epsilon_offset_mprime, emmprime, pPrec);
1317
1318 valpha[j] = vangles.x - alpha_offset_mprime;
1319 vepsilon[j] = vangles.y - epsilon_offset_mprime;
1320 cos_beta = vangles.z;
1321
1322 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1323 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1324
1325 vbetah[j] = acos(cBetah);
1326 }
1327 break;
1328 }
1329
1330 case 310:
1331 case 311:
1332 case 320:
1333 case 321:
1334 case 330:
1335
1336 {
1337
1338 /* Get the offset for the Euler angles alpha and epsilon. */
1339 REAL8 alpha_offset_mprime = pPrec->alpha_ref- pPrec->alpha0;
1340 REAL8 epsilon_offset_mprime = -pPrec->gamma_ref-pPrec->epsilon0;
1341
1342 REAL8 cos_beta=0., gamma=0., alpha_i=0.;
1343 REAL8 Mf;
1344 int success;
1345
1346
1347 /* Evaluate angles in coarse freq grid */
1348 for(UINT4 j=0; j<lenCoarseArray; j++)
1349 {
1350
1351 success = 0;
1352 Mf = coarseFreqs->data[j]*(2.0/emmprime);
1353
1354
1355 if(Mf< pPrec->ftrans_MRD)
1356 {
1357 success = gsl_spline_eval_e(pPrec->alpha_spline, Mf, pPrec->alpha_acc,&alpha_i);
1358 success = success + gsl_spline_eval_e(pPrec->cosbeta_spline, Mf, pPrec->cosbeta_acc,&cos_beta);
1359 success = success + gsl_spline_eval_e(pPrec->gamma_spline, Mf, pPrec->gamma_acc, &gamma);
1360
1361 XLAL_CHECK(success == XLAL_SUCCESS, XLAL_EFUNC, "%s: Failed to interpolate Euler angles at f=%.7f. \n",__func__,XLALSimIMRPhenomXUtilsMftoHz(Mf,pWF->Mtot));
1362 }
1363
1364 else {
1365
1366 if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321 ){
1367
1368 alpha_i=alphaMRD(Mf,pPrec->alpha_params);
1369 cos_beta=cos(betaMRD(Mf,pWF,pPrec->beta_params));
1370 success = gsl_spline_eval_e(pPrec->gamma_spline, Mf, pPrec->gamma_acc, &gamma);
1371 if(success!=XLAL_SUCCESS){
1372
1373 if(j>0)
1374 {
1375 REAL8 dMf=(coarseFreqs->data[j]-coarseFreqs->data[j-1])* (2.0 / emmprime);
1376 REAL8 deltagamma=0.;
1377 success = gamma_from_alpha_cosbeta(&deltagamma, Mf,dMf,pWF,pPrec);
1378 if(success!=XLAL_SUCCESS) gamma = pPrec->gamma_in;
1379 else gamma = pPrec->gamma_in+deltagamma;
1380
1381 }
1382
1383 else
1384
1385 {
1386 success = gsl_spline_eval_e(pPrec->gamma_spline, Mf, pPrec->gamma_acc,&gamma);
1387 if(success!=XLAL_SUCCESS) gamma = pPrec->gamma_in;
1388 }
1389 }
1390
1391 }
1392
1393 else{
1394
1395 alpha_i=pPrec->alpha_ftrans;
1396 cos_beta=pPrec->cosbeta_ftrans;
1397 gamma=pPrec->gamma_ftrans;
1398
1399 }
1400
1401 }
1402
1403 pPrec->gamma_in = gamma;
1404
1405 // make sure |cos(beta)| does not exceed 1 due to roundoff errors
1406 if(fabs(cos_beta)>1)
1407 cos_beta=copysign(1.0, cos_beta);
1408
1409 valpha[j]= alpha_i- alpha_offset_mprime;
1410 vepsilon[j] = -gamma - epsilon_offset_mprime;
1411
1412
1413 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1414 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1415 vbetah[j] = acos(cBetah);
1416
1417 }
1418
1419
1420 break;
1421 }
1422
1423
1424
1425 default:
1426 {
1427 XLAL_ERROR(XLAL_EINVAL,"Error: IMRPhenomXPrecVersion not recognized. Recommended default is 223.\n");
1428 break;
1429 }
1430 }
1431 }
1432
1433 /*
1434 We have the three Euler angles evaluated in the coarse frequency grid.
1435 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..
1436 The result will be three arrays of complex exponential evaluated in the finefreqs.
1437 */
1438 UINT4 fine_count = 0, ratio;
1439 REAL8 Omega_alpha, Omega_epsilon, Omega_betah, Qalpha, Qepsilon, Qbetah;
1440 REAL8 Mfhere, Mfnext, evaldMf;
1441 Mfnext = coarseFreqs->data[0];
1442 evaldMf = XLALSimIMRPhenomXUtilsHztoMf(pWF->deltaF, pWF->Mtot);
1443
1444 /*
1445 Number of points where the waveform will be computed.
1446 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.
1447 */
1448 size_t iStop = (size_t) (pWF->f_max_prime / pWF->deltaF) + 1 - offset;
1449
1450 UINT4 length_fine_grid = iStop + 3; // This is just to reserve memory, add 3 points of buffer.
1451
1452 COMPLEX16 *cexp_i_alpha = (COMPLEX16*)XLALMalloc(length_fine_grid * sizeof(COMPLEX16));
1453 COMPLEX16 *cexp_i_epsilon = (COMPLEX16*)XLALMalloc(length_fine_grid * sizeof(COMPLEX16));
1454 COMPLEX16 *cexp_i_betah = (COMPLEX16*)XLALMalloc(length_fine_grid * sizeof(COMPLEX16));
1455
1456
1457 #if DEBUG == 1
1458 printf("\n\nLENGTHS fine grid estimate, coarseFreqs->length = %i %i\n", length_fine_grid, lenCoarseArray);
1459 printf("fine_count, htildelm->length, offset = %i %i %i\n", fine_count, htildelm->data->length, offset);
1460 #endif
1461
1462 /* Loop over the coarse freq points */
1463 for(UINT4 j = 0; j<lenCoarseArray-1 && fine_count < iStop; j++)
1464 {
1465 Mfhere = Mfnext;
1466 Mfnext = coarseFreqs->data[j+1];
1467
1468 Omega_alpha = (valpha[j + 1] - valpha[j]) /(Mfnext - Mfhere);
1469 Omega_epsilon = (vepsilon[j + 1] - vepsilon[j])/(Mfnext - Mfhere);
1470 Omega_betah = (vbetah[j + 1] - vbetah[j]) /(Mfnext - Mfhere);
1471
1472 cexp_i_alpha[fine_count] = cexp(I*valpha[j]);
1473 cexp_i_epsilon[fine_count] = cexp(I*vepsilon[j]);
1474 cexp_i_betah[fine_count] = cexp(I*vbetah[j]);
1475
1476 Qalpha = cexp(I*evaldMf*Omega_alpha);
1477 Qepsilon = cexp(I*evaldMf*Omega_epsilon);
1478 Qbetah = cexp(I*evaldMf*Omega_betah);
1479
1480 fine_count++;
1481
1482 REAL8 dratio = (Mfnext-Mfhere)/evaldMf;
1483 UINT4 ceil_ratio = ceil(dratio);
1484 UINT4 floor_ratio = floor(dratio);
1485
1486 /* Make sure the rounding is done correctly. */
1487 if(fabs(dratio-ceil_ratio) < fabs(dratio-floor_ratio))
1488 {
1489 ratio = ceil_ratio;
1490 }
1491 else
1492 {
1493 ratio = floor_ratio;
1494 }
1495
1496 /* Compute complex exponential in fine points between two coarse points */
1497 /* This loop carry out the eq. 2.32 in arXiv:2001.10897 */
1498 for(UINT4 kk = 1; kk < ratio && fine_count < iStop; kk++){
1499 cexp_i_alpha[fine_count] = Qalpha*cexp_i_alpha[fine_count-1];
1500 cexp_i_epsilon[fine_count] = Qepsilon*cexp_i_epsilon[fine_count-1];
1501 cexp_i_betah[fine_count] = Qbetah*cexp_i_betah[fine_count-1];
1502 fine_count++;
1503 }
1504 }// Loop over coarse grid
1505
1506 /*
1507 Now we have the complex exponentials of the three Euler angles alpha, beta, epsilon evaluated in the fine frequency grid.
1508 Next step is do the twisting up with these.
1509 */
1510
1511 #if DEBUG == 1
1512 printf("fine_count, htildelm->length, offset = %i %i %i\n", fine_count, htildelm->data->length, offset);
1513 #endif
1514
1515
1516 /************** TWISTING UP in the fine grid *****************/
1517 for (UINT4 idx = 0; idx < fine_count; idx++)
1518 {
1519 double Mf = pWF->M_sec * (idx + offset)*pWF->deltaF;
1520
1521 hlmcoprec = htildelm->data->data[idx + offset]; /* Co-precessing waveform */
1522
1523 COMPLEX16 hplus = 0.0; /* h_+ */
1524 COMPLEX16 hcross = 0.0; /* h_x */
1525
1526 pPrec->cexp_i_alpha = cexp_i_alpha[idx];
1527 pPrec->cexp_i_epsilon = cexp_i_epsilon[idx];
1528 pPrec->cexp_i_betah = cexp_i_betah[idx];
1529
1530 if(pPrec->precessing_tag==3) pPrec->gamma_in = 0.;
1531
1532 status = IMRPhenomXPHMTwistUp(Mf, hlmcoprec, pWF, pPrec, ell, emmprime, &hplus, &hcross);
1533 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXPHMTwistUp failed.");
1534
1535 if(ell == 2 && emmprime == 2 && AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
1536 {
1537 COMPLEX16 hplus_antiSym = 0.0;
1538 COMPLEX16 hcross_antiSym = 0.0;
1539 hlmcoprec_antiSym = antiSym_amp->data[idx] * cexp(I*antiSym_phi->data[idx]);
1540 pPrec->PolarizationSymmetry = -1.0;
1541 IMRPhenomXPHMTwistUp(Mf, hlmcoprec_antiSym, pWF, pPrec, ell, emmprime, &hplus_antiSym, &hcross_antiSym);
1542 pPrec->PolarizationSymmetry = 1.0;
1543 hplus += hplus_antiSym;
1544 hcross += hcross_antiSym;
1545 }
1546
1547 (*hptilde)->data->data[idx + offset] += hplus ;
1548 (*hctilde)->data->data[idx + offset] += hcross ;
1549
1550 }
1551
1552 XLALDestroyREAL8Sequence(coarseFreqs);
1553 LALFree(valpha);
1554 LALFree(vepsilon);
1555 LALFree(vbetah);
1556 LALFree(cexp_i_alpha);
1557 LALFree(cexp_i_epsilon);
1558 LALFree(cexp_i_betah);
1559 }// End of Multibanding-specific.
1560
1562 }//Loop over emmprime
1563 }//Loop over ell
1564
1565 if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
1566 {
1567 XLALDestroyREAL8Sequence(antiSym_amp);
1568 XLALDestroyREAL8Sequence(antiSym_phi);
1569 }
1570
1571 if (IMRPhenomXPNRUseTunedAngles){
1572 gsl_spline_free(hm_angle_spline->alpha_spline);
1573 gsl_spline_free(hm_angle_spline->beta_spline);
1574 gsl_spline_free(hm_angle_spline->gamma_spline);
1575
1576 gsl_interp_accel_free(hm_angle_spline->alpha_acc);
1577 gsl_interp_accel_free(hm_angle_spline->beta_acc);
1578 gsl_interp_accel_free(hm_angle_spline->gamma_acc);
1579
1580 LALFree(hm_angle_spline);
1581 }
1582
1583 // Free memory used to hold non-precesing XHM struct
1585 // Cleaning up
1586 LALFree(pPrec->pWF22AS);
1587 }
1588
1590 XLALFree(hlms);
1591 /*
1592 Loop over h+ and hx to rotate waveform by 2 \zeta.
1593 See discussion in Appendix C: Frame Transformation and Polarization Basis.
1594 The formula for \zeta is given by eq. C26.
1595 */
1596 if(fabs(pPrec->zeta_polarization) > 0.0)
1597 {
1598 COMPLEX16 PhPpolp, PhPpolc;
1599 REAL8 cosPolFac, sinPolFac;
1600
1601 cosPolFac = cos(2.0 * pPrec->zeta_polarization);
1602 sinPolFac = sin(2.0 * pPrec->zeta_polarization);
1603
1604 for (UINT4 i = offset; i < (*hptilde)->data->length; i++)
1605 {
1606 PhPpolp = (*hptilde)->data->data[i];
1607 PhPpolc = (*hctilde)->data->data[i];
1608
1609 (*hptilde)->data->data[i] = cosPolFac * PhPpolp + sinPolFac * PhPpolc;
1610 (*hctilde)->data->data[i] = cosPolFac * PhPpolc - sinPolFac * PhPpolp;
1611 }
1612 }
1613
1614 /* Free memory */
1616 XLALDestroyValue(ModeArray);
1618
1619
1620 if(pPrec->precessing_tag==3)
1621 {
1622 LALFree(pPrec->alpha_params);
1623 LALFree(pPrec->beta_params);
1624
1625 gsl_spline_free(pPrec->alpha_spline);
1626 gsl_spline_free(pPrec->cosbeta_spline);
1627 gsl_spline_free(pPrec->gamma_spline);
1628
1629 gsl_interp_accel_free(pPrec->alpha_acc);
1630 gsl_interp_accel_free(pPrec->gamma_acc);
1631 gsl_interp_accel_free(pPrec->cosbeta_acc);
1632
1633 }
1634
1635
1636 #if DEBUG == 1
1637 printf("\n******Leaving IMRPhenomXPHM_hplushcross*****\n");
1638 #endif
1639
1640 return XLAL_SUCCESS;
1641}
1642
1643
1644
1645/*
1646 Core function of XLALSimIMRPhenomXPHMFromModes and XLALSimIMRPhenomXPHMFrequencySequence.
1647 Returns hptilde, hctilde for positive frequencies.
1648 The default non-precessing modes twisted up are 2|2|, 2|1|, 3|3|, 3|2| and 4|4|.
1649 It returns also the contribution of the corresponding negatives modes.
1650 It returns the same result than IMRPhenomXPHM_hplushcross but here it calls the individual precessing modes
1651 and then sum them all. It is therefor slower and does not include Multibanding for the angles.
1652 It can be evaulated in a non-uniform frequency grid. Assume positive frequencies.
1653*/
1655 COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency domain h+ GW strain */
1656 COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency domain hx GW strain */
1657 const REAL8Sequence *freqs_In, /**< Frequency array to evaluate the model. (fmin, fmax) for equally spaced grids. */
1658 IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
1659 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
1660 LALDict *lalParams /**< LAL Dictionary Structure */
1661)
1662{
1663
1664 if (pWF->f_max_prime <= pWF->fMin)
1665 XLAL_ERROR(XLAL_EDOM, "(fCut = %g Hz) <= f_min = %g\n", pWF->f_max_prime, pWF->fMin);
1666
1667 /* Set LIGOTimeGPS */
1668 LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO; // = {0,0}
1669
1670 lalParams = IMRPhenomXPHM_setup_mode_array(lalParams);
1671 LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(lalParams);
1672
1673 /* At this point ModeArray should contain the list of modes
1674 and therefore if NULL then something is wrong and abort. */
1675 if (ModeArray == NULL)
1676 {
1677 XLAL_ERROR(XLAL_EDOM, "ModeArray is NULL when it shouldn't be. Aborting.\n");
1678 }
1679
1680 INT4 status = 0; //Variable to check correc functions calls
1681
1682
1683 /* Build the frequency array and initialize hctilde to the length of freqs. */
1684 REAL8Sequence *freqs;
1685 UINT4 offset = SetupWFArrays(&freqs, hptilde, freqs_In, pWF, ligotimegps_zero);
1686
1687 /* Initialize hctilde according to hptilde. */
1688 size_t npts = (*hptilde)->data->length;
1689 *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &ligotimegps_zero, (*hptilde)->f0, pWF->deltaF, &lalStrainUnit, npts);
1690 XLAL_CHECK (*hctilde, XLAL_ENOMEM, "Failed to allocated waveform COMPLEX16FrequencySeries of length %zu.", npts);
1691 memset((*hctilde)->data->data, 0, npts * sizeof(COMPLEX16));
1692 XLALUnitMultiply(&((*hctilde)->sampleUnits), &((*hctilde)->sampleUnits), &lalSecondUnit);
1693
1694 /* Initialize useful powers of pi for the higher modes internal code. */
1696 XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
1697
1698 if(pPrec->precessing_tag==3){
1699 status=IMRPhenomX_Initialize_Euler_Angles(pWF,pPrec,lalParams);
1700 XLAL_CHECK(status==XLAL_SUCCESS, XLAL_EDOM, "%s: Error in IMRPhenomX_Initialize_Euler_Angles.\n",__func__);
1701 }
1702
1703 /* Loop over precessing modes */
1704 for(UINT4 ell = 2; ell <= 4; ell++)
1705 {
1706 for(INT4 emm = -1*ell; emm <= (INT4)ell; emm++)
1707 {
1708 #if DEBUG == 1
1709 printf("\n*****************************************************************\n");
1710 printf(" Precessing mode (%i%i) ", ell, emm);
1711 printf("*******************************************************************\n");
1712 #endif
1713
1714 COMPLEX16FrequencySeries *hlmpos = NULL;
1715 COMPLEX16FrequencySeries *hlmneg = NULL;
1716
1717 /* We now call one single precessing mode. */
1718 status = IMRPhenomXPHM_OneMode(&hlmpos, &hlmneg, freqs, pWF, pPrec, ell, emm, lalParams);
1719 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_OneMode failed to generate IMRPhenomXHM waveform.");
1720
1721 if (!(hlmpos)){ XLAL_ERROR(XLAL_EFUNC);}
1722 if (!(hlmneg)){ XLAL_ERROR(XLAL_EFUNC);}
1723
1724
1725 /* hlmpos and hlmneg are recomputed every time in the loop. Check that they always come out with the same length. */
1726 XLAL_CHECK ( ((*hptilde)->data->length == hlmpos->data->length) && ((*hptilde)->data->length == hlmpos->data->length)
1727 && (hlmpos->data->length == hlmneg->data->length),
1729 "Inconsistent lengths between frequency series hlmpos (%d), hlmneg (%d), hptilde (%d) and hctilde (%d).",
1730 hlmpos->data->length, hlmneg->data->length, (*hptilde)->data->length, (*hctilde)->data->length
1731 );
1732
1733 /*
1734 The precessing modes hlm that we just computed are in the J-frame.
1735 For computing hplus and hcross we have to sum them all with Ylm(thetaJN, 0) since the J-frame is aligned
1736 such that the line of sight N is in the x-z plane of the J-frame.
1737 See appendix C of Precessing Paper, in particular eq. C8.
1738 */
1739 COMPLEX16 Ylm = XLALSpinWeightedSphericalHarmonic(pPrec->thetaJN, 0, -2, ell, emm);
1740 COMPLEX16 Ylmstar = conj(Ylm);
1741
1742 for(UINT4 i = offset; i < (hlmpos)->data->length; i++)
1743 {
1744 (*hptilde)->data->data[i] += 0.5*(hlmpos->data->data[i] * Ylm + conj(hlmneg->data->data[i]) * Ylmstar);
1745 (*hctilde)->data->data[i] += I*0.5*(hlmpos->data->data[i] * Ylm - conj(hlmneg->data->data[i]) * Ylmstar);
1746 }
1747
1750 }
1751 }// End loop over precessing modes
1752
1753 /*
1754 Loop over h+ and hx to rotate waveform by 2 \zeta.
1755 See discussion in Appendix C: Frame Transformation and Polarization Basis.
1756 The formula for \zeta is given by eq. C24.
1757 */
1758 if(fabs(pPrec->zeta_polarization) > 0)
1759 {
1760 COMPLEX16 PhPpolp, PhPpolc;
1761 REAL8 cosPolFac, sinPolFac;
1762
1763 cosPolFac = cos(2.0 * pPrec->zeta_polarization);
1764 sinPolFac = sin(2.0 * pPrec->zeta_polarization);
1765
1766 for (UINT4 i = offset; i < (*hptilde)->data->length; i++)
1767 {
1768 PhPpolp = (*hptilde)->data->data[i];
1769 PhPpolc = (*hctilde)->data->data[i];
1770
1771 (*hptilde)->data->data[i] = cosPolFac * PhPpolp + sinPolFac * PhPpolc;
1772 (*hctilde)->data->data[i] = cosPolFac * PhPpolc - sinPolFac * PhPpolp;
1773 }
1774 }
1775
1776 /* Free memory */
1777 XLALDestroyValue(ModeArray);
1779
1780 if(pPrec->precessing_tag==3)
1781 {
1782 LALFree(pPrec->alpha_params);
1783 LALFree(pPrec->beta_params);
1784
1785 gsl_spline_free(pPrec->alpha_spline);
1786 gsl_spline_free(pPrec->cosbeta_spline);
1787 gsl_spline_free(pPrec->gamma_spline);
1788
1789 gsl_interp_accel_free(pPrec->alpha_acc);
1790 gsl_interp_accel_free(pPrec->gamma_acc);
1791 gsl_interp_accel_free(pPrec->cosbeta_acc);
1792
1793 }
1794
1795
1796 #if DEBUG == 1
1797 printf("\n******Leaving IMRPhenomXPHM_hplushcross_from_modes*****\n");
1798 #endif
1799
1800 return XLAL_SUCCESS;
1801}
1802
1803
1804/*
1805 Core twisting up routine to get hptilde and hctilde.
1806 Twist one h_lmprime waveform in the precessing L-frame to the inertial J-frame for one frequency point
1807 as described in section III of Precessing paper.
1808 The explicit formula used implemented in this function correspond to eqs. E18, E19 in Precessing paper.
1809 This function is used inside a loop over frequencies inside a loop over mprime >0 up to l.
1810*/
1812 const REAL8 Mf, /**< Frequency (Hz) */
1813 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.*/
1814 IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
1815 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
1816 INT4 l, /**< First index of the non-precessing (l,mprime) mode */
1817 INT4 mprime, /**< Second index of the non-precessing (l,mprime) mode */
1818 COMPLEX16 *hp, /**< [out] h_+ polarization \f$\tilde h_+\f$ */
1819 COMPLEX16 *hc /**< [out] h_x polarization \f$\tilde h_x\f$ */
1820)
1821{
1822 XLAL_CHECK(hp != NULL, XLAL_EFAULT);
1823 XLAL_CHECK(hc != NULL, XLAL_EFAULT);
1824
1825 /* Euler angles */
1826 double alpha = 0.0;
1827 double epsilon = 0.0;
1828
1829 double cBetah = 0.0;
1830 double sBetah = 0.0;
1831
1832 COMPLEX16 cexp_i_alpha, cexp_i_epsilon = 1;
1833
1834 if(pPrec->MBandPrecVersion == 0) /* No multibanding for angles */
1835 {
1837 {
1838 alpha = pPrec->alphaPNR - pPrec->alpha_offset;
1839 epsilon = -1.0 * pPrec->gammaPNR - pPrec->epsilon_offset;
1840 REAL8 cos_beta = cos(pPrec->betaPNR);
1841
1842 INT4 status = 0;
1843 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1844 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1845 }
1846 else
1847 {
1848
1849 switch(pPrec->IMRPhenomXPrecVersion)
1850 {
1851 case 101: /* Post-Newtonian Euler angles. Single spin approximantion. See sections IV-B and IV-C in Precessing paper. */
1852 case 102: /* The different number 10i means different PN order. */
1853 case 103:
1854 case 104:
1855 {
1856 Get_alpha_beta_epsilon(&alpha, &cBetah, &sBetah, &epsilon, mprime, Mf, pPrec, pWF);
1857 break;
1858 }
1859 case 220: /* Use MSA angles. See section IV-D in Precessing paper. */
1860 case 221:
1861 case 222:
1862 case 223:
1863 case 224:
1864 {
1865 /* Get Euler angles. */
1866 const double v = cbrt (LAL_PI * Mf * (2.0 / mprime) );
1867 const vector vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWF,pPrec);
1868 double cos_beta = 0.0;
1869
1870 /* Get the offset for the Euler angles alpha and epsilon. */
1871 REAL8 alpha_offset_mprime = 0, epsilon_offset_mprime = 0;
1872 Get_alpha_epsilon_offset(&alpha_offset_mprime, &epsilon_offset_mprime, mprime, pPrec);
1873
1874 alpha = vangles.x - alpha_offset_mprime;
1875 epsilon = vangles.y - epsilon_offset_mprime;
1876 cos_beta = vangles.z;
1877
1878 INT4 status = 0;
1879 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1880 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1881
1882 break;
1883 }
1884
1885 case 310:
1886 case 311:
1887 case 320:
1888 case 321:
1889 case 330:
1890
1891 {
1892 /* Get Euler angles. */
1893 int success = XLAL_SUCCESS;
1894 REAL8 Mfprime = Mf * (2.0 / mprime);
1895 REAL8 cos_beta=0., gamma=0., alpha_i=0.;
1896
1897
1898 if(Mfprime< pPrec->ftrans_MRD)
1899 {
1900 success = success + gsl_spline_eval_e(pPrec->cosbeta_spline, Mfprime, pPrec->cosbeta_acc,&cos_beta);
1901 success = success + gsl_spline_eval_e(pPrec->gamma_spline, Mfprime, pPrec->gamma_acc, &gamma);
1902 success = success + gsl_spline_eval_e(pPrec->alpha_spline, Mfprime, pPrec->alpha_acc,&alpha_i);
1903
1904 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);
1905
1906 }
1907
1908
1909
1910 else {
1911
1912 if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321)
1913 {
1914 alpha_i=alphaMRD(Mfprime,pPrec->alpha_params);
1915 cos_beta=cos(betaMRD(Mfprime,pWF,pPrec->beta_params));
1916 success = gsl_spline_eval_e(pPrec->gamma_spline, Mfprime, pPrec->gamma_acc, &gamma);
1917
1918 if(success!=XLAL_SUCCESS)
1919 {
1920 REAL8 deltagamma=0.;
1921 success = gamma_from_alpha_cosbeta(&deltagamma, Mfprime, pWF->deltaMF*2./mprime,pWF,pPrec);
1922 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);
1923 gamma =pPrec->gamma_in+deltagamma;
1924 }
1925
1926 }
1927 else{
1928 // just repeat the last cached value for the Euler angles
1929 alpha_i = pPrec->alpha_ftrans;
1930 cos_beta = pPrec->cosbeta_ftrans;
1931 gamma = pPrec->gamma_ftrans;
1932
1933
1934 }
1935 }
1936
1937
1938 pPrec->gamma_in = gamma;
1939
1940 REAL8 alpha_offset_mprime = pPrec->alpha_ref- pPrec->alpha0;
1941 REAL8 epsilon_offset_mprime = -pPrec->gamma_ref-pPrec->epsilon0;
1942
1943 alpha = alpha_i - alpha_offset_mprime;
1944 epsilon = -gamma - epsilon_offset_mprime;
1945
1946 INT4 status = 0;
1947 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
1948 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
1949 break;
1950 }
1951
1952
1953
1954 default:
1955 {
1956 XLAL_ERROR(XLAL_EINVAL,"Error. IMRPhenomXPrecVersion not recognized. Recommended default is 223.\n");
1957 break;
1958 }
1959 }
1960 }
1961 cexp_i_alpha = cexp(+I*alpha);
1962
1963 #if DEBUG == 1
1964 // Used for writing angles to file in debug mode.
1965 cexp_i_epsilon = cexp(+I*epsilon);
1966 pPrec->cexp_i_betah = cBetah + I*sBetah;
1967 #endif
1968
1969 } // End of no multibanding
1970 else{ /* For Multibanding */
1971 cexp_i_alpha = pPrec->cexp_i_alpha;
1972 cexp_i_epsilon = pPrec->cexp_i_epsilon;
1973 cBetah = (pPrec->cexp_i_betah + 1./pPrec->cexp_i_betah)*0.5;
1974 sBetah = (pPrec->cexp_i_betah - 1./pPrec->cexp_i_betah)*0.5/I;
1975 } // End of Multibanding-specific
1976
1977 /* Useful powers of the Wigner coefficients */
1978 REAL8 cBetah2 = cBetah * cBetah;
1979 REAL8 cBetah3 = cBetah * cBetah2;
1980 REAL8 cBetah4 = cBetah * cBetah3;
1981 REAL8 cBetah5 = cBetah * cBetah4;
1982 REAL8 cBetah6 = cBetah * cBetah5;
1983 REAL8 cBetah7 = cBetah * cBetah6;
1984 REAL8 cBetah8 = cBetah * cBetah7;
1985
1986 REAL8 sBetah2 = sBetah * sBetah;
1987 REAL8 sBetah3 = sBetah * sBetah2;
1988 REAL8 sBetah4 = sBetah * sBetah3;
1989 REAL8 sBetah5 = sBetah * sBetah4;
1990 REAL8 sBetah6 = sBetah * sBetah5;
1991 REAL8 sBetah7 = sBetah * sBetah6;
1992 REAL8 sBetah8 = sBetah * sBetah7;
1993
1994 /*
1995 The following expressions for the Wigner-d coefficients correspond to those in appendix A of the Precessing paper.
1996 They are the same expressions used in IMRPhenomXPHMTwistUpOneMode.
1997 */
1998
1999 COMPLEX16 hp_sum = 0;
2000 COMPLEX16 hc_sum = 0;
2001
2002 /* Sum over l = 2 modes */
2003 if (l == 2 && mprime == 2){
2004 /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2005 /* Precompute powers of e^{i m alpha} */
2006 COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2007
2008 COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2009 COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2010
2011 COMPLEX16 cexp_im_alpha_l2[5] = {cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha};
2012
2013 COMPLEX16 Y2mA[5] = {pPrec->Y2m2, pPrec->Y2m1, pPrec->Y20, pPrec->Y21, pPrec->Y22};
2014
2015 // d^2_{-2,2} d^2_{-1,2} d^2_{0,2} d^2_{1,2} d^2_{2,2}
2016 COMPLEX16 d22[5] = {sBetah4, 2.0*cBetah*sBetah3, pPrec->sqrt6*sBetah2*cBetah2, 2.0*cBetah3*sBetah, cBetah4};
2017 // d^2_{-2,-2} d^2_{-1,-2} d^2_{0,-2} d^2_{1,-2} d^2_{2,-2}
2018 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 */
2019
2020 REAL8 polarizationSymmetry = pPrec->PolarizationSymmetry;
2021
2022 for(int m=-2; m<=2; m++)
2023 {
2024 /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper */
2025 COMPLEX16 A2m2emm = cexp_im_alpha_l2[-m+2] * d2m2[m+2] * Y2mA[m+2];
2026 COMPLEX16 A22emmstar = cexp_im_alpha_l2[m+2] * d22[m+2] * conj(Y2mA[m+2]);
2027
2028 hp_sum += A2m2emm + polarizationSymmetry * A22emmstar;
2029 hc_sum += I*(A2m2emm - polarizationSymmetry * A22emmstar);
2030 }
2031 }
2032
2033 if (l == 2 && mprime == 1){
2034 /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2035 /* Precompute powers of e^{i m alpha} */
2036 COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2037
2038 COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2039 COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2040
2041 COMPLEX16 cexp_im_alpha_l2[5] = {cexp_m2i_alpha, cexp_mi_alpha, 1.0, cexp_i_alpha, cexp_2i_alpha};
2042
2043 COMPLEX16 Y2mA[5] = {pPrec->Y2m2, pPrec->Y2m1, pPrec->Y20, pPrec->Y21, pPrec->Y22};
2044
2045 // d^2_{-2,1} d^2_{-1,1} d^2_{0,1} d^2_{1,1} d^2_{2,1}
2046 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};
2047 // d^2_{-2,-1} d^2_{-1,-1} d^2_{0,-1} d^2_{1,-1} d^2_{2,-1}
2048 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. */
2049
2050
2051 for(int m=-2; m<=2; m++)
2052 {
2053 /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper. */
2054 COMPLEX16 A2m1emm = cexp_im_alpha_l2[-m+2] * d2m1[m+2] * Y2mA[m+2];
2055 COMPLEX16 A21emmstar = cexp_im_alpha_l2[m+2] * d21[m+2] * conj(Y2mA[m+2]);
2056 hp_sum += A2m1emm + A21emmstar;
2057 hc_sum += I*(A2m1emm - A21emmstar);
2058 }
2059
2060 }
2061
2062 /* Sum over l = 3 modes */
2063 if (l == 3 && mprime == 3){
2064 /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2065 /* Precompute powers of e^{i m alpha} */
2066 COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2067 COMPLEX16 cexp_3i_alpha = cexp_i_alpha * cexp_2i_alpha;
2068
2069 COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2070 COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2071 COMPLEX16 cexp_m3i_alpha = cexp_mi_alpha * cexp_m2i_alpha;
2072
2073 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};
2074
2075 COMPLEX16 Y3mA[7] = {pPrec->Y3m3, pPrec->Y3m2, pPrec->Y3m1, pPrec->Y30, pPrec->Y31, pPrec->Y32, pPrec->Y33};
2076
2077 // 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}
2078 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};
2079 // 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}
2080 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. */
2081
2082 for(int m=-3; m<=3; m++)
2083 {
2084 /* Transfer functions */
2085 COMPLEX16 A3m3emm = cexp_im_alpha_l3[-m+3] * d3m3[m+3] * Y3mA[m+3];
2086 COMPLEX16 A33emmstar = cexp_im_alpha_l3[m+3] * d33[m+3] * conj(Y3mA[m+3]);
2087 hp_sum += A3m3emm - A33emmstar;
2088 hc_sum += I*(A3m3emm + A33emmstar);
2089 }
2090 }
2091
2092 /* Sum over l = 3 modes */
2093 if (l == 3 && mprime == 2){
2094 /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2095 /* Precompute powers of e^{i m alpha} */
2096 COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2097 COMPLEX16 cexp_3i_alpha = cexp_i_alpha * cexp_2i_alpha;
2098
2099 COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2100 COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2101 COMPLEX16 cexp_m3i_alpha = cexp_mi_alpha * cexp_m2i_alpha;
2102
2103 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};
2104
2105 COMPLEX16 Y3mA[7] = {pPrec->Y3m3, pPrec->Y3m2, pPrec->Y3m1, pPrec->Y30, pPrec->Y31, pPrec->Y32, pPrec->Y33};
2106
2107 // 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}
2108 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};
2109 // 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}
2110 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. */
2111
2112
2113 for(int m=-3; m<=3; m++)
2114 {
2115 /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper. */
2116 COMPLEX16 A3m2emm = cexp_im_alpha_l3[-m+3] * d3m2[m+3] * Y3mA[m+3];
2117 COMPLEX16 A32emmstar = cexp_im_alpha_l3[m+3] * d32[m+3] * conj(Y3mA[m+3]);
2118 hp_sum += A3m2emm - A32emmstar;
2119 hc_sum += I*(A3m2emm + A32emmstar);
2120 }
2121
2122 }
2123
2124 /* Sum over l = 4 modes */
2125 if (l == 4 && mprime == 4){
2126 /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2127 /* Precompute powers of e^{i m alpha} */
2128 COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2129 COMPLEX16 cexp_3i_alpha = cexp_i_alpha * cexp_2i_alpha;
2130 COMPLEX16 cexp_4i_alpha = cexp_i_alpha * cexp_3i_alpha;
2131
2132 COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2133 COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2134 COMPLEX16 cexp_m3i_alpha = cexp_mi_alpha * cexp_m2i_alpha;
2135 COMPLEX16 cexp_m4i_alpha = cexp_mi_alpha * cexp_m3i_alpha;
2136
2137 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};
2138
2139 COMPLEX16 Y4mA[9] = {pPrec->Y4m4, pPrec->Y4m3, pPrec->Y4m2, pPrec->Y4m1, pPrec->Y40, pPrec->Y41, pPrec->Y42, pPrec->Y43, pPrec->Y44};
2140
2141 // 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}
2142 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};
2143 // 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}
2144 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. */
2145
2146 for(int m=-4; m<=4; m++)
2147 {
2148 /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper. */
2149 COMPLEX16 A4m4emm = cexp_im_alpha_l4[-m+4] * d4m4[m+4] * Y4mA[m+4];
2150 COMPLEX16 A44emmstar = cexp_im_alpha_l4[m+4] * d44[m+4] * conj(Y4mA[m+4]);
2151 hp_sum += A4m4emm + A44emmstar;
2152 hc_sum += I*(A4m4emm - A44emmstar);
2153 }
2154 }
2155
2156 /* Sum over l = 4 modes. This is only used when twisting PhenomHM. */
2157 if (l == 4 && mprime == 3){
2158 /* Sum up contributions to \tilde{h}_+ and \tilde{h}_x */
2159 /* Precompute powers of e^{i m alpha} */
2160 COMPLEX16 cexp_2i_alpha = cexp_i_alpha * cexp_i_alpha;
2161 COMPLEX16 cexp_3i_alpha = cexp_i_alpha * cexp_2i_alpha;
2162 COMPLEX16 cexp_4i_alpha = cexp_i_alpha * cexp_3i_alpha;
2163
2164 COMPLEX16 cexp_mi_alpha = 1.0 / cexp_i_alpha;
2165 COMPLEX16 cexp_m2i_alpha = cexp_mi_alpha * cexp_mi_alpha;
2166 COMPLEX16 cexp_m3i_alpha = cexp_mi_alpha * cexp_m2i_alpha;
2167 COMPLEX16 cexp_m4i_alpha = cexp_mi_alpha * cexp_m3i_alpha;
2168
2169 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};
2170
2171 COMPLEX16 Y4mA[9] = {pPrec->Y4m4, pPrec->Y4m3, pPrec->Y4m2, pPrec->Y4m1, pPrec->Y40, pPrec->Y41, pPrec->Y42, pPrec->Y43, pPrec->Y44};
2172
2173 // 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}
2174 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};
2175 // 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}
2176 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. */
2177
2178 for(int m=-4; m<=4; m++)
2179 {
2180 /* Transfer functions, see eqs. 3.5-3.7 in Precessing paper. */
2181 COMPLEX16 A4m3emm = cexp_im_alpha_l4[-m+4] * d4m3[m+4] * Y4mA[m+4];
2182 COMPLEX16 A43emmstar = cexp_im_alpha_l4[m+4] * d43[m+4] * conj(Y4mA[m+4]);
2183 hp_sum += A4m3emm + A43emmstar;
2184 hc_sum += I*(A4m3emm - A43emmstar);
2185 }
2186 }
2187
2188 COMPLEX16 eps_phase_hP_lmprime;
2189
2190 if(pPrec->MBandPrecVersion == 0) // No multibanding
2191 {
2192 eps_phase_hP_lmprime = cexp(-1.*mprime*I*epsilon) * hlmprime / 2.0;
2193 }
2194 else{ // With multibanding
2195 COMPLEX16 exp_imprime_epsilon = cexp_i_epsilon;
2196 for(INT4 i=1; i<mprime; i++)
2197 {
2198 exp_imprime_epsilon *= cexp_i_epsilon;
2199 }
2200 eps_phase_hP_lmprime = 1./exp_imprime_epsilon * hlmprime / 2.0;
2201 }
2202
2203
2204 /* Return h_+ and h_x */
2205 *hp += eps_phase_hP_lmprime * hp_sum;
2206 *hc += eps_phase_hP_lmprime * hc_sum;
2207
2208 #if DEBUG == 1
2209 /* Save angles in output file. */
2210 FILE *fileangle;
2211 char fileSpec[40];
2212
2213 if(pPrec->MBandPrecVersion == 0)
2214 {
2215 sprintf(fileSpec, "angles_hphc_%i%i.dat", l, mprime);
2216 }
2217 else
2218 {
2219 sprintf(fileSpec, "angles_hphc_MB_%i%i.dat", l, mprime);
2220 }
2221 fileangle = fopen(fileSpec,"a");
2222
2223 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));
2224 fclose(fileangle);
2225 #endif
2226
2227 return XLAL_SUCCESS;
2228}
2229
2230/* @} */
2231/* @} */
2232
2233/** @addtogroup LALSimIMRPhenomX_c
2234* @{
2235* @name Routines for IMRPhenomXPHM
2236* @{
2237* @author Cecilio García Quirós, Geraint Pratten
2238*
2239* @brief C code for IMRPhenomXPHM phenomenological waveform model.
2240*
2241* This is a frequency domain precessing model based on the twisting-up of the aligned spin model with higher modes IMRPhenomXHM.
2242* See G.Pratten et al arXiv:2004.06503 for details. Any studies that use this waveform model should include
2243* a reference to this paper.
2244*
2245* @note DCC link to the paper: https://dcc.ligo.org/LIGO-P2000039. This paper will be refered in the code as the "Precessing paper".
2246*
2247* Waveform flags:
2248*
2249* All the flags for IMRPhenomXP apply here plus the following ones:
2250*
2251* TwistPhenomHM: option to twist-up the AS model PhenomHM instead of PhenomXHM. It is only available for the polarizations, not for individual modes.
2252* - 0: (DEFAULT) twist-up PhenomXHM
2253* - 1: twist-up PhenomHM
2254*
2255* UseModes: Determine how the polarizations hp, hc are computed.
2256* - 0: (DEFAULT) Compute the non-precessing modes once and do the twistin up as in eq. 3.5-3.7 in the Precessing paper.
2257* - 1: Compute first the individual precessing modes in the inertial J-frame and sum them to get the polarizations.
2258*
2259* ModesL0Frame: Determine in which frame the individual precessing modes are returned.
2260* - 0: inertial J-frame (DEFAULT).
2261* - 1: inertial L0-frame (only working near the aligned spin limit).
2262*
2263* PrecModes: Determine which indiviual modes are returned, the non-precessing or the precessing.
2264* - 0: (DEFAULT) Return the precessing individual mode in the J-frame.
2265* - 1: Return the non-precessing individual mode before the twisting-up with the modified final spin.
2266*
2267* Multibanding flags:
2268*
2269* 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.
2270* - 0.001 (DEFAULT)
2271* - 0: Switch off the multibanding.
2272*
2273* MBandPrecVersion: Determines the algorithm to build the non-uniform frequency grid for the Euler angles.
2274* - 0: (DEFAULT) Not use multibanding. Activated to 1 when PrecThresholdMband is non-zero. Note that the default for PrecThresholdMband is not zero, so this will turn on by default.
2275* - 1: Use the same grid that for the non-precessing modes. Activated when PrecThresholdMband is non-zero.
2276* - 2: This enables the conditionalPrecMBand flag defined in LALSimIMRPhenomX_precession.c, where the precession multibanding is disabled when q>7 and the opening angle dramatically closes near merger.
2277**/
2278
2279
2280
2281/*********************************************/
2282/* */
2283/* SINGLE-MODE PRECESSING FUNCTIONS */
2284/* */
2285/*********************************************/
2286
2287/**
2288 Function to compute one hlm precessing mode in an uniform frequency grid.
2289 By default the mode is given in the inertial J-frame. It can be transformed to the L0-frame with the option "PhenomXPHMModesL0Frame" which
2290 currently only works for cases near AS limit.
2291 It can return the co-precessing mode with the option "PhenomXPHMPrecModes": this corresponds to the XHM mode with the modified
2292 ringdown/damping frequencies of the precessing final spin. In this case only m<0 are supported, so hlmneg will be filled with zeros.
2293 Returns two frequency series, one for the positive frequencies and other for the negative frequencies since, as opposite to the
2294 aligned spin case, in the precessing case all the modes have support in the whole frequency regime.
2295 This is a wrapper of the internal core function that actually does the calculation IMRPhenomXPHM_OneMode.
2296*/
2298 COMPLEX16FrequencySeries **hlmpos, /**< [out] Frequency-domain waveform hlm inertial frame positive frequencies */
2299 COMPLEX16FrequencySeries **hlmneg, /**< [out] Frequency-domain waveform hlm inertial frame negative frequencies */
2300 const UINT4 l, /**< First index of the (l,m) precessing mode */
2301 const INT4 m, /**< Second index of the (l,m) precessing mode */
2302 REAL8 m1_SI, /**< mass of companion 1 (kg) */
2303 REAL8 m2_SI, /**< mass of companion 2 (kg) */
2304 REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2305 REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2306 REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2307 REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2308 REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2309 REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2310 const REAL8 distance, /**< distance of source (m) */
2311 const REAL8 inclination, /**< inclination of source (rad) */
2312 const REAL8 phiRef, /**< reference orbital phase (rad) */
2313 const REAL8 deltaF, /**< Sampling frequency (Hz) */
2314 const REAL8 f_min, /**< Starting GW frequency (Hz) */
2315 const REAL8 f_max, /**< End frequency; 0 defaults to ringdown cutoff freq */
2316 const REAL8 fRef_In, /**< Reference frequency */
2317 LALDict *lalParams /**<LAL Dictionary */
2318)
2319{
2320 /* Variable to check correct calls to functions. */
2321 INT4 status;
2322
2323 /* Check if m1 > m2, swap the bodies otherwise. */
2324 status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2325 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2326
2327 #if DEBUG == 1
2328 printf("fRef_In : %e\n",fRef_In);
2329 printf("m1_SI : %e\n",m1_SI);
2330 printf("m2_SI : %e\n",m2_SI);
2331 printf("chi1_l : %e\n",chi1z);
2332 printf("chi2_l : %e\n",chi2z);
2333 printf("phiRef : %e\n",phiRef);
2334 printf("Prec V. : %d\n\n",XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams));
2335 printf("Performing sanity checks...\n");
2336 #endif
2337
2338 /* Perform initial sanity checks */
2339 XLAL_CHECK(NULL != hlmpos, XLAL_EFAULT, "Error: hlmpos already defined. \n");
2340 XLAL_CHECK(NULL != hlmneg, XLAL_EFAULT, "Error: hlmneg already defined. \n");
2341 XLAL_CHECK(fRef_In >= 0, XLAL_EFUNC, "Error: fRef_In must be positive or set to 0 to ignore. \n");
2342 XLAL_CHECK(deltaF > 0, XLAL_EFUNC, "Error: deltaF must be positive and greater than 0. \n");
2343 XLAL_CHECK(m1_SI > 0, XLAL_EFUNC, "Error: m1 must be positive and greater than 0. \n");
2344 XLAL_CHECK(m2_SI > 0, XLAL_EFUNC, "Error: m2 must be positive and greater than 0. \n");
2345 XLAL_CHECK(f_min > 0, XLAL_EFUNC, "Error: f_min must be positive and greater than 0. \n");
2346 XLAL_CHECK(f_max >= 0, XLAL_EFUNC, "Error: f_max must be non-negative. \n");
2347 XLAL_CHECK(distance > 0, XLAL_EFUNC, "Error: Distance must be positive and greater than 0. \n");
2348
2349 /*
2350 Perform a basic sanity check on the region of the parameter space in which model is evaluated.
2351 Behaviour is as follows, consistent with the choices for IMRPhenomXAS/IMRPhenomXHM
2352 - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
2353 - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
2354 - For mass ratios > 1000: throw a hard error that model is not valid.
2355 - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
2356
2357 */
2358 REAL8 mass_ratio;
2359 if(m1_SI > m2_SI)
2360 {
2361 mass_ratio = m1_SI / m2_SI;
2362 }
2363 else
2364 {
2365 mass_ratio = m2_SI / m1_SI;
2366 }
2367 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"); }
2368 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
2369 if(fabs(chi1z) > 0.99 || fabs(chi2z) > 0.99) { XLAL_PRINT_WARNING("Warning: Extrapolating to extremal spins, model is not trusted.\n"); }
2370
2371 /* If no reference frequency is given, set it to the starting gravitational wave frequency. */
2372 REAL8 fRef = (fRef_In == 0.0) ? f_min : fRef_In;
2373
2374 /* Use an auxiliar laldict to not overwrite the input argument */
2375 LALDict *lalParams_aux;
2376 /* setup mode array */
2377 if (lalParams == NULL)
2378 {
2379 lalParams_aux = XLALCreateDict();
2380 }
2381 else{
2382 lalParams_aux = XLALDictDuplicate(lalParams);
2383 }
2384 /* Check that the modes chosen are available for the model */
2385 XLAL_CHECK(check_input_mode_array(lalParams_aux) == XLAL_SUCCESS, XLAL_EFAULT, "Not available mode chosen.\n");
2386
2387 #if DEBUG == 1
2388 printf("\n\n **** Initializing waveform struct... **** \n\n");
2389 #endif
2390
2391 /* Initialize the useful powers of LAL_PI */
2393 XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
2394
2395 /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly. */
2396 /* We pass inclination 0 since for the individual modes is not relevant. */
2398 pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2399 status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1z, chi2z, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, PHENOMXDEBUG);
2400 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2401
2402 /*
2403 Create a REAL8 frequency series.
2404 Use fLow, fHigh, deltaF to compute frequency sequence. Only pass the boundaries (fMin, fMax).
2405 */
2407 freqs->data[0] = pWF->fMin;
2408 freqs->data[1] = pWF->f_max_prime;
2409
2410
2411 #if DEBUG == 1
2412 printf("\n\n **** Initializing precession struct... **** \n\n");
2413 #endif
2414
2415 /* Initialize IMRPhenomX Precession struct and check that it generated successfully. */
2417 pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2418
2419 // only relevant for SpinTaylor angles
2421 if(pflag==310||pflag==311||pflag==320||pflag==321||pflag==330){
2422
2423 pPrec->M_MIN = MAX(1,abs(m)), pPrec->M_MAX = l;
2424 pPrec->L_MAX_PNR = l;
2425
2426 }
2427
2428
2430 pWF,
2431 pPrec,
2432 m1_SI,
2433 m2_SI,
2434 chi1x,
2435 chi1y,
2436 chi1z,
2437 chi2x,
2438 chi2y,
2439 chi2z,
2440 lalParams_aux,
2442 );
2443 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2444
2445 if(pPrec->precessing_tag==3){
2446
2447 status=IMRPhenomX_Initialize_Euler_Angles(pWF,pPrec,lalParams);
2448 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_Initialize_Euler_Angles failed.\n");
2449 }
2450
2451 /* Ensure recovering AS limit when modes are in the L0 frame. */
2453 {
2454 XLAL_PRINT_WARNING("The L0Frame option only works near the AS limit, it should not be used otherwise.");
2456 {
2457 case 0:
2458 case 5:
2459 //pWF->phi0 = pPrec->phi0_aligned;
2460 break;
2461 case 1:
2462 case 6:
2463 case 7:
2464 {
2465 //pWF->phi0 = pPrec->epsilon0 - pPrec->alpha0 + phiRef;
2466 pWF->phi0 = phiRef;
2467 break;
2468 }
2469 }
2470 }
2471
2472
2473 #if DEBUG == 1
2474 printf("\n\n **** Calling IMRPhenomXPHM_OneMode... **** \n\n");
2475 #endif
2476
2477 /* We now call the core IMRPhenomXPHM_OneMode waveform generator */
2478 status = IMRPhenomXPHM_OneMode(hlmpos, hlmneg, freqs, pWF, pPrec, l, m, lalParams_aux);
2479 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_OneMode failed to generate IMRPhenomXHM waveform.");
2480
2481 /* Tranform modes to L0-frame if requested. It only works for (near) AS cases. */
2483 {
2485 {
2486 case 0:
2487 case 5:
2488 //pWF->phi0 = pPrec->phi0_aligned;
2489 break;
2490 case 1:
2491 case 6:
2492 case 7:
2493 {
2494 COMPLEX16 shiftpos = cexp( abs(m)*I*( pPrec->epsilon0 - pPrec->alpha0) );
2495 COMPLEX16 shiftneg = 1./shiftpos;
2496
2497 for(UINT4 i = 0; i<(*hlmpos)->data->length; i++)
2498 {
2499 (*hlmpos)->data->data[i] *= shiftpos;
2500 (*hlmneg)->data->data[i] *= shiftneg;
2501 }
2502 break;
2503 }
2504 }
2505 }
2506
2507 #if DEBUG == 1
2508 printf("\n\n **** Call to IMRPhenomXPHM_OneMode complete. **** \n\n");
2509 #endif
2510
2511 /* Resize hptilde, hctilde */
2512 REAL8 lastfreq;
2513 if (pWF->f_max_prime < pWF->fMax)
2514 {
2515 /* The user has requested a higher f_max than Mf = fCut.
2516 Resize the frequency series to fill with zeros beyond the cutoff frequency. */
2517 lastfreq = pWF->fMax;
2518 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);
2519 }
2520 else{ // We have to look for a power of 2 anyway.
2521 lastfreq = pWF->f_max_prime;
2522 }
2523 // We want to have the length be a power of 2 + 1
2524 size_t n_full = NextPow2(lastfreq / deltaF) + 1;
2525 size_t n = (*hlmpos)->data->length;
2526
2527 /* Resize the COMPLEX16 frequency series */
2528 *hlmpos = XLALResizeCOMPLEX16FrequencySeries(*hlmpos, 0, n_full);
2529 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 );
2530
2531 /* Resize the COMPLEX16 frequency series */
2532 *hlmneg = XLALResizeCOMPLEX16FrequencySeries(*hlmneg, 0, n_full);
2533 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 );
2534
2535
2536 if(pPrec->precessing_tag==3)
2537 {
2538 LALFree(pPrec->alpha_params);
2539 LALFree(pPrec->beta_params);
2540
2541 gsl_spline_free(pPrec->alpha_spline);
2542 gsl_spline_free(pPrec->cosbeta_spline);
2543 gsl_spline_free(pPrec->gamma_spline);
2544
2545 gsl_interp_accel_free(pPrec->alpha_acc);
2546 gsl_interp_accel_free(pPrec->gamma_acc);
2547 gsl_interp_accel_free(pPrec->cosbeta_acc);
2548 }
2549
2550
2551 /* Free memory */
2552 LALFree(pWF);
2553 LALFree(pPrec);
2555 XLALDestroyDict(lalParams_aux);
2556
2557
2558
2559 return XLAL_SUCCESS;
2560}
2561
2562/**
2563 Function to compute one hlm precessing mode on a custom frequency grid.
2564 Equivalent options and behaviour to that of XLALSimIMRPhenomXPHMOneMode.
2565*/
2567 COMPLEX16FrequencySeries **hlmpos, /**< [out] Frequency-domain waveform hlm inertial frame positive frequencies */
2568 COMPLEX16FrequencySeries **hlmneg, /**< [out] Frequency-domain waveform hlm inertial frame negative frequencies */
2569 const REAL8Sequence *freqs, /**< Input Frequency series [Hz] */
2570 const UINT4 l, /**< First index of the (l,m) precessing mode */
2571 const INT4 m, /**< Second index of the (l,m) precessing mode */
2572 REAL8 m1_SI, /**< mass of companion 1 (kg) */
2573 REAL8 m2_SI, /**< mass of companion 2 (kg) */
2574 REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2575 REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2576 REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
2577 REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2578 REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2579 REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
2580 const REAL8 distance, /**< distance of source (m) */
2581 const REAL8 inclination, /**< inclination of source (rad) */
2582 const REAL8 phiRef, /**< reference orbital phase (rad) */
2583 const REAL8 fRef_In, /**< Reference frequency */
2584 LALDict *lalParams /**<LAL Dictionary */
2585)
2586{
2587 /* Variable to check correct calls to functions. */
2588 INT4 status;
2589
2590 /* Check if m1 > m2, swap the bodies otherwise. */
2591 status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2592 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2593
2594 #if DEBUG == 1
2595 printf("fRef_In : %e\n",fRef_In);
2596 printf("m1_SI : %e\n",m1_SI);
2597 printf("m2_SI : %e\n",m2_SI);
2598 printf("chi1_l : %e\n",chi1z);
2599 printf("chi2_l : %e\n",chi2z);
2600 printf("phiRef : %e\n",phiRef);
2601 printf("Prec V. : %d\n\n",XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams));
2602 printf("Performing sanity checks...\n");
2603 #endif
2604
2605 /* Perform initial sanity checks */
2606 XLAL_CHECK(NULL != hlmpos, XLAL_EFAULT, "Error: hlmpos already defined. \n");
2607 XLAL_CHECK(NULL != hlmneg, XLAL_EFAULT, "Error: hlmneg already defined. \n");
2608 XLAL_CHECK(fRef_In >= 0, XLAL_EFUNC, "Error: fRef_In must be positive or set to 0 to ignore. \n");
2609 XLAL_CHECK(m1_SI > 0, XLAL_EFUNC, "Error: m1 must be positive and greater than 0. \n");
2610 XLAL_CHECK(m2_SI > 0, XLAL_EFUNC, "Error: m2 must be positive and greater than 0. \n");
2611 XLAL_CHECK(distance > 0, XLAL_EFUNC, "Error: Distance must be positive and greater than 0. \n");
2612
2613 /* Get minimum and maximum frequencies. */
2614 REAL8 f_min = freqs->data[0];
2615 REAL8 f_max = freqs->data[freqs->length - 1];
2616
2617 /*
2618 Perform a basic sanity check on the region of the parameter space in which model is evaluated.
2619 Behaviour is as follows, consistent with the choices for IMRPhenomXAS/IMRPhenomXHM
2620 - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
2621 - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
2622 - For mass ratios > 1000: throw a hard error that model is not valid.
2623 - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
2624 */
2625 REAL8 mass_ratio;
2626 if(m1_SI > m2_SI)
2627 {
2628 mass_ratio = m1_SI / m2_SI;
2629 }
2630 else
2631 {
2632 mass_ratio = m2_SI / m1_SI;
2633 }
2634 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"); }
2635 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
2636 if(fabs(chi1z) > 0.99 || fabs(chi2z) > 0.99) { XLAL_PRINT_WARNING("Warning: Extrapolating to extremal spins, model is not trusted.\n"); }
2637
2638 /* If no reference frequency is given, set it to the starting gravitational wave frequency. */
2639 REAL8 fRef = (fRef_In == 0.0) ? f_min : fRef_In;
2640
2641 /* Use an auxiliar laldict to not overwrite the input argument */
2642 LALDict *lalParams_aux;
2643 /* setup mode array */
2644 if (lalParams == NULL)
2645 {
2646 lalParams_aux = XLALCreateDict();
2647 }
2648 else{
2649 lalParams_aux = XLALDictDuplicate(lalParams);
2650 }
2651
2652 /* Check that the modes chosen are available for the model */
2653 XLAL_CHECK(check_input_mode_array(lalParams_aux) == XLAL_SUCCESS, XLAL_EFAULT, "Not available mode chosen.\n");
2654
2655 #if DEBUG == 1
2656 printf("\n\n **** Initializing waveform struct... **** \n\n");
2657 #endif
2658
2659 /* Initialize the useful powers of LAL_PI */
2661 XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
2662
2663 /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly. */
2664 /* We pass inclination 0 since for the individual modes is not relevant. */
2666 pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2667 status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1z, chi2z, 0.0, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, PHENOMXDEBUG);
2668 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2669
2670
2671 #if DEBUG == 1
2672 printf("\n\n **** Initializing precession struct... **** \n\n");
2673 #endif
2674
2675 /* Initialize IMRPhenomX Precession struct and check that it generated successfully. */
2677 pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2678
2679 // only relevant for SpinTaylor angles
2681 if(pflag==310||pflag==311||pflag==320||pflag==321||pflag==330){
2682
2683 pPrec->M_MIN = MAX(1,abs(m)), pPrec->M_MAX = l;
2684 pPrec->L_MAX_PNR = l;
2685 }
2686
2688 {
2690 }
2691
2693 pWF,
2694 pPrec,
2695 m1_SI,
2696 m2_SI,
2697 chi1x,
2698 chi1y,
2699 chi1z,
2700 chi2x,
2701 chi2y,
2702 chi2z,
2703 lalParams_aux,
2705 );
2706 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2707
2708 if(pPrec->precessing_tag==3){
2709
2710 status=IMRPhenomX_Initialize_Euler_Angles(pWF,pPrec,lalParams);
2711 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_Initialize_Euler_Angles failed.\n");
2712 }
2713
2714
2715 /* Ensure recovering AS limit when modes are in the L0 frame. */
2717 {
2718 XLAL_PRINT_WARNING("The L0Frame option only works near the AS limit, it should not be used otherwise.");
2720 {
2721 case 0:
2722 case 5:
2723 //pWF->phi0 = pPrec->phi0_aligned;
2724 break;
2725 case 1:
2726 case 6:
2727 case 7:
2728 {
2729 //pWF->phi0 = pPrec->epsilon0 - pPrec->alpha0 + phiRef;
2730 pWF->phi0 = phiRef;
2731 break;
2732 }
2733 }
2734 }
2735
2736 #if DEBUG == 1
2737 printf("\n\n **** Calling IMRPhenomXPHM_OneMode... **** \n\n");
2738 #endif
2739
2740 // Ensure that multibanding is *always* off when calling a custom grid
2742
2743 /* We now call the core IMRPhenomXPHM_OneMode waveform generator */
2744 status = IMRPhenomXPHM_OneMode(hlmpos, hlmneg, freqs, pWF, pPrec, l, m, lalParams_aux);
2745 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_OneMode failed to generate IMRPhenomXHM waveform.");
2746
2747 /* Tranform modes to L0-frame if requested. It only works for (near) AS cases. */
2749 {
2751 {
2752 case 0:
2753 case 5:
2754 //pWF->phi0 = pPrec->phi0_aligned;
2755 break;
2756 case 1:
2757 case 6:
2758 case 7:
2759 {
2760 COMPLEX16 shiftpos = cexp( abs(m)*I*( pPrec->epsilon0 - pPrec->alpha0) );
2761 COMPLEX16 shiftneg = 1./shiftpos;
2762
2763 for(UINT4 i = 0; i<(*hlmpos)->data->length; i++)
2764 {
2765 (*hlmpos)->data->data[i] *= shiftpos;
2766 (*hlmneg)->data->data[i] *= shiftneg;
2767 }
2768 break;
2769 }
2770 }
2771 }
2772
2773 #if DEBUG == 1
2774 printf("\n\n **** Call to IMRPhenomXPHM_OneMode complete. **** \n\n");
2775 #endif
2776
2777 //CHECK ME
2778
2779 if(pPrec->precessing_tag==3)
2780 {
2781 LALFree(pPrec->alpha_params);
2782 LALFree(pPrec->beta_params);
2783
2784 gsl_spline_free(pPrec->alpha_spline);
2785 gsl_spline_free(pPrec->cosbeta_spline);
2786 gsl_spline_free(pPrec->gamma_spline);
2787
2788 gsl_interp_accel_free(pPrec->alpha_acc);
2789 gsl_interp_accel_free(pPrec->gamma_acc);
2790 gsl_interp_accel_free(pPrec->cosbeta_acc);
2791
2792 }
2793
2794 /* Free memory */
2795 LALFree(pWF);
2796 LALFree(pPrec);
2797 XLALDestroyDict(lalParams_aux);
2798
2799 return XLAL_SUCCESS;
2800}
2801/** @}
2802* @} **/
2803
2804
2805/**
2806 Core funciton to compute an individual precessing mode hlm in the inertial J-frame.
2807 Returns two frequency series, one for the positive frequencies and other for the negative frequencies.
2808 It can be evaluated in a non-uniform frequency grid through the argument REAL8Seuqnce *freqs_In. This is in fact done when Calling
2809 XLALSimIMRPhenomXPHMFrequencySequence with the option of 'UseModes' activated.
2810*/
2812 COMPLEX16FrequencySeries **hlmpos, /**< [out] Frequency domain hlm GW strain inertial frame positive frequencies */
2813 COMPLEX16FrequencySeries **hlmneg, /**< [out] Frequency domain hlm GW strain inertial frame negative frequencies */
2814 const REAL8Sequence *freqs_In, /**< Input frequency grid (Hz) */
2815 IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
2816 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
2817 UINT4 ell, /**< l index of the (l,m) precessing mode */
2818 INT4 m, /**< m index of the (l,m) precessing mode */
2819 LALDict *lalParams /**< LAL Dictionary Structure */
2820)
2821{
2822 if (pWF->f_max_prime <= pWF->fMin)
2823 XLAL_ERROR(XLAL_EDOM, "(fCut = %g Hz) <= f_min = %g\n", pWF->f_max_prime, pWF->fMin);
2824
2825 /* Set LIGOTimeGPS */
2826 LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO; // = {0,0}
2827
2828 REAL8 deltaF = pWF->deltaF;
2829
2830 lalParams = IMRPhenomXPHM_setup_mode_array(lalParams);
2831 LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(lalParams);
2832
2833 /* At this point ModeArray should contain the list of modes
2834 and therefore if NULL then something is wrong and abort. */
2835 if (ModeArray == NULL)
2836 {
2837 XLAL_ERROR(XLAL_EDOM, "ModeArray is NULL when it shouldn't be. Aborting.\n");
2838 }
2839
2840 /* Check that the co-precessing ModeArray has at least one ell mode. If not, twisting-up is not possible. */
2841 bool mode_arrays_consistent = false;
2842 INT4 emm = -(INT4)ell;
2843 while (mode_arrays_consistent == false && emm<=(INT4)ell){
2844 if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ell, emm) ==1){
2845 mode_arrays_consistent = true;
2846 }
2847 emm++;
2848 }
2849 if(mode_arrays_consistent == false){
2850 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);
2851 }
2852
2853 INT4 status = 0; //Variable to check correct functions calls.
2854
2855 /* Build the frequency array and initialize hctilde to the length of freqs. */
2856 REAL8Sequence *freqs;
2857 UINT4 offset = SetupWFArrays(&freqs, hlmpos, freqs_In, pWF, ligotimegps_zero);
2858
2859 /* Initialize hlmneg according to hlmpos. */
2860 size_t npts = (*hlmpos)->data->length;
2861 *hlmneg = XLALCreateCOMPLEX16FrequencySeries("hlmneg: FD waveform", &(*hlmpos)->epoch, (*hlmpos)->f0, pWF->deltaF, &lalStrainUnit, npts);
2862 XLAL_CHECK (*hlmneg, XLAL_ENOMEM, "Failed to allocated waveform COMPLEX16FrequencySeries of length %zu.", npts);
2863 memset((*hlmneg)->data->data, 0, npts * sizeof(COMPLEX16));
2864 XLALUnitMultiply(&((*hlmneg)->sampleUnits), &((*hlmneg)->sampleUnits), &lalSecondUnit);
2865
2866 /* Variable to store the strain of only one (negative) mode: h_l-mprime */
2867 COMPLEX16FrequencySeries *htilde22 = NULL;
2868
2869 /*
2870 Take input/default value for the threshold of the Multibanding for the hlms modes.
2871 If = 0 then do not use Multibanding. Default value defined in XLALSimInspiralWaveformParams.c.
2872 If the input freqs_In is non-uniform the Multibanding has been already switched off.
2873 */
2875
2876 /* Initialize the power of pi for the HM internal functions. */
2878 XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
2879
2880 UINT4 n_coprec_modes = 0;
2881
2882 /* Set up code for using PNR tuned angles */
2883 int IMRPhenomXPNRUseTunedAngles = pPrec->IMRPhenomXPNRUseTunedAngles;
2884 int AntisymmetricWaveform = pPrec->IMRPhenomXAntisymmetricWaveform;
2885
2886 IMRPhenomX_PNR_angle_spline *hm_angle_spline = NULL;
2887 REAL8 Mf_RD_22 = pWF->fRING;
2888 REAL8 Mf_RD_lm = 0.0;
2889
2890 if (IMRPhenomXPNRUseTunedAngles){
2891
2892 /* Allocate the spline interpolant struct */
2894 if (!hm_angle_spline)
2895 {
2896 XLAL_ERROR(XLAL_EFUNC, "hm_angle_spline struct allocation failed in LALSimIMRPhenomXPHM.c.");
2897 }
2898
2899 /* Populate interpolant structs for the (2,2) angles */
2900 status = IMRPhenomX_PNR_GeneratePNRAngleInterpolants(hm_angle_spline, pWF, pPrec, lalParams);
2901 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_GeneratePNRAngleInterpolants failed.\n");
2902
2903 /* Here we assign the reference values of alpha and gamma to their values in the precession struct */
2904 /* NOTE: the contribution from pPrec->alpha0 is assigned in IMRPhenomX_PNR_RemapThetaJSF */
2905 pPrec->alpha_offset = gsl_spline_eval(hm_angle_spline->alpha_spline, pWF->fRef, hm_angle_spline->alpha_acc);
2906 /* NOTE: the sign is flipped between gamma and epsilon */
2907 pPrec->epsilon_offset = -gsl_spline_eval(hm_angle_spline->gamma_spline, pWF->fRef, hm_angle_spline->gamma_acc) - pPrec->epsilon0;
2908
2909 /* Remap the J-frame sky location to use beta instead of ThetaJN */
2910 REAL8 betaPNR_ref = gsl_spline_eval(hm_angle_spline->beta_spline, pWF->fRef, hm_angle_spline->beta_acc);
2911 status = IMRPhenomX_PNR_RemapThetaJSF(betaPNR_ref, pWF, pPrec, lalParams);
2912 XLAL_CHECK(
2914 XLAL_EFUNC,
2915 "Error: IMRPhenomX_PNR_RemapThetaJSF failed in IMRPhenomX_PNR_GeneratePNRAngles.");
2916
2917 }
2918
2919 REAL8Sequence *antiSym_amp = NULL;
2920 REAL8Sequence *antiSym_phi = NULL;
2921
2922 if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
2923 {
2924 antiSym_amp = XLALCreateREAL8Sequence(freqs->length);
2925 antiSym_phi = XLALCreateREAL8Sequence(freqs->length);
2926 }
2927
2928
2929 /***** Loop over non-precessing modes ******/
2930 for (UINT4 emmprime = 1; emmprime <= ell; emmprime++)
2931 {
2932 /* Loop over only positive mprime is intentional.
2933 The single mode function returns the negative mode h_l-mprime, and the positive
2934 is added automatically in during the twisting up in IMRPhenomXPHMTwistUpOneMode.
2935 First check if (l,m) mode is 'activated' in the ModeArray.
2936 If activated then generate the mode, else skip this mode.
2937 */
2938 if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ell, emmprime) != 1)
2939 { /* skip mode */
2940 continue;
2941 } /* else: generate mode */
2942
2943 if(XLALSimInspiralWaveformParamsLookupPhenomXPHMPrecModes(lalParams) == 1 && (INT4)emmprime!=-m)
2944 {
2945 continue;
2946 }
2947
2948 n_coprec_modes++;
2949
2950 #if DEBUG == 1
2951 printf("\n*************************************************\n Non-precessing Mode %i%i\n************************************",ell, emmprime);
2952 #endif
2953
2954 /* Variable to store the strain of only one (negative) mode: h_l-mprime */
2955 COMPLEX16FrequencySeries *htildelm = NULL;
2956
2957 /* Compute non-precessing mode */
2958 if (thresholdMB == 0){ // No multibanding
2959 if(ell == 2 && emmprime == 2)
2960 {
2961 status = IMRPhenomXASGenerateFD(&htildelm, freqs_In, pWF, lalParams);
2962 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXASGenerateFD failed to generate IMRPhenomXHM waveform.");
2963 }
2964 else
2965 {
2966 status = IMRPhenomXHMGenerateFDOneMode(&htildelm, freqs_In, pWF, ell, emmprime, lalParams);
2967 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMGenerateFDOneMode failed to generate IMRPhenomXHM waveform.");
2968 }
2969 }
2970 else{ // With multibanding
2971 if(ell==3 && emmprime==2){ // mode with mode-mixing
2972 status = IMRPhenomXHMMultiBandOneModeMixing(&htildelm, htilde22, pWF, ell, emmprime, lalParams);
2973 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMMultiBandOneModeMixing failed to generate IMRPhenomXHM waveform.");
2974 }
2975 else{ // modes without mode-mixing including 22 mode
2976 status = IMRPhenomXHMMultiBandOneMode(&htildelm, pWF, ell, emmprime, lalParams);
2977 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXHMMultiBandOneMode failed to generate IMRPhenomXHM waveform.");
2978 }
2979
2980 /* IMRPhenomXHMMultiBandOneMode* functions set pWF->deltaF=0 internally, we put it back here. */
2981 pWF->deltaF = deltaF;
2982
2983 /* 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.
2984 The 22 mode is always computed first than the 32, we store the 22 mode in the variable htilde22. */
2985 if(ell==2 && emmprime==2 && XLALSimInspiralModeArrayIsModeActive(ModeArray, 3, 2)==1){
2986 htilde22 = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &(ligotimegps_zero), 0.0, pWF->deltaF, &lalStrainUnit, htildelm->data->length);
2987 for(UINT4 idx = 0; idx < htildelm->data->length; idx++){
2988 htilde22->data->data[idx] = htildelm->data->data[idx];
2989 }
2990 }
2991 }
2992
2993 if(ell==2 && emmprime==2)
2994 {
2995 if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
2996 {
2997 IMRPhenomX_PNR_GenerateAntisymmetricWaveform(antiSym_amp,antiSym_phi,freqs,pWF,pPrec,lalParams);
2998 }
2999 }
3000
3001
3002 if (!(htildelm)){ XLAL_ERROR(XLAL_EFUNC);}
3003
3004
3005 /* htildelm is recomputed every time in the loop. Check that it always comes out with the same length */
3006 XLAL_CHECK ( ((*hlmpos)->data->length==htildelm->data->length)
3007 && ((*hlmneg)->data->length==htildelm->data->length),
3009 "Inconsistent lengths between frequency series htildelm (%d), hlmpos (%d) and hlmneg (%d).",
3010 htildelm->data->length, (*hlmpos)->data->length, (*hlmneg)->data->length
3011 );
3012
3013 /* Skip twisting-up if the non-precessing mode is zero. */
3014 if((pWF->q == 1) && (pWF->chi1L == pWF->chi2L) && (emmprime % 2 != 0))
3015 {
3017 continue;
3018 }
3019 /*
3020 TWISTING UP
3021 Transform modes from the precessing L-frame to inertial J-frame.
3022 */
3023
3024 /* Variable to store the non-precessing waveform in one frequency point. */
3025 COMPLEX16 hlmcoprec;
3026 COMPLEX16 hlmcoprec_antiSym =0.0;
3027
3029 {
3030 for (UINT4 idx = 0; idx < freqs->length; idx++)
3031 {
3032 hlmcoprec = htildelm->data->data[idx + offset]; /* Co-precessing waveform */
3033 if(m < 0) (*hlmpos)->data->data[idx + offset] = hlmcoprec; // Positive frequencies. Freqs do 0, df, 2df, ...., fmax
3034 if(m > 0) (*hlmneg)->data->data[idx + offset] = hlmcoprec; // Negative frequencies. Freqs do 0, -df, -2df, ...., -fmax
3035 }
3036 }
3037 else{
3038
3039 /* set PNR variables if needed */
3040 REAL8 Mf_high = 0.0;
3041 REAL8 Mf_low = 0.0;
3042 UINT4 PNRtoggleInspiralScaling = pPrec->PNRInspiralScaling;
3043
3044 // Precompute PNR transition frequencies if needed
3045 if (IMRPhenomXPNRUseTunedAngles){
3046
3047 if((ell==2)&&(emmprime==2))
3048 {
3049 /* the frequency parameters don't matter here */
3050 Mf_RD_lm = 0.0;
3051 }
3052 else
3053 {
3054 /* Get the (l,m) RD frequency */
3055
3056 Mf_RD_lm = IMRPhenomXHM_GenerateRingdownFrequency(ell, emmprime, pWF);
3057
3058 /* Set the frequency interpolation transitions */
3059 status = IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies(&Mf_low, &Mf_high, emmprime, Mf_RD_22, Mf_RD_lm, pPrec);
3060 XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_LinearFrequencyMapTransitionFrequencies failed.\n");
3061 }
3062 }
3063
3064 for (UINT4 idx = 0; idx < freqs->length; idx++)
3065 {
3066 REAL8 Mf = pWF->M_sec * freqs->data[idx];
3067 hlmcoprec = htildelm->data->data[idx + offset]; /* Co-precessing waveform */
3068
3069 /***** construct h(2,2) or h(2,-2) in co-precessing frame with antisymmetric contribution *****/
3070 if(ell == 2 && emmprime == 2 && AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
3071 {
3072 hlmcoprec_antiSym = antiSym_amp->data[idx]*cexp(I*antiSym_phi->data[idx]);
3073 }
3074
3075 COMPLEX16Sequence *hlm;
3077
3078 if(IMRPhenomXPNRUseTunedAngles)
3079 {
3080 REAL8 Mf_mapped = IMRPhenomX_PNR_LinearFrequencyMap(Mf, ell, emmprime, Mf_low, Mf_high, Mf_RD_22, Mf_RD_lm, PNRtoggleInspiralScaling);
3081 REAL8 f_mapped = XLALSimIMRPhenomXUtilsMftoHz(Mf_mapped, pWF->Mtot);
3082
3083 pPrec->alphaPNR = gsl_spline_eval(hm_angle_spline->alpha_spline, f_mapped, hm_angle_spline->alpha_acc);
3084 pPrec->betaPNR = gsl_spline_eval(hm_angle_spline->beta_spline, f_mapped, hm_angle_spline->beta_acc);
3085 pPrec->gammaPNR = gsl_spline_eval(hm_angle_spline->gamma_spline, f_mapped, hm_angle_spline->gamma_acc);
3086 }
3087
3088 // Twist up
3089 IMRPhenomXPHMTwistUpOneMode(Mf, hlmcoprec, hlmcoprec_antiSym, pWF, pPrec, ell, emmprime, m, hlm);
3090
3091
3092 (*hlmpos)->data->data[idx + offset] += hlm->data[0]; // Positive frequencies. Freqs do 0, df, 2df, ...., fmax
3093 (*hlmneg)->data->data[idx + offset] += hlm->data[1]; // Negative frequencies. Freqs do 0, -df, -2df, ...., -fmax
3095 }
3096
3097 if(IMRPhenomXPNRUseTunedAngles)
3098 {
3099 gsl_interp_accel_reset(hm_angle_spline->alpha_acc);
3100 gsl_interp_accel_reset(hm_angle_spline->beta_acc);
3101 gsl_interp_accel_reset(hm_angle_spline->gamma_acc);
3102 }
3103 }
3104
3106
3107 }// End Loop over emmprime
3108 if (IMRPhenomXPNRUseTunedAngles){
3109 gsl_spline_free(hm_angle_spline->alpha_spline);
3110 gsl_spline_free(hm_angle_spline->beta_spline);
3111 gsl_spline_free(hm_angle_spline->gamma_spline);
3112
3113 gsl_interp_accel_free(hm_angle_spline->alpha_acc);
3114 gsl_interp_accel_free(hm_angle_spline->beta_acc);
3115 gsl_interp_accel_free(hm_angle_spline->gamma_acc);
3116
3117 LALFree(hm_angle_spline);
3118 }
3119
3120
3121 if(n_coprec_modes == 0)
3122 {
3123 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);
3124 }
3125
3126 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPHM_OneMode failed to generate IMRPhenomXPHM waveform.");
3127
3128/* Free memory */
3131XLALDestroyValue(ModeArray);
3132
3133if (AntisymmetricWaveform && IMRPhenomXPNRUseTunedAngles)
3134{
3135 XLALDestroyREAL8Sequence(antiSym_amp);
3136 XLALDestroyREAL8Sequence(antiSym_phi);
3137}
3138
3139
3140#if DEBUG == 1
3141printf("\n******Leaving IMRPhenomXPHM_OneMode*****\n");
3142#endif
3143
3144return XLAL_SUCCESS;
3145}
3146
3147
3148/*
3149 Core twisting up routine to get one single precessing mode.
3150 Twist the waveform in the precessing L-frame to the inertial J-frame for one frequency point.
3151 This function will be inside a loop of frequencies insid a loop over the non-precessing modes.
3152 It carries out the operation specified in eqs. E3-E4 in the Precessing paper.
3153*/
3155 const REAL8 Mf, /**< Frequency (Mf geometric units) */
3156 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.*/
3157 const COMPLEX16 hlmprime_antisym, /**< antisymmetric waveform in the co-precessing frame */
3158 IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
3159 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precession Struct */
3160 UINT4 l, /**< l index of the (l,m) (non-)precessing mode */
3161 UINT4 mprime, /**< second index of the (l,mprime) non-precessing mode */
3162 INT4 m, /**< second index of the (l,m) precessing mode */
3163 COMPLEX16Sequence *hlminertial /**< [out] hlm in the inertial J-frame for one frequency point, precessing waveform */
3164)
3165{
3166 XLAL_CHECK(hlminertial != NULL, XLAL_EFAULT);
3167
3168 /* Euler angles */
3169 double alpha = 0.0;
3170 double epsilon = 0.0;
3171
3172 double cBetah = 0.0;
3173 double sBetah = 0.0;
3174
3176 {
3177 alpha = pPrec->alphaPNR - pPrec->alpha_offset;
3178 epsilon = -1.0 * pPrec->gammaPNR - pPrec->epsilon_offset;
3179 REAL8 cos_beta = cos(pPrec->betaPNR);
3180
3181 INT4 status = 0;
3182 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
3183 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
3184 }
3185 else
3186 {
3187 switch(pPrec->IMRPhenomXPrecVersion)
3188 {
3189 case 101: /* Post-Newtonian Euler angles. Single spin approximantion. See sections IV-B and IV-C in Precessing paper. */
3190 case 102: /* The different number 10i means different PN order. */
3191 case 103:
3192 case 104:
3193 {
3194 /* NNLO PN Euler Angles */
3195 Get_alpha_beta_epsilon(&alpha, &cBetah, &sBetah, &epsilon, mprime, Mf, pPrec, pWF);
3196 break;
3197 }
3198 case 220:
3199 case 221:
3200 case 222:
3201 case 223:
3202 case 224:
3203 {
3204 /* ~~~~~ Euler Angles from Chatziioannou et al, PRD 95, 104004, (2017) ~~~~~ */
3205 const double v = cbrt(LAL_PI * Mf * (2.0/mprime) );
3206 const vector vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWF,pPrec);
3207 double cos_beta = 0.0;
3208
3209 REAL8 alpha_offset_mprime = 0, epsilon_offset_mprime = 0;
3210 Get_alpha_epsilon_offset(&alpha_offset_mprime, &epsilon_offset_mprime, mprime, pPrec);
3211
3212 alpha = vangles.x - alpha_offset_mprime;
3213 epsilon = vangles.y - epsilon_offset_mprime;
3214 cos_beta = vangles.z;
3215
3216 INT4 status = 0;
3217 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
3218 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
3219
3220 break;
3221 }
3222 case 310:
3223 case 311:
3224 case 320:
3225 case 321:
3226 case 330:
3227
3228 {
3229 /* Get Euler angles. */
3230 int success = XLAL_SUCCESS;
3231 REAL8 cos_beta=0., gamma=0., alpha_i=0.;
3232
3233 REAL8 alpha_offset_mprime = pPrec->alpha_ref- pPrec->alpha0;
3234 REAL8 epsilon_offset_mprime = -pPrec->gamma_ref-pPrec->epsilon0;
3235
3236 REAL8 Mfprime = Mf * (2.0 / mprime);
3237
3238 if(Mfprime< pPrec->ftrans_MRD)
3239 {
3240 success = success + gsl_spline_eval_e(pPrec->cosbeta_spline, Mfprime, pPrec->cosbeta_acc,&cos_beta);
3241 success = success + gsl_spline_eval_e(pPrec->gamma_spline, Mfprime, pPrec->gamma_acc, &gamma);
3242 success = success + gsl_spline_eval_e(pPrec->alpha_spline, Mfprime, pPrec->alpha_acc,&alpha_i);
3243
3244 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);
3245
3246 pPrec->gamma_in = gamma;
3247
3248 }
3249
3250
3251
3252 else {
3253
3254 if(pPrec->IMRPhenomXPrecVersion==320 || pPrec->IMRPhenomXPrecVersion==321 )
3255 {
3256
3257 alpha_i=alphaMRD(Mfprime,pPrec->alpha_params);
3258 cos_beta=cos(betaMRD(Mfprime,pWF,pPrec->beta_params));
3259 success = gsl_spline_eval_e(pPrec->gamma_spline, Mfprime, pPrec->gamma_acc, &gamma);
3260 if(success!=XLAL_SUCCESS)
3261 {
3262 REAL8 deltagamma=0.;
3263 success = gamma_from_alpha_cosbeta(&deltagamma, Mfprime, pWF->deltaMF*2./mprime,pWF,pPrec);
3264 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);
3265 gamma =pPrec->gamma_in+deltagamma;
3266 }
3267 pPrec->gamma_in = gamma;
3268
3269 }
3270 else{
3271 // just repeat the last cached value for the Euler angles
3272 alpha_i = pPrec->alpha_ftrans;
3273 cos_beta = pPrec->cosbeta_ftrans;
3274 gamma = pPrec->gamma_ftrans;
3275
3276
3277 }
3278 }
3279
3280
3281
3282
3283 alpha = alpha_i- alpha_offset_mprime;
3284 epsilon = -gamma - epsilon_offset_mprime;
3285
3286 INT4 status = 0;
3287 status = IMRPhenomXWignerdCoefficients_cosbeta(&cBetah, &sBetah, cos_beta);
3288 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients_cosbeta failed.");
3289 break;
3290 }
3291
3292 default:
3293 {
3294 XLAL_ERROR(XLAL_EINVAL,"Error. IMRPhenomXPrecVersion not recognized. Recommended default is 223.\n");
3295 break;
3296 }
3297 }
3298 }
3299
3300
3301 /* Useful powers of the Wigner coefficients */
3302 REAL8 cBetah2 = cBetah * cBetah;
3303 REAL8 cBetah3 = cBetah * cBetah2;
3304 REAL8 cBetah4 = cBetah * cBetah3;
3305 REAL8 cBetah5 = cBetah * cBetah4;
3306 REAL8 cBetah6 = cBetah * cBetah5;
3307 REAL8 cBetah7 = cBetah * cBetah6;
3308 REAL8 cBetah8 = cBetah * cBetah7;
3309
3310 REAL8 sBetah2 = sBetah * sBetah;
3311 REAL8 sBetah3 = sBetah * sBetah2;
3312 REAL8 sBetah4 = sBetah * sBetah3;
3313 REAL8 sBetah5 = sBetah * sBetah4;
3314 REAL8 sBetah6 = sBetah * sBetah5;
3315 REAL8 sBetah7 = sBetah * sBetah6;
3316 REAL8 sBetah8 = sBetah * sBetah7;
3317
3318 /*
3319 The following expressions for the Wigner-d coefficients correspond to those in appendix A of the Precessing paper.
3320 They are the same expressions used in IMRPhenomXPHMTwistUp.
3321 */
3322
3323 COMPLEX16 hlm = 0, hlmneg = 0;
3324 INT4 minus1l = 1;
3325
3326 /* Sum over l = 2 modes */
3327 if (l == 2 && mprime == 2){
3328 // d^2_{-2,2} d^2_{-1,2} d^2_{0,2} d^2_{1,2} d^2_{2,2}
3329 COMPLEX16 d22[5] = {sBetah4, 2.0*cBetah*sBetah3, pPrec->sqrt6*sBetah2*cBetah2, 2.0*cBetah3*sBetah, cBetah4};
3330 // d^2_{-2,-2} d^2_{-1,-2} d^2_{0,-2} d^2_{1,-2} d^2_{2,-2}
3331 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 */
3332
3333 /* See eqs. E3-E4 in Precessing paper. */
3334 COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3335 hlm += cexp_im_alpha * d2m2[m+2];
3336 hlmneg += cexp_im_alpha * d22[m+2];
3337 }
3338
3339 /* Case (l, mprime) = (2, 1) */
3340 if (l == 2 && mprime == 1){
3341 // d^2_{-2,1} d^2_{-1,1} d^2_{0,1} d^2_{1,1} d^2_{2,1}
3342 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};
3343 // d^2_{-2,-1} d^2_{-1,-1} d^2_{0,-1} d^2_{1,-1} d^2_{2,-1}
3344 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. */
3345
3346 /* See eqs. E3-E4 in Precessing paper. */
3347 COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3348 hlm += cexp_im_alpha * d2m1[m+2];
3349 hlmneg += cexp_im_alpha * d21[m+2];
3350 }
3351
3352 /* Case (l, mprime) = (3, 3) */
3353 if (l == 3 && mprime == 3){
3354 minus1l = -1;
3355 // 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}
3356 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};
3357 // 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}
3358 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. */
3359
3360 /* See eqs. E3-E4 in Precessing paper. */
3361 COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3362 hlm += cexp_im_alpha * d3m3[m+3];
3363 hlmneg += cexp_im_alpha * d33[m+3];
3364 }
3365
3366 /* Case (l, mprime) = (3, 2) */
3367 if (l == 3 && mprime == 2){
3368 minus1l = -1;
3369 // 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}
3370 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};
3371 // 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}
3372 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. */
3373
3374 /* See eqs. E3-E4 in Precessing paper. */
3375 COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3376 hlm += cexp_im_alpha * d3m2[m+3];
3377 hlmneg += cexp_im_alpha * d32[m+3];
3378 }
3379
3380 /* Case (l, mprime) = (4, 4) */
3381 if (l == 4 && mprime == 4){
3382 // 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}
3383 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};
3384 // 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}
3385 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. */
3386
3387 /* See eqs. E3-E4 in Precessing paper. */
3388 COMPLEX16 cexp_im_alpha = cexp(-1.*I*m*alpha);
3389 hlm += cexp_im_alpha * d4m4[m+4];
3390 hlmneg += cexp_im_alpha * d44[m+4];
3391 }
3392
3393 COMPLEX16 eps_phase_hP_lmprime;
3394 COMPLEX16 eps_phase_hP_lmprime_neg;
3395
3396 /* See eqs. E3-E4 in Precessing paper. */
3397 COMPLEX16 exp_imprime_epsilon = cexp(mprime*I*epsilon);
3398
3399 eps_phase_hP_lmprime = 1./exp_imprime_epsilon * (hlmprime + hlmprime_antisym);
3400 eps_phase_hP_lmprime_neg = exp_imprime_epsilon * minus1l * conj(hlmprime - hlmprime_antisym);
3401
3402 /* Return h_lminertail */
3403 (hlminertial)->data[0] = eps_phase_hP_lmprime * hlm;
3404 (hlminertial)->data[1] = eps_phase_hP_lmprime_neg * hlmneg;
3405
3406
3407 return XLAL_SUCCESS;
3408}
3409
3410/** Function to obtain a SphHarmFrequencySeries with the individual modes h_lm.
3411 By default it returns all the modes available in the model, positive and negatives.
3412 With the mode array option in the LAL dictionary, the user can specify a custom mode array.
3413 The modes are computed in the inertial J-frame, so the mode array option does not refers to
3414 the modes in the co-precessing frame conversely to the functions for the polarizations XLALSimIMRPhenomXPHM.
3415 This function is to be used by ChooseFDModes.
3416*/
3418 SphHarmFrequencySeries **hlms, /**< [out] list with single modes h_lm in the J-frame */
3419 REAL8 m1_SI, /**< mass of companion 1 (kg) */
3420 REAL8 m2_SI, /**< mass of companion 2 (kg) */
3421 REAL8 S1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
3422 REAL8 S1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
3423 REAL8 S1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
3424 REAL8 S2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
3425 REAL8 S2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
3426 REAL8 S2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
3427 REAL8 deltaF, /**< frequency spacing (Hz) */
3428 REAL8 f_min, /**< starting GW frequency (Hz) */
3429 REAL8 f_max, /**< ending GW frequency (Hz) */
3430 REAL8 f_ref, /**< reference GW frequency (Hz) */
3431 REAL8 phiRef, /**< phase shift at reference frequency */
3432 REAL8 distance, /**< distance of source (m) */
3433 REAL8 inclination, /**< inclination of source (rad) */
3434 LALDict *lalParams /**< LAL dictionary with extra options */
3435)
3436{
3437 LALValue *ModeArrayJframe = NULL; // Modes in the precessing J-frame. Specified through the new LAL dictionary option "ModeArrayJframe"
3438
3439 /* Use an auxiliar laldict to not overwrite the input argument */
3440 LALDict *lalParams_aux;
3441 /* setup mode array */
3442 if (lalParams == NULL)
3443 {
3444 lalParams_aux = XLALCreateDict();
3445 }
3446 else{
3447 lalParams_aux = XLALDictDuplicate(lalParams);
3448 }
3449
3450 /* Check that the co-precessing modes chosen are available for the model */
3451 XLAL_CHECK(check_input_mode_array(lalParams_aux) == XLAL_SUCCESS, XLAL_EFAULT, "Not available co-precessing mode chosen.\n");
3452
3453
3454 /* Read mode array from LAL dictionary */
3455 ModeArrayJframe = XLALSimInspiralWaveformParamsLookupModeArrayJframe(lalParams_aux);
3456
3457 /* If input LAL dictionary does not have mode array, use all the modes available for XPHM (l<=4) */
3458 if(ModeArrayJframe == NULL)
3459 {
3461 ModeArrayJframe = XLALSimInspiralModeArrayActivateAllModesAtL(ModeArrayJframe, 3);
3462 ModeArrayJframe = XLALSimInspiralModeArrayActivateAllModesAtL(ModeArrayJframe, 4);
3463 }
3464 else{
3465 /* Check that the modes chosen are available for the model */
3466 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);
3467 }
3468
3469
3470 INT4 length = 0;
3471 /***** Loop over modes ******/
3472 for (UINT4 ell = 2; ell <= LAL_SIM_L_MAX_MODE_ARRAY; ell++)
3473 {
3474 for (INT4 emm = -(INT4)ell; emm <= (INT4)ell; emm++)
3475 {
3476 if(XLALSimInspiralModeArrayIsModeActive(ModeArrayJframe, ell, emm) !=1)
3477 {
3478 /* Skip mode if user did not specified it. */
3479 continue;
3480 }
3481 //Variable to store the strain of only one (positive/negative) mode: h_lm
3482 COMPLEX16FrequencySeries *hlmpos = NULL;
3483 COMPLEX16FrequencySeries *hlmneg = NULL;
3484 COMPLEX16FrequencySeries *hlmall = NULL;
3485
3486 /* Compute precessing single mode */
3487 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);
3488
3489 if (!(hlmpos) || !hlmneg){ XLAL_ERROR(XLAL_EFUNC);}
3490
3491 length = hlmpos->data->length-1;
3492
3493 hlmall = XLALCreateCOMPLEX16FrequencySeries("hlmall: precessing FD mode", &(hlmpos->epoch), hlmpos->f0, hlmpos->deltaF, &(hlmpos->sampleUnits), 2*length+1);
3494
3495 for(INT4 i=0; i<=length; i++)
3496 {
3497 hlmall->data->data[i+length] = hlmpos->data->data[i];
3498 hlmall->data->data[i] = hlmneg->data->data[length-i];
3499 }
3500
3501
3502 // Add single mode to list
3503 *hlms = XLALSphHarmFrequencySeriesAddMode(*hlms, hlmall, ell, emm);
3504
3505 // Free memory
3509 }
3510 } /* End loop over modes */
3511
3512
3513 /* Add frequency array to SphHarmFrequencySeries */
3514 REAL8Sequence *freqs = XLALCreateREAL8Sequence(2*length+1);
3515 for (INT4 i = -length; i<=length; i++)
3516 {
3517 freqs->data[i+length] = i*deltaF;
3518 }
3520
3521 /* Free memory */
3522 XLALDestroyDict(lalParams_aux);
3523 XLALDestroyValue(ModeArrayJframe);
3524
3525
3526 return XLAL_SUCCESS;
3527
3528}
3529
3530
3531/*********************************************/
3532/* */
3533/* AUXILIARY FUNCTIONS */
3534/* */
3535/*********************************************/
3536
3537
3538/** Wrapper function for setup ModeArray of modes in the precessing frame to be twisted up. */
3539LALDict *IMRPhenomXPHM_setup_mode_array(LALDict *lalParams)
3540{
3541 /* setup ModeArray */
3542 INT4 lalParams_In = 0;
3543 if (lalParams == NULL)
3544 {
3545 lalParams_In = 1;
3546 lalParams = XLALCreateDict();
3547 }
3548 LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(lalParams);
3549
3550 /* If the mode array is empty, populate using a default choice of modes */
3551 if (ModeArray == NULL)
3552 {
3553 /* Default behaviour */
3554 XLAL_PRINT_INFO("Using default non-precessing modes for IMRPhenomXPHM: 2|2|, 2|1|, 3|3|, 3|2|, 4|4|.\n");
3555 ModeArray = XLALSimInspiralCreateModeArray();
3556
3557 /* IMRPhenomXHM has the following calibrated modes. 22 mode taken from IMRPhenomXAS */
3558 XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 2);
3559 XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 1);
3560 XLALSimInspiralModeArrayActivateMode(ModeArray, 3, 3);
3561 XLALSimInspiralModeArrayActivateMode(ModeArray, 3, 2);
3562 XLALSimInspiralModeArrayActivateMode(ModeArray, 4, 4);
3563 XLALSimInspiralModeArrayActivateMode(ModeArray, 2, -2);
3564 XLALSimInspiralModeArrayActivateMode(ModeArray, 2, -1);
3565 XLALSimInspiralModeArrayActivateMode(ModeArray, 3, -3);
3566 XLALSimInspiralModeArrayActivateMode(ModeArray, 3, -2);
3567 XLALSimInspiralModeArrayActivateMode(ModeArray, 4, -4);
3569 }
3570 else {XLAL_PRINT_INFO("Using custom non-precessing modes for PhenomXPHM.\n"); }
3571
3572 XLALDestroyValue(ModeArray);
3573 if(lalParams_In == 1)
3574 {
3575 XLALDestroyDict(lalParams);
3576 }
3577
3578 return lalParams;
3579}
3580
3581
3582/* Function to check if the input mode array in the J-frame contains unsupported modes */
3583INT4 check_input_mode_array_Jframe(LALValue *ModeArrayJframe){
3584 /* Check if the input array has a too high l. */
3585 for(INT4 ell=2; ell<=LAL_SIM_L_MAX_MODE_ARRAY; ell++)
3586 {
3587 for(INT4 emm=0; emm<=ell; emm++)
3588 {
3589 if(XLALSimInspiralModeArrayIsModeActive(ModeArrayJframe, ell, emm) == 1 && ell>L_MAX){
3590 XLALDestroyValue(ModeArrayJframe);
3591 return XLAL_FAILURE;
3592 }
3593 }
3594 }
3595 return XLAL_SUCCESS;
3596}
3597
3598/*
3599 Return the offset at reference frequency for alpha and epsilon Euler angles for a particular non-precessing mode.
3600 E.g.: alpha_offset_mprime = alpha(2*pi*MfRef/mprime) - alpha0. Used for Pv2 and Pv3 angles.
3601*/
3603 REAL8 *alpha_offset_mprime, /**< [out] Offset alpha angle at reference frequency */
3604 REAL8 *epsilon_offset_mprime, /**< [out] Offset epsilon angle at reference frequency */
3605 INT4 mprime, /**< Second index of the non-precesssing mode (l, mprime) */
3606 IMRPhenomXPrecessionStruct *pPrec /**< IMRPhenomXP Precessing structure*/
3607)
3608{
3609 /* The angles are evaluated at frequency 2*pi*MfRef/mprime so the offset depend on mprime. */
3610 switch(mprime)
3611 {
3612 case 1:
3613 {
3614 *alpha_offset_mprime = pPrec->alpha_offset_1;
3615 *epsilon_offset_mprime = pPrec->epsilon_offset_1;
3616 break;
3617 }
3618 case 2:
3619 {
3620 *alpha_offset_mprime = pPrec->alpha_offset; // These variable was already used in the XP code,
3621 *epsilon_offset_mprime = pPrec->epsilon_offset; // that is why we did not add the _2 here.
3622 break;
3623 }
3624 case 3:
3625 {
3626 *alpha_offset_mprime = pPrec->alpha_offset_3;
3627 *epsilon_offset_mprime = pPrec->epsilon_offset_3;
3628 break;
3629 }
3630 case 4:
3631 {
3632 *alpha_offset_mprime = pPrec->alpha_offset_4;
3633 *epsilon_offset_mprime = pPrec->epsilon_offset_4;
3634 break;
3635 }
3636 default:
3637 {
3638 XLAL_ERROR(XLAL_EINVAL,"Error: mprime not supported, check available non-precessing modes.\n");
3639 break;
3640 }
3641 }
3642
3643 return XLAL_SUCCESS;
3644}
3645
3646
3647/* Return the 3 Post-Newtonian Euler angles evaluated at frequency (2*pi*Mf/mprime). */
3648/* 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. */
3650 REAL8 *alpha, /**< [out] Azimuthal angle of L w.r.t J */
3651 REAL8 *cBetah, /**< [out] Cosine of polar angle between L and J */
3652 REAL8 *sBetah, /**< [out] Sine of polar angle between L and J */
3653 REAL8 *epsilon, /**< [out] Minus the third Euler angle (-gamma) describing L w.r.t J, fixed by minimal rotation condition */
3654 INT4 mprime, /**< Second index of the non-precesssing mode (l, mprime) */
3655 REAL8 Mf, /**< Frequency geometric units */
3656 IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Precessing structure*/
3657 IMRPhenomXWaveformStruct *pWF /**< IMRPhenomX Waveform structure*/
3658)
3659{
3660 double omega = LAL_PI * Mf *2./mprime;
3661 double logomega = log(omega);
3662 double omega_cbrt = cbrt(omega);
3663 double omega_cbrt2 = omega_cbrt * omega_cbrt;
3664
3665 REAL8 alpha_offset_mprime = 0., epsilon_offset_mprime = 0.;
3666
3667 Get_alpha_epsilon_offset(&alpha_offset_mprime, &epsilon_offset_mprime, mprime, pPrec);
3668
3669 *alpha = (
3670 pPrec->alpha1 / omega
3671 + pPrec->alpha2 / omega_cbrt2
3672 + pPrec->alpha3 / omega_cbrt
3673 + pPrec->alpha4L * logomega
3674 + pPrec->alpha5 * omega_cbrt
3675 - alpha_offset_mprime
3676 );
3677
3678 *epsilon = (
3679 pPrec->epsilon1 / omega
3680 + pPrec->epsilon2 / omega_cbrt2
3681 + pPrec->epsilon3 / omega_cbrt
3682 + pPrec->epsilon4L * logomega
3683 + pPrec->epsilon5 * omega_cbrt
3684 - epsilon_offset_mprime
3685 );
3686
3687 INT4 status = 0;
3688 status = IMRPhenomXWignerdCoefficients(cBetah, sBetah, omega_cbrt, pWF, pPrec);
3689 XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "Call to IMRPhenomXWignerdCoefficients failed.");
3690
3691 return status;
3692}
3693
3694
3695/*
3696 Non-uniform/coarse frequency grid for the Multibanding of the angles.
3697
3698 - In this first release we use the same coarse grid that is used for computing the non-precessing modes.
3699 - This grid is discussed in section II of arXiv:2001.10897. See also section D of Precessing paper.
3700 - This grid is computed with the function XLALSimIMRPhenomXMultibandingVersion defined in LALSimIMRPhenomXHM_multiband.c.
3701 - The version of the coarse grid will be changed with the option 'MBandPrecVersion' defined in LALSimInspiralWaveformParams.c.
3702 - Currently there is only one version available and the option value for that is 0, which is the default value.
3703*/
3705 REAL8Sequence **coarseFreqs, /**<[out] Non-uniform coarse frequency grid (1D array) */
3706 UINT4 ell, /**< First index non-precessing mode */
3707 UINT4 emmprime, /**< Second index non-precessing mode */
3708 IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct*/
3709 LALDict *lalParams) /**< LAL dictionary */
3710{
3711 /* This function is basically a copy of the first part of IMRPhenomXHMMultiBandOneMode and IMRPhenomXHMMultiBandOneModeMixing. */
3712
3713 /* Create non-uniform grid for each mode. */
3715
3716 /* Compute the coarse frequency array. It is stored in a list of grids. */
3717 size_t iStart = (size_t) (pWF->fMin / pWF->deltaF);
3718
3719 /* Final grid spacing, adimensional (NR) units */
3720 REAL8 evaldMf = XLALSimIMRPhenomXUtilsHztoMf(pWF->deltaF, pWF->Mtot);
3721
3722 /* Variable for the Multibanding criteria. See eq. 2.8-2.9 of arXiv:2001.10897. */
3723 REAL8 dfpower = 11./6.;
3724 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);
3725
3726 /* Variables for the coarse frequency grid */
3727 REAL8 Mfmin = XLALSimIMRPhenomXUtilsHztoMf(iStart*pWF->deltaF, pWF->Mtot);
3729 REAL8 MfMECO, MfLorentzianEnd;
3730 REAL8 dfmerger = 0., dfringdown = 0.;
3731 UINT4 lengthallGrids = 20;
3732
3735
3736 //populate coefficients of 22 mode, to rotate to spherical
3739 IMRPhenomXGetPhaseCoefficients(pWF, pPhase22);
3740
3741 /* Allocate and initialize the PhenomXHM lm amplitude coefficients struct */
3744
3745 if(ell == 2 && emmprime ==2){
3746 MfMECO = pWF->fMECO;
3747 #if DEBUG == 1
3748 printf("\nfRING = %e\n",pWF->fRING);
3749 printf("fDAMP = %e\n",pWF->fDAMP);
3750 printf("alphaL22 = %.16e", pPhase22->cLovfda/pWF->eta);
3751 #endif
3752 MfLorentzianEnd = pWF->fRING + 2*pWF->fDAMP;
3754 dfmerger = deltaF_mergerBin(pWF->fDAMP, pPhase22->cLovfda/pWF->eta, thresholdMB);
3755 dfringdown = deltaF_ringdownBin(pWF->fDAMP, pPhase22->cLovfda/pWF->eta, pAmp22->gamma2/(pAmp22->gamma3*pWF->fDAMP),thresholdMB);
3756 }
3757 else{
3758 // allocate qnm struct
3759 QNMFits *qnms = (QNMFits *) XLALMalloc(sizeof(QNMFits));
3761 // Populate pWFHM
3762 IMRPhenomXHM_SetHMWaveformVariables(ell, emmprime, pWFHM, pWF, qnms, lalParams);
3763 LALFree(qnms);
3764
3765 /* Allocate and initialize the PhenomXHM lm phase and amp coefficients struct */
3768
3769 if(pWFHM->MixingOn == 1){
3770 /* Get coefficients for Amplitude and phase */
3771 GetSpheroidalCoefficients(pPhase, pPhase22, pWFHM, pWF);
3773 }
3774
3775 /* Get coefficients for Amplitude and phase */
3776 IMRPhenomXHM_GetAmplitudeCoefficients(pAmp, pPhase, pAmp22, pPhase22, pWFHM, pWF);
3777 IMRPhenomXHM_GetPhaseCoefficients(pAmp, pPhase, pAmp22, pPhase22, pWFHM, pWF,lalParams);
3778
3779 MfMECO = pWFHM->fMECOlm;
3780 MfLorentzianEnd = pWFHM->fRING + 2*pWFHM->fDAMP;
3781 #if DEBUG == 1
3782 printf("\nfRING = %e\n",pWFHM->fRING);
3783 printf("fDAMP = %e\n",pWFHM->fDAMP);
3784 printf("alphaL = %.16e", pPhase->alphaL);
3785 #endif
3786 deltaF_MergerRingdown(&dfmerger, &dfringdown, thresholdMB, pWFHM, pAmp, pPhase);
3787 }
3788 LALFree(pWFHM);
3789 LALFree(pAmp);
3790 LALFree(pPhase);
3791 LALFree(pAmp22);
3792 LALFree(pPhase22);
3793
3794 UINT4 nGridsUsed = XLALSimIMRPhenomXMultibandingGrid(Mfmin, MfMECO, MfLorentzianEnd, Mfmax, evaldMf, dfpower, dfcoefficient, allGrids, dfmerger, dfringdown);
3795 if (allGrids == NULL)
3796 {
3797 #if DEBUG == 1
3798 printf("Malloc of allGrids failed!\n");
3799 printf("nGridsUsed = %i\n", nGridsUsed);
3800 #endif
3801 return -1;
3802 }
3803
3804 /* Number of fine frequencies per coarse interval in every coarse grid */
3805 /* Actual number of subgrids to be used. We allocated more than needed. */
3806 UINT4 actualnumberofGrids = 0;
3807 /* Length of coarse frequency array */
3808 UINT4 lenCoarseArray = 0;
3809
3810 /* Transform the coarse frequency array to 1D array. */
3811 // Take only the subgrids needed
3812 for(UINT4 kk = 0; kk < nGridsUsed; kk++){
3813 lenCoarseArray = lenCoarseArray + allGrids[kk].Length;
3814 actualnumberofGrids++;
3815
3816 #if DEBUG == 1
3817 printf("\nkk = %i\n",kk);
3818 printf("xStart: %.16e\n", allGrids[kk].xStart);
3819 printf("xEnd: %.16e\n", allGrids[kk].xEndRequested);
3820 printf("Length: %i\n", allGrids[kk].Length);
3821 printf("deltax: %.16e\n", allGrids[kk].deltax);
3822 printf("evaldMf: %.16e\n", evaldMf);
3823 printf("xMax: %.16e\n", allGrids[kk].xMax);
3824 printf("Last grid.xMax = %.16f\n", allGrids[actualnumberofGrids-1].xMax);
3825 #endif
3826
3827 if(allGrids[kk].xMax + evaldMf >= Mfmax){
3828 break;
3829 }
3830 }
3831
3832 // Add extra points to the coarse grid if the last freq is lower than Mfmax
3833 while(allGrids[actualnumberofGrids-1].xMax < Mfmax){
3834 allGrids[actualnumberofGrids-1].xMax = allGrids[actualnumberofGrids-1].xMax + allGrids[actualnumberofGrids-1].deltax;
3835 allGrids[actualnumberofGrids-1].Length = allGrids[actualnumberofGrids-1].Length + 1;
3836 lenCoarseArray++;
3837 }
3838
3839 #if DEBUG == 1
3840 printf("\nactualnumberofGrids = %i\n", actualnumberofGrids);
3841 printf("lenCoarseArray = %i\n", lenCoarseArray);
3842 printf("Last grid.xMax = %.16f", allGrids[actualnumberofGrids-1].xMax);
3843 #endif
3844
3845 // Transform coarse frequency array to 1D vector
3846 /* Allocate memory for frequency array and terminate if this fails */
3847 (*coarseFreqs) = XLALCreateREAL8Sequence(lenCoarseArray);
3848 if (!(*coarseFreqs)) { XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");}
3849
3850 UINT4 lencoarseFreqs = 0;
3851
3852 for(UINT4 kk = 0; kk < actualnumberofGrids; kk++){
3853 for(INT4 ll = 0; ll < allGrids[kk].Length; ll++){
3854 (*coarseFreqs)->data[lencoarseFreqs] = (allGrids[kk].xStart + allGrids[kk].deltax*ll);
3855 lencoarseFreqs++;
3856 }
3857 }
3858
3859 /* End of coarse frequency array. */
3860
3861 #if DEBUG == 1
3862 printf("\n******** Coarse frequencies array done ********* \n");
3863 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));
3864 #endif
3865
3866
3867 LALFree(allGrids);
3868 return actualnumberofGrids;
3869}
3870
3871/* @} */
3872/* @} */
void XLALDestroyDict(LALDict *dict)
LALDict * XLALDictDuplicate(const LALDict *orig)
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)
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)
#define MAX(a, b)
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.
LALValue * XLALSimInspiralWaveformParamsLookupModeArrayJframe(LALDict *params)
LALValue * XLALSimInspiralWaveformParamsLookupModeArray(LALDict *params)
int XLALSimInspiralWaveformParamsInsertModeArray(LALDict *params, LALValue *value)
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 * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
COMPLEX16FrequencySeries * XLALResizeCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series, int first, 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 IMRPhenomX_PNR_GenerateAntisymmetricWaveform(REAL8Sequence *antisymamp, REAL8Sequence *antisymphase, const REAL8Sequence *freqs, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
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.
int XLALSimInspiralModeArrayIsModeActive(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralModeArrayActivateMode(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralCreateModeArray(void)
Create a LALValue pointer to store the mode array.
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
COMPLEX16Sequence * XLALCreateCOMPLEX16Sequence(size_t length)
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
void XLALDestroyCOMPLEX16Sequence(COMPLEX16Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(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.
REAL8 gamma_ref
Record value of gamma at f_ref.
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 alpha_ref
Record value of alpha at f_ref.
REAL8 * data
double f_min
Definition: unicorn.c:22
double f_max
Definition: unicorn.c:23