LALSimulation  5.4.0.1-fe68b98
LALSimIMRPhenomX.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2018 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 /* Standard LAL */
21 #include <lal/Sequence.h>
22 #include <lal/Date.h>
23 #include <lal/Units.h>
24 #include <lal/XLALError.h>
25 
26 /* LAL datatypes and constants */
27 #include <lal/LALDatatypes.h>
28 #include <lal/LALStdlib.h>
29 #include <lal/LALConstants.h>
30 
31 /* Time series, frequency series and spherical harmonics */
32 #include <lal/TimeSeries.h>
33 #include <lal/TimeFreqFFT.h>
34 #include <lal/SphericalHarmonics.h>
35 #include <lal/FrequencySeries.h>
36 
37 /* LALSimulation */
38 #include <lal/LALSimIMR.h>
39 #include <lal/LALSimInspiral.h>
40 
41 /* Standard C */
42 #include <math.h>
43 #include <complex.h>
44 #include <stdbool.h>
45 
46 #ifndef PHENOMXHMDEBUG
47 #define DEBUG 0
48 #define PHENOMXDEBUG 0
49 #define PHENOMXPDEBUG 0
50 #else
51 #define DEBUG 1 //print debugging info
52 #define PHENOMXDEBUG 1
53 #define PHENOMXPDEBUG 1
54 #endif
55 
56 /* Link IMRPhenomX routines */
57 #include "LALSimIMRPhenomX.h"
63 #include "LALSimIMRPhenomX_PNR.c"
65 
66 /* Note: This is declared in LALSimIMRPhenomX_internals.c and avoids namespace clashes */
68 
69 #ifndef _OPENMP
70 #define omp ignore
71 #endif
72 
73 /* ******** ALIGNED SPIN IMR PHENOMENOLOGICAL WAVEFORM: IMRPhenomXAS ********* */
74 
75 /* EXTERNAL ROUTINES */
76 
77 /**
78  * @addtogroup LALSimIMRPhenomX_c
79  * @brief Routines to produce IMRPhenomX-family of phenomenological
80  * inspiral-merger-ringdown waveforms.
81  *
82  * These are frequency-domain models for compact binaries at comparable and extreme mass ratios,
83  * tuned to numerical-relativity simulations.
84  * * IMRPhenomXAS model for the 22 mode of non-precessing binaries. https://arxiv.org/abs/2001.11412 ; DCC link: https://dcc.ligo.org/P2000018
85  * * IMRPhenomXHM model with subdominant modes for non-precessing binaries. https://arxiv.org/abs/2001.10914 ; DCC link: https://dcc.ligo.org/P1900393
86  * * Multibanding for IMRPhenomXHM. https://arxiv.org/abs/2001.10897 ; DCC link: https://dcc.ligo.org/P1900391
87  * * IMRPhenomXP model for the 22 mode (in the coprecessing frame) of precessing binaries. https://arxiv.org/abs/2004.06503 ; DCC link: https://dcc.ligo.org/P2000039
88  * * IMRPhenomXPHM model with subdominant modes for precessing binaries. https://arxiv.org/abs/2004.06503 ; DCC link: https://dcc.ligo.org/P2000039
89  *
90  * The previous models are for binary black holes. There are also versions for binary neutron stars, using the extension of the binary black hole models
91  * given in https://arxiv.org/abs/1905.06011 ; DCC link: https://dcc.ligo.org/P1900148
92  * * IMRPhenomXAS_NRTidalv2 model for the 22 mode of non-precessing binary neutron stars
93  * * IMRPhenomXP_NRTidalv2 model for the 22 mode (in the coprecessing frame) of precessing binary neutron stars
94  *
95  * @review IMRPhenomXAS & IMRPhenomXHM reviewed by Maria Haney, Patricia Schmidt,
96  * Roberto Cotesta, Anuradha Samajdar, Jonathan Thompson, N.V. Krishnendu.
97  * IMRPhenomXP & IMRPhenomXPHM reviewed by Maria Haney, Jonathan Thompson,
98  * Marta Colleoni, David Keitel.
99  * Combined review wiki:
100  * https://git.ligo.org/waveforms/reviews/imrphenomx/-/wikis/home
101  *
102  * @review IMRPhenomXAS_NRTidalv2 & IMRPhenomXP_NRTidalv2 plus SpinTaylor precession option also applicable to IMRPhenomXP & IMRPhenomXPHM reviewed by Maria Haney,
103  * Sarp Akcay, N.V. Krishnendu, Shubhanshu Tiwari.
104  * Review wiki: https://git.ligo.org/waveforms/reviews/imrphenomxp_nrtidalv2/-/wikis/home
105  *
106  */
107 
108  /**
109  * @addtogroup LALSimIMRPhenomX_c
110  * @{
111  *
112  * @name Routines for IMRPhenomXAS
113  * @{
114  *
115  * @author Geraint Pratten
116  *
117  * @brief C code for IMRPhenomXAS phenomenological waveform model.
118  *
119  * This is an aligned-spin frequency domain model for the 22 mode.
120  * See G.Pratten et al arXiv:2001.11412 for details. Any studies that use this waveform model should include
121  * a reference to this paper.
122  *
123  * @note The model was calibrated to mass-ratios from 1 to 1000.
124  * The calibration points will be given in forthcoming papers.
125  *
126  * @attention The model is usable outside this parameter range,
127  * and in tests to date gives sensible physical results,
128  * but conclusive statements on the physical fidelity of
129  * the model for these parameters await comparisons against further
130  * numerical-relativity simulations. For more information, see the review wiki
131  * under https://git.ligo.org/waveforms/reviews/imrphenomx/wikis/home
132  *
133  *
134  * Waveform flags:
135  * InsPhaseVersion: Determines the inspiral phase model.
136  * - 104 : Canonical TaylorF2 at 3.5PN including cubic-in-spin and quadratic-in-spin corrections. Uses 4 pseudo-PN coefficients. (RECOMMENDED).
137  * - 105 : Canonical TaylorF2 at 3.5PN including cubic-in-spin and quadratic-in-spin corrections. Uses 5 pseudo-PN coefficients.
138  * - 114 : Extended TaylorF2. Includes cubic-in-spin, quadratic-in-spin corrections, 4PN and 4.5PN orbital corrections. Uses 4 pseudo-PN coefficients.
139  * - 115 : Extended TaylorF2. Includes cubic-in-spin, quadratic-in-spin corrections, 4PN and 4.5PN orbital corrections. Uses 5 pseudo-PN coefficients.
140  *
141  * IntPhaseVersion: Determines the intermediate phase model.
142  * - 104 : 4th order polynomial ansatz.
143  * - 105 : 5th order polynomial ansatz. (RECOMMENDED).
144  *
145  * RDPhaseVersion: Determines the merger-ringdown phase model.
146  * - 105 : Deformed Lorentzian using 5 coefficients. (RECOMMENDED).
147  *
148  * InsAmpVersion : Determines inspiral amplitude model.
149  * - 103 : Canonical PN re-expanded TaylorF2 amplitude with pseudo-PN corrections. (RECOMMENDED).
150  *
151  * IntAmpVersion : Determines intermediate amplitude model.
152  * - 104 : Based on a 4th order polynomial ansatz. Less accurate but stable extrapolation. (RECOMMENDED).
153  * - 105 : Based on 5th order polynomial ansatz. More accurate in calibration domain, more unstable extrapolation.
154  *
155  * RDAmpVersion : Determines the merger-ringdown amplitude model.
156  * - 103 : Deformed Lorentzian with 3 free coefficients. Uses 1 calibrated collocation point and 2 calibrated phenomenological coefficients. (RECOMMENDED).
157  *
158  */
159 
160 
161 /**
162  * Driver routine to calculate an IMRPhenomX aligned-spin,
163  * inspiral-merger-ringdown phenomenological waveform model
164  * in the frequency domain.
165  *
166  * arXiv:2001.11412, https://arxiv.org/abs/2001.11412
167  *
168  * All input parameters should be in SI units. Angles should be in radians.
169  *
170  * XLALSimIMRPhenomXASGenerateFD() returns the strain of the 2-2 mode as a complex
171  * frequency series with equal spacing deltaF and contains zeros from zero frequency
172  * to the starting frequency and zeros beyond the cutoff frequency in the ringdown.
173  *
174  */
176  COMPLEX16FrequencySeries **htilde22, /**< [out] FD waveform */
177  REAL8 m1_SI, /**< Mass of companion 1 (kg) */
178  REAL8 m2_SI, /**< Mass of companion 2 (kg) */
179  REAL8 chi1L, /**< Dimensionless aligned spin of companion 1 */
180  REAL8 chi2L, /**< Dimensionless aligned spin of companion 2 */
181  REAL8 distance, /**< Luminosity distance (m) */
182  REAL8 f_min, /**< Starting GW frequency (Hz) */
183  REAL8 f_max, /**< End frequency; 0 defaults to Mf = 0.3 */
184  REAL8 deltaF, /**< Sampling frequency (Hz) */
185  REAL8 phi0, /**< Orbital phase at fRef (rad) */
186  REAL8 fRef_In, /**< Reference frequency (Hz) */
187  LALDict *lalParams /**< LAL Dictionary */
188 )
189 {
190  UINT4 status;
191 
192  /* Set debug status here */
193  UINT4 debug = PHENOMXDEBUG;
194 
195  if(debug)
196  {
197  printf("fRef_In : %e\n",fRef_In);
198  printf("m1_SI : %e\n",m1_SI);
199  printf("m2_SI : %e\n",m2_SI);
200  printf("chi1L : %e\n",chi1L);
201  printf("chi2L : %e\n\n",chi2L);
202  printf("Performing sanity checks...\n");
203  }
204 
205  /* Perform initial sanity checks */
206  if(*htilde22) { XLAL_CHECK(NULL != htilde22, XLAL_EFAULT); }
207  if(fRef_In < 0.0) { XLAL_ERROR(XLAL_EDOM, "fRef_In must be positive or set to 0 to ignore.\n"); }
208  if(deltaF <= 0.0) { XLAL_ERROR(XLAL_EDOM, "deltaF must be positive.\n"); }
209  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
210  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
211  if(f_min <= 0.0) { XLAL_ERROR(XLAL_EDOM, "f_min must be positive.\n"); }
212  if(f_max < 0.0) { XLAL_ERROR(XLAL_EDOM, "f_max must be non-negative.\n"); }
213  if(distance < 0.0) { XLAL_ERROR(XLAL_EDOM, "Distance must be positive and greater than 0.\n"); }
214 
215  /*
216  Perform a basic sanity check on the region of the parameter space in which model is evaluated. Behaviour is as follows:
217  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
218  - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
219  - For mass ratios > 1000: throw a hard error that model is not valid.
220  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
221 
222  */
223  REAL8 mass_ratio;
224  if(m1_SI > m2_SI)
225  {
226  mass_ratio = m1_SI / m2_SI;
227  }
228  else
229  {
230  mass_ratio = m2_SI / m1_SI;
231  }
232  if(mass_ratio > 20.0 ) { XLAL_PRINT_INFO("Warning: Extrapolating outside of Numerical Relativity calibration domain."); }
233  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
234  if(fabs(chi1L) > 0.99 || fabs(chi2L) > 0.99) { XLAL_PRINT_INFO("Warning: Extrapolating to extremal spins, model is not trusted."); }
235 
236  /* If no reference frequency is given, set it to the starting gravitational wave frequency */
237  REAL8 fRef = (fRef_In == 0.0) ? f_min : fRef_In;
238 
239 
240  if(debug)
241  {
242  printf("\n\n **** Initializing waveform struct... **** \n\n");
243  }
244 
245 
246  /* Initialize the useful powers of LAL_PI */
248  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
249 
250  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
252  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
253  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, fRef, phi0, f_min, f_max, distance, 0.0, lalParams, debug);
254  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
255 
256  /*
257  Create a REAL8 frequency series.
258  Use fLow, fHigh, deltaF to compute frequency sequence. Only pass the boundaries (fMin, fMax).
259  */
261  freqs->data[0] = pWF->fMin;
262  freqs->data[1] = pWF->f_max_prime;
263 
264 
265  if(debug)
266  {
267  printf("\n\n **** Calling IMRPhenomXASGenerateFD... **** \n\n");
268  }
269 
270  /* We now call the core IMRPhenomXAS waveform generator */
271  status = IMRPhenomXASGenerateFD(htilde22, freqs, pWF, lalParams);
272  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXASFDCore failed to generate IMRPhenomX waveform.");
273 
274  if(debug)
275  {
276  printf("\n\n **** Call to IMRPhenomXASGenerateFD complete. **** \n\n");
277  }
278 
279  /*
280  We now resize htilde22 if our waveform was generated to a cut-off frequency below
281  the desired maximum frequency. Simply fill the remaining frequencies with zeros.
282  */
283  REAL8 lastfreq;
284  if (pWF->f_max_prime < pWF->fMax)
285  {
286  /*
287  As the user has requested an f_max > Mf = fCut,
288  we resize the frequency series to fill with zeros beyond the cutoff frequency.
289  */
290  lastfreq = pWF->fMax;
291  }
292  else{ // We have to look for a power of 2 anyway.
293  lastfreq = pWF->f_max_prime;
294  }
295  /* Enforce length to be a power of 2 + 1 */
296  size_t n_full = NextPow2(lastfreq / pWF->deltaF) + 1;
297  size_t n = (*htilde22)->data->length;
298 
299  /* Resize the COMPLEX16 frequency series */
300  *htilde22 = XLALResizeCOMPLEX16FrequencySeries(*htilde22, 0, n_full);
301  XLAL_CHECK (*htilde22, XLAL_ENOMEM, "Failed to resize waveform 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 );
302 
303 
304  LALFree(pWF);
306  return XLAL_SUCCESS;
307 }
308 
309 
310 /**
311  * Compute waveform in LAL format at specified frequencies for the IMRPhenomX model.
312  *
313  * All input parameters should be in SI units. Angles should be in radians.
314  *
315  * XLALSimIMRPhenomXASFrequencySequence() returns the strain of the 2-2 mode as a
316  * complex frequency series with entries exactly at the frequencies specified in
317  * the sequence freqs (which can be unequally spaced). No zeros are added. Assumes positive frequencies.
318  */
320  COMPLEX16FrequencySeries **htilde22, /**< [out] FD waveform */
321  const REAL8Sequence *freqs, /**< [out] Frequency series [Hz] */
322  REAL8 m1_SI, /**< Mass of companion 1 (kg) */
323  REAL8 m2_SI, /**< Mass of companion 2 (kg) */
324  REAL8 chi1L, /**< Dimensionless aligned spin of companion 1 */
325  REAL8 chi2L, /**< Dimensionless aligned spin of companion 2 */
326  REAL8 distance, /**< Luminosity distance (m) */
327  REAL8 phi0, /**< Phase at reference frequency */
328  REAL8 fRef_In, /**< Reference frequency (Hz) */
329  LALDict *lalParams /**< LAL Dictionary */
330  )
331  {
332  INT4 return_code = 0;
333 
334  /* Sanity checks */
335  if(*htilde22) { XLAL_CHECK(NULL != htilde22, XLAL_EFAULT); }
336  if(fRef_In < 0.0) { XLAL_ERROR(XLAL_EDOM, "fRef_In must be positive or set to 0 to ignore.\n"); }
337  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
338  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
339  if(distance < 0.0) { XLAL_ERROR(XLAL_EDOM, "Distance must be positive and greater than 0.\n"); }
340 
341  /*
342  Perform a basic sanity check on the region of the parameter space in which model is evaluated. Behaviour is as follows:
343  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
344  - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
345  - For mass ratios > 1000: throw a hard error that model is not valid.
346  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
347 
348  */
349  REAL8 mass_ratio;
350  if(m1_SI > m2_SI)
351  {
352  mass_ratio = m1_SI / m2_SI;
353  }
354  else
355  {
356  mass_ratio = m2_SI / m1_SI;
357  }
358  if(mass_ratio > 20.0 ) { XLAL_PRINT_INFO("Warning: Extrapolating outside of Numerical Relativity calibration domain."); }
359 
360  // Check on the mass-ratio with a 1e-12 tolerance to avoid rounding errors
361  if(mass_ratio > 1000. && fabs(mass_ratio - 1000) > 1e-12) { XLAL_ERROR(XLAL_EDOM, "ERROR: Model not valid at mass ratios beyond 1000."); }
362  if(fabs(chi1L) > 0.99 || fabs(chi2L) > 0.99) { XLAL_PRINT_INFO("Warning: Extrapolating to extremal spins, model is not trusted."); }
363 
364  // If fRef is not provided, then set fRef to be the starting GW Frequency
365  REAL8 fRef = (fRef_In == 0.0) ? freqs->data[0] : fRef_In;
366 
368  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
369 
370  /*
371  This routine automatically performs sanity checks on the masses, spins, etc.
372  */
373  REAL8 f_min_In = freqs->data[0];
374  REAL8 f_max_In = freqs->data[freqs->length - 1];
375 
376  /*
377  Passing deltaF = 0 implies that freqs is a frequency grid with non-uniform spacing.
378  The function waveform then start at lowest given frequency.
379  */
380 
381  /* Initialize IMRPhenomX waveform struct and perform sanity check. */
383  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
384  return_code = IMRPhenomXSetWaveformVariables(pWF,m1_SI, m2_SI, chi1L, chi2L, 0.0, fRef, phi0, f_min_In, f_max_In, distance, 0.0, lalParams, 0);
385  XLAL_CHECK(XLAL_SUCCESS == return_code, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
386 
387  /* Now call the core IMRPhenomX waveform generator */
388  return_code = IMRPhenomXASGenerateFD(
389  htilde22,
390  freqs,
391  pWF,
392  lalParams
393  );
394  XLAL_CHECK(return_code == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXASFDCore failed to generate IMRPhenomX waveform.");
395  LALFree(pWF);
396 
397  return XLAL_SUCCESS;
398  }
399 
400  /**
401  * Compute the duration of IMRPhenomXAS using the approximate SPA relation \f$t_f \sim \frac{1}{2 \pi} \frac{d \varphi}{d f} \f$
402  *
403  * All input parameters should be in SI units. Angles should be in radians.
404  *
405  * XLALSimIMRPhenomXASDuration() returns the duration in s of IMRPhenomXAS from the specified starting frequency in Hz up to
406  * the peak ringdown frequency as defined in Eq. 5.14 of https://arxiv.org/abs/2001.11412.
407  */
409  const REAL8 m1_SI, /**< mass of companion 1 (kg) */
410  const REAL8 m2_SI, /**< mass of companion 2 (kg) */
411  const REAL8 chi1L, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
412  const REAL8 chi2L, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
413  const REAL8 f_start /**< Initial frequency (Hz) */
414  )
415  {
416  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
417  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
418  if(f_start <= 0.0) { XLAL_ERROR(XLAL_EDOM, "f_start must be positive.\n"); }
419  if(fabs(chi1L) > 1.0) { XLAL_ERROR(XLAL_EDOM, "Unphysical chi_1 requested: must obey the Kerr bound [-1,1].\n"); }
420  if(fabs(chi2L) > 1.0) { XLAL_ERROR(XLAL_EDOM, "Unphysical chi_2 requested: must obey the Kerr bound [-1,1].\n"); }
421 
422  /* Set debug status here */
423  int debug = PHENOMXDEBUG;
424 
425  /* Initialize useful powers of LAL_PI */
427  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
428 
429  LALDict *lal_dict;
430  lal_dict = XLALCreateDict();
431 
432  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
434  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
435  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, 0, f_start, f_start, 0, 0, 1.0, 0.0, lal_dict, debug);
436  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
437 
438  /* Allocate and initialize the PhenomX 22 amplitude coefficients struct */
440  pAmp22 = XLALMalloc(sizeof(IMRPhenomXAmpCoefficients));
442  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetAmplitudeCoefficients failed.\n");
443 
444  /* Allocate and initialize the PhenomX 22 phase coefficients struct */
445  IMRPhenomXPhaseCoefficients *pPhase22;
446  pPhase22 = XLALMalloc(sizeof(IMRPhenomXPhaseCoefficients));
447  status = IMRPhenomXGetPhaseCoefficients(pWF,pPhase22);
448  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetPhaseCoefficients failed.\n");
449 
450  /* Initialize a struct containing useful powers of Mf at fRef */
451  IMRPhenomX_UsefulPowers powers_of_MfRef;
452  status = IMRPhenomX_Initialize_Powers(&powers_of_MfRef,pWF->MfRef);
453  XLAL_CHECK(XLAL_SUCCESS == status, status, "IMRPhenomX_Initialize_Powers failed for MfRef.\n");
454 
455  /* Get phase connection coefficients */
457 
458  /* 1/eta is used to re-scale phase */
459  REAL8 inveta = (1.0 / pWF->eta);
460 
461  REAL8 duration = 0.0;
462  double M_sec = LAL_MTSUN_SI * (m1_SI + m2_SI) / LAL_MSUN_SI;
463 
464  double dphi_start = 0.0;
465  double dphi_end = 0.0;
466 
467  /* Starting frequency in geometric units */
468  double Mf_start = f_start * M_sec;
469  /* Approximate peak of the ringdown in geometric units, see Eq. 5.14 of https://arxiv.org/abs/2001.11412 */
470  double Mf_end = pAmp22->fAmpRDMin;
471 
472  IMRPhenomX_UsefulPowers powers_of_Mf;
473  status = IMRPhenomX_Initialize_Powers(&powers_of_Mf,Mf_start);
474  XLAL_CHECK(XLAL_SUCCESS == status, status, "IMRPhenomX_Initialize_Powers failed for Mf_start.\n");
475  dphi_start = inveta * IMRPhenomX_dPhase_22(Mf_start, &powers_of_Mf, pPhase22, pWF);
476 
477  status = IMRPhenomX_Initialize_Powers(&powers_of_Mf,Mf_end);
478  XLAL_CHECK(XLAL_SUCCESS == status, status, "IMRPhenomX_Initialize_Powers failed for Mf_end.\n");
479  dphi_end = inveta * IMRPhenomX_dPhase_22(Mf_end, &powers_of_Mf, pPhase22, pWF);
480 
481  /*
482  - Convert from geometric back to physical units to report the duration in s
483  - Use fabs as we want to report the duration as the time to merger
484  */
485  duration = fabs(dphi_start - dphi_end) / 2.0 / LAL_PI * M_sec;
486 
487  /* Free up arrays */
488  LALFree(pAmp22);
489  LALFree(pPhase22);
490  LALFree(pWF);
491  XLALDestroyDict(lal_dict);
492  return duration;
493  }
494 
495  /** @} */
496  /** @} */
497 
498 
499  /* *********************************************************************************
500  *
501  * The following private function generates an IMRPhenomX frequency-domain waveform
502  * - Only aligned-spin
503  * - Only the 22-mode
504  * - Physical parameters are passed via the waveform struct
505  * *********************************************************************************
506  */
508  COMPLEX16FrequencySeries **htilde22, /**< [out] FD waveform */
509  const REAL8Sequence *freqs_In, /**< Input frequency grid */
510  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
511  LALDict *lalParams /**< LAL Dictionary Structure */
512 )
513 {
514  /* Inherits debug flag from waveform struct */
515  UINT4 debug = PHENOMXDEBUG;
516 
517  /* Set tidal version */
518  NRTidal_version_type NRTidal_version;
519 
520  NRTidal_version=IMRPhenomX_SetTidalVersion(lalParams);
521 
522  if(debug)
523  {
524  printf("\n **** Now in IMRPhenomXASGenerateFD... **** \n");
525  }
526 
527  /* Set LIGOTimeGPS */
528  LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO; // = {0,0}
529 
530  /* Initialize useful powers of LAL_PI */
532  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.");
533 
534  /* Inherit minimum and maximum frequencies to generate wavefom from input frequency grid */
535  double f_min = freqs_In->data[0];
536  double f_max = freqs_In->data[freqs_In->length - 1];
537 
538  /* Size of array */
539  size_t npts = 0;
540 
541  /* Index shift between freqs and the frequency series */
542  UINT4 offset = 0;
543 
544  /* Initialize frequency sequence */
545  REAL8Sequence *freqs = NULL;
546 
547  /* Initialize tidal corrections (following tidal code modified from LALSimIMRPhenomP.c) */
548 
549  REAL8Sequence *phi_tidal = NULL;
550  REAL8Sequence *amp_tidal = NULL;
551  REAL8Sequence *planck_taper = NULL;
552 
553 
554  /* Set matter parameters (set to zero in pWF if NRTidal additions are not turned on) */
555  REAL8 lambda1 = pWF->lambda1;
556  REAL8 lambda2 = pWF->lambda2;
557 
558  /* New variables needed for the NRTidalv2 model */
559  REAL8 X_A = pWF->m1; // Already scaled by Mtot
560  REAL8 X_B = pWF->m2; // Ibid.
561  REAL8 pfaN = 3./(128.*X_A*X_B);
562 
563  /* If deltaF is non-zero then we need to generate a uniformly sampled frequency grid of spacing deltaF. Start at f = 0. */
564  if(pWF->deltaF > 0)
565  {
566  /* Return the closest power of 2 */
567  npts = (size_t) (f_max/pWF->deltaF) + 1;
568 
569  /* Debug information */
570  if(debug)
571  {
572  printf("npts = %zu\n",npts);
573  printf("fMin = %.4f\n",f_min);
574  printf("fMax = %.4f\n",f_max);
575  printf("dF = %.4f\n",pWF->deltaF);
576  }
577 
578  XLAL_CHECK(XLALGPSAdd(&ligotimegps_zero, -1. / pWF->deltaF ), XLAL_EFUNC, "Failed to shift the coalescence time to t=0. Tried to apply a shift of -1/df with df = %g.", pWF->deltaF);
579 
580  /* Initialize the htilde frequency series */
581  *htilde22 = XLALCreateCOMPLEX16FrequencySeries("htilde22: FD waveform",&ligotimegps_zero,0.0,pWF->deltaF,&lalStrainUnit,npts);
582 
583  /* Check that frequency series generated okay */
584  XLAL_CHECK(*htilde22,XLAL_ENOMEM,"Failed to allocate COMPLEX16FrequencySeries of length %zu for f_max = %f, deltaF = %g.\n",npts,f_max,pWF->deltaF);
585 
586  /* Frequencies will be set using only the lower and upper bounds that we passed */
587  size_t iStart = (size_t) (f_min / pWF->deltaF);
588  size_t iStop = (size_t) (f_max / pWF->deltaF) + 1;
589 
590  XLAL_CHECK ( (iStop <= npts) && (iStart <= iStop), XLAL_EDOM,
591  "minimum freq index %zu and maximum freq index %zu do not fulfill 0<=ind_min<=ind_max<=htilde->data>length=%zu.", iStart, iStop, npts);
592 
593  /* Allocate memory for frequency array and terminate if this fails */
594  freqs = XLALCreateREAL8Sequence(iStop - iStart);
595  if (!freqs)
596  {
597  XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
598  }
599 
600  /* Populate frequency array */
601  for (UINT4 i = iStart; i < iStop; i++)
602  {
603  freqs->data[i-iStart] = i * pWF->deltaF;
604  }
605  offset = iStart;
606  }
607  else
608  {
609  /* freqs is a frequency grid with non-uniform spacing, so we start at the lowest given frequency */
610  npts = freqs_In->length;
611  *htilde22 = XLALCreateCOMPLEX16FrequencySeries("htilde22: FD waveform, 22 mode", &ligotimegps_zero, f_min, pWF->deltaF, &lalStrainUnit, npts);
612 
613  XLAL_CHECK (*htilde22, XLAL_ENOMEM, "Failed to allocated waveform COMPLEX16FrequencySeries of length %zu from sequence.", npts);
614 
615  offset = 0;
616  freqs = XLALCreateREAL8Sequence(freqs_In->length);
617 
618  /* Allocate memory for frequency array and terminate if this fails */
619  if (!freqs)
620  {
621  XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
622  }
623 
624  /* Populate frequency array */
625  for (UINT4 i = 0; i < freqs_In->length; i++)
626  {
627  freqs->data[i] = freqs_In->data[i];
628  }
629  }
630 
631  memset((*htilde22)->data->data, 0, npts * sizeof(COMPLEX16));
632  XLALUnitMultiply(&((*htilde22)->sampleUnits), &((*htilde22)->sampleUnits), &lalSecondUnit);
633 
634  /* Check if LAL dictionary exists. If not, create a LAL dictionary. */
635  INT4 lalParams_In = 0;
636  if(lalParams == NULL)
637  {
638  lalParams_In = 1;
639  lalParams = XLALCreateDict();
640  }
641 
642  if(debug)
643  {
644  printf("\n\n **** Initializing amplitude struct... **** \n\n");
645  }
646 
647  /* Allocate and initialize the PhenomX 22 amplitude coefficients struct */
649  pAmp22 = XLALMalloc(sizeof(IMRPhenomXAmpCoefficients));
651  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetAmplitudeCoefficients failed.\n");
652 
653  if(debug)
654  {
655  printf("\n\n **** Amplitude struct initialized. **** \n\n");
656  printf("\n\n **** Initializing phase struct... **** \n\n");
657  }
658 
659  /* Allocate and initialize the PhenomX 22 phase coefficients struct */
660  IMRPhenomXPhaseCoefficients *pPhase22;
661  pPhase22 = XLALMalloc(sizeof(IMRPhenomXPhaseCoefficients));
662  status = IMRPhenomXGetPhaseCoefficients(pWF,pPhase22);
663  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetPhaseCoefficients failed.\n");
664  // initialize coefficients of tidal phase
665  if(NRTidal_version!=NoNRT_V) IMRPhenomXGetTidalPhaseCoefficients(pWF,pPhase22,NRTidal_version);
666 
667  if(debug)
668  {
669  printf("\n\n **** Phase struct initialized. **** \n\n");
670  }
671 
672  /*
673  Apply time shifts so peak amplitude is near t ~ 0.
674  */
675 
676  /* Initialize a struct containing useful powers of Mf at fRef */
677  IMRPhenomX_UsefulPowers powers_of_MfRef;
678  status = IMRPhenomX_Initialize_Powers(&powers_of_MfRef,pWF->MfRef);
679  XLAL_CHECK(XLAL_SUCCESS == status, status, "IMRPhenomX_Initialize_Powers failed for MfRef.\n");
680 
681  /* Linear time and phase shifts so that model peaks near t ~ 0 */
682  REAL8 lina = 0;
683 
684  /* Get phase connection coefficients */
686  double linb=IMRPhenomX_TimeShift_22(pPhase22, pWF);
687 
688  //
689  INT4 APPLY_PNR_DEVIATIONS = pWF->APPLY_PNR_DEVIATIONS;
690  INT4 PNRForceXHMAlignment = pWF->IMRPhenomXPNRForceXHMAlignment;
691  // If applying PNR deviations, then we want to be able to refer to some non-PNR waveform properties. For that, we must compute the struct for when PNR is off (and specifically, XAS is wanted).
692  if ( APPLY_PNR_DEVIATIONS && PNRForceXHMAlignment ) {
693 
694  /*<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.<-.
695  Shift phifRef and linb so that the PNR CoPrecessing model is aligned with XHM
696  ->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.->.*/
697 
698  // // Development printing
699  // printf("** Enforcing XAS Phase Alignment\n");
700  // printf("(1) linb = %f\n",linb);
701 
702 
703  IMRPhenomX_PNR_EnforceXASPhaseAlignment(&linb,pWF,pPhase22);
704 
705  // // Development printing
706  // printf("(2) linb = %f\n",linb);
707 
708  }
709  // extra contribution to phi(fRef) due to tidal corrections
710  double phiTfRef = 0.;
711 
712  REAL8 f_final=freqs->data[freqs->length-1];
713 
714  // correct for time and phase shifts due to tidal phase
715  if(NRTidal_version!=NoNRT_V){
716 
717  REAL8 f_merger = XLALSimNRTunedTidesMergerFrequency(pWF->Mtot, pWF->kappa2T, pWF->q);
718  if(f_merger<f_final)
719  f_final = f_merger;
720 
721  IMRPhenomX_UsefulPowers powers_of_ffinal;
722  REAL8 Mf_final = f_final*pWF->M_sec;
723  status = IMRPhenomX_Initialize_Powers(&powers_of_ffinal,Mf_final);
724  XLAL_CHECK(XLAL_SUCCESS == status, status, "IMRPhenomX_Initialize_Powers failed for f_final.\n");
725  REAL8 dphi_fmerger=1/pWF->eta*IMRPhenomX_dPhase_22(Mf_final, &powers_of_ffinal, pPhase22, pWF)+linb-IMRPhenomX_TidalPhaseDerivative(&powers_of_ffinal, pWF, pPhase22, NRTidal_version);
726  REAL8 tshift = -dphi_fmerger;
727  linb+=tshift;
728  phiTfRef = -IMRPhenomX_TidalPhase(&powers_of_MfRef, pWF, pPhase22, NRTidal_version);
729 
730  }
731 
732 
733  /* 1/eta is used to re-scale phase */
734  REAL8 inveta = (1.0 / pWF->eta);
735 
736  /* Calculate phase at reference frequency: phifRef = 2.0*phi0 + LAL_PI_4 + PhenomXPhase(fRef) */
737  double phifRef = -(inveta * IMRPhenomX_Phase_22(pWF->MfRef, &powers_of_MfRef, pPhase22, pWF) + phiTfRef + linb*pWF->MfRef + lina) + 2.0*pWF->phi0 + LAL_PI_4;
738 
739  // Define pWF->phifRef and phifRef separately, as "phifRef" may ultimately differ superficially
740  pWF->phifRef = phifRef;
741 
742 
743  /*
744  Here we declare explicit REAL8 variables for main loop in order to avoid numerous
745  pointer calls.
746  */
747  //REAL8 MfRef = pWF->MfRef;
748  REAL8 Msec = pWF->M_sec;
749 
750  REAL8 C1IM = pPhase22->C1Int;
751  REAL8 C2IM = pPhase22->C2Int;
752  REAL8 C1RD = pPhase22->C1MRD;
753  REAL8 C2RD = pPhase22->C2MRD;
754 
755  REAL8 fPhaseIN = pPhase22->fPhaseMatchIN;
756  REAL8 fPhaseIM = pPhase22->fPhaseMatchIM;
757  REAL8 fAmpIN = pAmp22->fAmpMatchIN;
758  REAL8 fAmpIM = pAmp22->fAmpRDMin;
759 
760  if(debug)
761  {
762  printf("\n\n **** Phase struct initialized. **** \n\n");
763  printf("C1IM = %.4f\n",C1IM);
764  printf("C2IM = %.4f\n",C2IM);
765  printf("C1RD = %.4f\n",C1RD);
766  printf("C2RD = %.4f\n",C2RD);
767  printf("fIN = %.4f\n",fPhaseIN);
768  printf("fIM = %.4f\n",fPhaseIM);
769  }
770 
771  REAL8 Amp0 = pWF->amp0 * pWF->ampNorm;
772 
773  /* initial_status used to track */
774  UINT4 initial_status = XLAL_SUCCESS;
775 
776  if (NRTidal_version!=NoNRT_V) {
777  int ret = 0;
778  UINT4 L_fCut = freqs->length;
779  phi_tidal = XLALCreateREAL8Sequence(L_fCut);
780  amp_tidal = XLALCreateREAL8Sequence(L_fCut);
781  planck_taper = XLALCreateREAL8Sequence(L_fCut);
782  /* Get FD tidal phase correction and amplitude factor */
783  ret = XLALSimNRTunedTidesFDTidalPhaseFrequencySeries(phi_tidal, amp_tidal, planck_taper, freqs, pWF->m1_SI, pWF->m2_SI, lambda1, lambda2, NRTidal_version);
784  XLAL_CHECK(XLAL_SUCCESS == ret, ret, "XLALSimNRTunedTidesFDTidalPhaseFrequencySeries Failed.");
785  }
786 
787  /* Now loop over main driver to generate waveform: h(f) = A(f) * Exp[I phi(f)] */
788  #pragma omp parallel for
789  for (UINT4 idx = 0; idx < freqs->length; idx++)
790  {
791  double Mf = Msec * freqs->data[idx]; // Mf is declared locally inside the loop
792  UINT4 jdx = idx + offset; // jdx is declared locally inside the loop
793 
794  /* We do not want to generate the waveform at frequencies > f_max (default = 0.3 Mf) */
795  if(Mf <= (pWF->f_max_prime * pWF->M_sec))
796  {
797 
798  /* Initialize a struct containing useful powers of Mf */
799  IMRPhenomX_UsefulPowers powers_of_Mf;
800  initial_status = IMRPhenomX_Initialize_Powers(&powers_of_Mf,Mf);
801  if(initial_status != XLAL_SUCCESS)
802  {
803  status = initial_status;
804  XLALPrintError("IMRPhenomX_Initialize_Powers failed for Mf, initial_status=%d",initial_status);
805  }
806  else
807  {
808  /* Generate amplitude and phase at MfRef */
809  REAL8 amp = 0.0;
810  REAL8 phi = 0.0;
811 
812  /* The functions in this routine are inlined to help performance. */
813  /* Construct phase */
814  if(Mf < fPhaseIN)
815  {
816  phi = IMRPhenomX_Inspiral_Phase_22_AnsatzInt(Mf, &powers_of_Mf, pPhase22);
817  }
818  else if(Mf > fPhaseIM)
819  {
820  phi = IMRPhenomX_Ringdown_Phase_22_AnsatzInt(Mf, &powers_of_Mf, pWF, pPhase22) + C1RD + (C2RD * Mf);
821  }
822  else
823  {
824  phi = IMRPhenomX_Intermediate_Phase_22_AnsatzInt(Mf, &powers_of_Mf, pWF, pPhase22) + C1IM + (C2IM * Mf);
825  }
826 
827  /* Scale phase by 1/eta */
828  phi *= inveta;
829  phi += linb*Mf + lina + phifRef;
830 
831  /* Construct amplitude */
832  if(Mf < fAmpIN)
833  {
834  amp = IMRPhenomX_Inspiral_Amp_22_Ansatz(Mf, &powers_of_Mf, pWF, pAmp22);
835  }
836  else if(Mf > fAmpIM)
837  {
838  amp = IMRPhenomX_Ringdown_Amp_22_Ansatz(Mf, pWF, pAmp22);
839  }
840  else
841  {
842  amp = IMRPhenomX_Intermediate_Amp_22_Ansatz(Mf, &powers_of_Mf, pWF, pAmp22);
843  }
844 
845  // /* Reconstruct waveform: h(f) = A(f) * Exp[I phi(f)] */
846  // ((*htilde22)->data->data)[jdx] = Amp0 * powers_of_Mf.m_seven_sixths * amp * cexp(I * phi);
847 
848  /* NOTE that the above lines are commented out to clarify legacy code structure.
849  HERE, we allow the user to toggle output of ONLY the model's phase using lalParams.
850  The intended use of this option is to enable exact output of the phase, without unwanted numerical effects that result from unwrapping the complete waveform. In particular, at low frequencies, the phase may vary so quickly that adjacent frequency points correctly differ by more than 2*pi, thus limiting unwrap routines which assume that this is not the case.
851  */
852  if ( pWF->PhenomXOnlyReturnPhase ) {
853  //
854  ((*htilde22)->data->data)[jdx] = phi;
855  } else {
856  /* Add NRTidal phase, if selected, code adapted from LALSimIMRPhenomP.c */
857 
858  if (NRTidal_version!=NoNRT_V) {
859 
860  REAL8 phaseTidal = phi_tidal->data[idx];
861  double ampTidal = amp_tidal->data[idx];
862  double window = planck_taper->data[idx];
863 
864  /* Add spin-induced quadrupole moment terms to tidal phasing */
865 
866  /* 2PN terms */
867  phaseTidal += pfaN * pPhase22->c2PN_tidal* powers_of_lalpi.m_one_third * powers_of_Mf.m_one_third;
868 
869  /* 3PN terms */
870  phaseTidal += pfaN * pPhase22->c3PN_tidal* powers_of_lalpi.one_third * powers_of_Mf.one_third;
871 
872  /* 3.5PN terms are only in NRTidalv2 */
873  if (NRTidal_version == NRTidalv2_V) {
874  phaseTidal += pfaN * pPhase22->c3p5PN_tidal * powers_of_lalpi.two_thirds * powers_of_Mf.two_thirds;
875  }
876  /* Reconstruct waveform with NRTidal terms included: h(f) = [A(f) + A_tidal(f)] * Exp{I [phi(f) - phi_tidal(f)]} * window(f) */
877  ((*htilde22)->data->data)[jdx] = pWF->amp0 * (pWF->ampNorm * powers_of_Mf.m_seven_sixths * amp + 2*sqrt(1./5.)*powers_of_lalpi.sqrt * ampTidal) * cexp(I * (phi - phaseTidal))* window;
878 
879  }
880  else if (NRTidal_version == NoNRT_V) {
881  /* Reconstruct waveform: h(f) = A(f) * Exp[I phi(f)] */
882  ((*htilde22)->data->data)[jdx] = Amp0 * powers_of_Mf.m_seven_sixths * amp * cexp(I * phi);
883  }
884  else {
885  XLAL_PRINT_INFO("Warning: Only NRTidal, NRTidalv2, and NoNRT NRTidal_version values allowed and NRTidal is not implemented completely in IMRPhenomX*.");
886  }
887  }
888  }
889  }
890  else
891 
892  {
893  /* Mf > Mf_max, so return 0 */
894  ((*htilde22)->data->data)[jdx] = 0.0 + I*0.0;
895  }
896 
897  }
898 
899  // Free allocated memory
900  LALFree(pAmp22);
901  LALFree(pPhase22);
903 
904  // Free allocated memory for tidal extension
905  XLALDestroyREAL8Sequence(phi_tidal);
906  XLALDestroyREAL8Sequence(amp_tidal);
907  XLALDestroyREAL8Sequence(planck_taper);
908 
909  if(lalParams_In == 1)
910  {
911  XLALDestroyDict(lalParams);
912  }
913 
914  return status;
915 }
916 
917 
918 /* Useful wrapper to check for uniform frequency grids taken from LALSimIMRPhenomHM.c */
920  REAL8Sequence *frequencies,
921  REAL8 df
922 )
923 {
924  INT4 IsUniform = 0;
925 
926  /* If the frequency series has length of 2 and a df > 0 then it is uniformly sampled */
927  if( (frequencies->length == 2) && (df > 0.) )
928  {
929  IsUniform = 1;
930  }
931 
932  return IsUniform;
933 };
934 
935 
936 
937 
938 /* ******** PRECESSING IMR PHENOMENOLOGICAL WAVEFORM: IMRPhenomXP ********* */
939 
940 
941 /*
942  Decleration for internal function to generate precessing-spin, 22 only IMRPhenomXP waveform.
943  Declared here and not in header due to IMRPhenomXPrecessionStruct.
944 */
946  COMPLEX16FrequencySeries **hptilde, /**< [out] FD waveform */
947  COMPLEX16FrequencySeries **hctilde, /**< [out] FD waveform */
948  const REAL8Sequence *freqs_In, /**< Input frequency grid */
949  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
950  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Waveform Struct */
951  LALDict *lalParams /**< LAL Dictionary Structure */
952 );
953 
954 /**
955  * @addtogroup LALSimIMRPhenomX_c
956  * @{
957  *
958  * @name Routines for IMRPhenomXP
959  * @{
960  *
961  * @author Geraint Pratten, Cecilio García Quirós
962  *
963  * @brief C code for IMRPhenomXP phenomenological waveform model.
964  *
965  * This is a precessing frequency domain model.
966  * See Pratten, García-Quirós, Colleoni et al arXiv:2004.06503 for details.
967 * Studies using this model are kindly asked
968  * to cite Pratten et al arXiv:2001.11412, García-Quirós et al arXiv:2001.10914
969  * and Pratten, García-Quirós, Colleoni et al arXiv:2004.06503.
970  *
971  * @note The underlying aligned-spin model was calibrated for
972  * mass-ratios 1 to 1000.
973  *
974  * @attention The model can be called outside this parameter space
975  * and in all tests to date gives sensible physical results
976  * but conclusive statements on the physical fidelity of
977  * the model requires further detailed comparisons against
978  * numerical-relativity simulations. For more information, see the review wiki
979  * under https://git.ligo.org/waveforms/reviews/imrphenomx/wikis/home
980  *
981  * IMRPhenomXP/HM is based on a modular framework. User can specify flags to control how Euler angles are calculated, the final spin
982  * parameterization and the conventions used in reconstructing the waveform in the LAL frame. A detailed discussion can be
983  * found in arXiv:2004.06503. The various flags are detailed below.
984  *
985  * Precession flags:
986  * PhenomXPrecVersion:
987  * - 101 : NNLO PN Euler angles and a 2PN non-spinning approximation to L
988  * - 102 : NNLO PN Euler angles and a 3PN spinning approximation to L
989  * - 103 : NNLO PN Euler angles and a 4PN spinning approximation to L
990  * - 104 : NNLO PN Euler angles and a 4PN spinning approximation to L augmeneted with leading PN order at all order in spin terms. See N. Siemonsen et al, PRD, 97, 124046, (2018), arXiv:1712.08603
991  * - 220 : MSA Euler angles and a 3PN spinning approximation to L, see K. Chatziioannou et al, PRD, 95, 104004, (2017), arXiv:1703.03967. Defaults to NNLO version 102 if MSA fails to initialize.
992  * - 221 : MSA Euler angles and a 3PN spinning approximation to L.
993  * - 222 : MSA Euler angles as implemented in LALSimInspiralFDPrecAngles.
994  * - 223 : MSA Euler angles as implemented in LALSimInspiralFDPrecAngles. Defaults to NNLO version 102 if MSA fails to initialize. [Default].
995  * - 224 : As version 220 but using the \f$\phi_{z,0}\f$ and \f$\zeta_{z,0}\f$ prescription from 223.
996  * - 310 : Numerical integration of SpinTaylor equations with constant angles in merger-ringdown
997  * - 311 : Numerical integration of SpinTaylor equations with constant angles in merger-ringdown, without tidal and non-black hole spin-induced quadrupole terms in the SpinTaylor equations
998  * when used in IMRPhenomXP_NRTidalv2, for comparison
999  * - 320 : Numerical integration of SpinTaylor equations, analytical continuation in merger-ringdown
1000  * - 321 : Numerical integration of SpinTaylor equations, analytical continuation in merger-ringdown, without tidal and non-black hole spin-induced quadrupole terms in the SpinTaylor equations
1001  * when used in IMRPhenomXP_NRTidalv2, for comparison
1002 
1003  *
1004  * PhenomXPExpansionOrder:
1005  * - -1, 0, 1, 2, 3, 4, 5. Controls the expansion order of the leading-order MSA terms for both \f$\zeta\f$ and \f$\phi_z\f$. [Default is 5].
1006  *
1007  * PhenomXPFinalSpinMod:
1008  * - 0 : Modify final spin based on \f$\chi_p\f$. [Recommended default for NNLO angles].
1009  * - 1 : Modify final spin using \f$\chi_{1x}\f$. This is pathological. Do not use. Implemented to compare to PhenomPv3 pre-bug fix.
1010  * - 2 : Modify final spin using norm of total in-plane spin vector.
1011  * - 3 : Modify final spin using precession-averaged couplings from MSA analysis. Only works with MSA Euler angles (versions 220, 221, 222, 223 and 224). If MSA fails to initialize
1012  * or called with NNLO angles, default to version 0. [Default]
1013  * - 4: Modify final spin estimating the total in-plane spin from the PN spin-evolution equations.
1014  *
1015  * PhenomXPConvention (App. C and Table IV of arXiv:2004.06503):
1016  * - 0 : Conventions defined as following https://dcc.ligo.org/LIGO-T1500602
1017  * - 1 : Convention defined following App. C, see Table II of arXiv:2004.06503 for specific details. [Default]
1018  * - 5 : Conventions as used in PhenomPv3/HM
1019  * - 6 : Conventions defined following App. C, see Table II of arXiv:2004.06503 for specific details.
1020  * - 7 : Conventions defined following App. C, see Table II of arXiv:2004.06503 for specific details.
1021  */
1022 
1023 
1024 /*
1025  * Prototype wrapper function:
1026 
1027  * Driver routine to calculate an IMRPhenomX precessing,
1028  * inspiral-merger-ringdown phenomenological waveform model
1029  * in the frequency domain.
1030  *
1031  * All input parameters should be in SI units. Angles should be in radians.
1032  *
1033  * Returns the plus and cross polarizations as a complex frequency series with
1034  * equal spacing deltaF and contains zeros from zero frequency to the starting
1035  * frequency fLow and zeros beyond the cutoff frequency in the ringdown.
1036  *
1037  */
1039  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency-domain waveform h+ */
1040  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency-domain waveform hx */
1041  REAL8 m1_SI, /**< mass of companion 1 (kg) */
1042  REAL8 m2_SI, /**< mass of companion 2 (kg) */
1043  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1044  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1045  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1046  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1047  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1048  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1049  const REAL8 distance, /**< Distance of source (m) */
1050  const REAL8 inclination, /**< inclination of source (rad) */
1051  const REAL8 phiRef, /**< Orbital phase (rad) at reference frequency */
1052  REAL8 f_min, /**< Starting GW frequency (Hz) */
1053  REAL8 f_max, /**< Ending GW frequency (Hz); Defaults to Mf = 0.3 if no f_max is specified. */
1054  const REAL8 deltaF, /**< Sampling frequency (Hz). To use non-uniform frequency grid, set deltaF <= 0. */
1055  REAL8 fRef_In, /**< Reference frequency (Hz) */
1056  LALDict *lalParams /**< LAL Dictionary struct */
1057 )
1058 {
1059  UINT4 status;
1060  UINT4 debug = PHENOMXPDEBUG;
1061 
1062  /*
1063  Set initial values of masses and z-components of spins to pass to IMRPhenomXSetWaveformVariables() so it can swap the
1064  matter parameters (and masses and spins) appropriately if m1 < m2, since the masses and spin vectors will also be
1065  swapped by XLALIMRPhenomXPCheckMassesAndSpins() below.
1066  */
1067  const REAL8 m1_SI_init = m1_SI;
1068  const REAL8 m2_SI_init = m2_SI;
1069  const REAL8 chi1z_init = chi1z;
1070  const REAL8 chi2z_init = chi2z;
1071 
1072  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
1073  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
1074 
1075  #if PHENOMXPDEBUG == 1
1076  printf("fRef_In : %e\n",fRef_In);
1077  printf("m1_SI : %e\n",m1_SI);
1078  printf("m2_SI : %e\n",m2_SI);
1079  printf("chi1z : %e\n",chi1z);
1080  printf("chi2z : %e\n",chi2z);
1081  printf("phiRef : %e\n",phiRef);
1082  printf("Prec V. : %d\n\n",XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams));
1083  printf("Performing sanity checks...\n");
1084  #endif
1085 
1086  /* Perform initial sanity checks */
1087  if(*hptilde) { XLAL_CHECK(NULL != hptilde, XLAL_EFAULT); }
1088  if(*hctilde) { XLAL_CHECK(NULL != hctilde, XLAL_EFAULT); }
1089  if(fRef_In < 0.0) { XLAL_ERROR(XLAL_EDOM, "fRef_In must be positive or set to 0 to ignore.\n"); }
1090  if(deltaF <= 0.0) { XLAL_ERROR(XLAL_EDOM, "deltaF must be positive.\n"); }
1091  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
1092  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
1093  if(f_min <= 0.0) { XLAL_ERROR(XLAL_EDOM, "f_min must be positive.\n"); }
1094  if(f_max < 0.0) { XLAL_ERROR(XLAL_EDOM, "f_max must be non-negative.\n"); }
1095  if(distance <= 0.0) { XLAL_ERROR(XLAL_EDOM, "Distance must be positive and greater than 0.\n"); }
1096 
1097  /*
1098  Perform a basic sanity check on the region of the parameter space in which model is evaluated.
1099  Behaviour is as follows, consistent with the choices for IMRPhenomXAS/IMRPhenomXHM
1100  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
1101  - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
1102  - For mass ratios > 1000: throw a hard error that model is not valid.
1103  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
1104 
1105  */
1106  REAL8 mass_ratio;
1107  if(m1_SI > m2_SI)
1108  {
1109  mass_ratio = m1_SI / m2_SI;
1110  }
1111  else
1112  {
1113  mass_ratio = m2_SI / m1_SI;
1114  }
1115  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"); }
1116  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
1117  if(fabs(chi1z) > 0.99 || fabs(chi2z) > 0.99) { XLAL_PRINT_WARNING("Warning: Extrapolating to extremal spins, aligned spin model is not trusted.\n"); }
1118 
1119  /* If no reference frequency is given, set it to the starting gravitational wave frequency */
1120  const REAL8 fRef = (fRef_In == 0.0) ? f_min : fRef_In;
1121 
1122  /* Use an auxiliar laldict to not overwrite the input argument */
1123  LALDict *lalParams_aux;
1124  /* setup mode array */
1125  if (lalParams == NULL)
1126  {
1127  lalParams_aux = XLALCreateDict();
1128  }
1129  else{
1130  lalParams_aux = XLALDictDuplicate(lalParams);
1131  }
1132 
1133  #if PHENOMXPDEBUG == 1
1134  printf("\n\n **** Initializing waveform struct... **** \n\n");
1135  #endif
1136 
1137  /* Initialize useful powers of LAL_PI - this is used in the code called by IMRPhenomXPGenerateFD */
1139  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
1140 
1141  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
1143  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
1144  // this function will use the original input to swap the order of tidal parameters, if necessary to enforce m1>m2
1145  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI_init, m2_SI_init, chi1z_init, chi2z_init, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, debug);
1146  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
1147 
1148 
1149  /*
1150  Create a REAL8 frequency series.
1151  Use fLow, fHigh, deltaF to compute frequency sequence. Only pass the boundaries (fMin, fMax).
1152  */
1154  freqs->data[0] = pWF->fMin;
1155  freqs->data[1] = pWF->f_max_prime;
1156 
1158  XLAL_CHECK(
1159  (fRef >= pWF->fMin)&&(fRef <= pWF->f_max_prime),
1160  XLAL_EFUNC,
1161  "Error: f_min = %.2f <= fRef = %.2f < f_max = %.2f required when using tuned angles.\n",pWF->fMin,fRef,pWF->f_max_prime);
1162  }
1163 
1164  #if PHENOMXPDEBUG == 1
1165  printf("\n\n **** Initializing precession struct... **** \n\n");
1166  #endif
1167 
1168 
1169  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
1171  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
1172 
1173  /* If user chose SpinTaylor angles, set bounds for interpolation of angles */
1174  int pflag = XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams_aux);
1175  if(pflag==310||pflag==311||pflag==320||pflag==321)
1176  pPrec->M_MIN = 2, pPrec->M_MAX = 2;
1177 
1179  pWF,
1180  pPrec,
1181  m1_SI,
1182  m2_SI,
1183  chi1x,
1184  chi1y,
1185  chi1z,
1186  chi2x,
1187  chi2y,
1188  chi2z,
1189  lalParams_aux,
1191  );
1192  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
1193 
1194  #if PHENOMXPDEBUG == 1
1195  printf("\n\n **** Calling IMRPhenomXPGenerateFD... **** \n\n");
1196  #endif
1197 
1198  /* We now call the core IMRPhenomXP waveform generator */
1199  status = IMRPhenomXPGenerateFD(hptilde, hctilde, freqs, pWF, pPrec, lalParams_aux);
1200  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPGenerateFD failed to generate IMRPhenomX waveform.\n");
1201 
1202  #if PHENOMXPDEBUG == 1
1203  printf("\n\n **** Call to IMRPhenomXPGenerateFD complete. **** \n\n");
1204  #endif
1205 
1206  /* Resize hptilde & hctilde */
1207  REAL8 lastfreq;
1208  if (pWF->f_max_prime < pWF->fMax)
1209  {
1210  /*
1211  The user has requested a higher f_max than Mf = fCut.
1212  Resize the frequency series to fill with zeros beyond the cutoff frequency.
1213  */
1214  lastfreq = pWF->fMax;
1215  }
1216  else
1217  {
1218  lastfreq = pWF->f_max_prime;
1219  }
1220  /* Enforce length to be a power of 2 + 1 */
1221  size_t n_full = NextPow2(lastfreq / pWF->deltaF) + 1;
1222  size_t n = (*hptilde)->data->length;
1223 
1224  /* Resize the COMPLEX16 frequency series */
1225  *hptilde = XLALResizeCOMPLEX16FrequencySeries(*hptilde, 0, n_full);
1226  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", n, pWF->fCut, n_full, pWF->fMax );
1227 
1228  /* Resize the COMPLEX16 frequency series */
1229  *hctilde = XLALResizeCOMPLEX16FrequencySeries(*hctilde, 0, n_full);
1230  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", n, pWF->fCut, n_full, pWF->fMax );
1231 
1232 
1233  /* Destroy structs and arrays */
1234  LALFree(pWF);
1235  LALFree(pPrec);
1236  XLALDestroyREAL8Sequence(freqs);
1237  XLALDestroyDict(lalParams_aux);
1238 
1239  return XLAL_SUCCESS;
1240 }
1241 
1242 
1243 /**
1244  * Compute waveform in LAL format at specified frequencies for the IMRPhenomXP model.
1245  *
1246  * XLALSimIMRPhenomXPGenerateFD() returns the plus and cross polarizations as a complex
1247  * frequency series with equal spacing deltaF and contains zeros from zero frequency
1248  * to the starting frequency and zeros beyond the cutoff frequency in the ringdown.
1249  *
1250  * In contrast, XLALSimIMRPhenomXPFrequencySequence() returns a
1251  * complex frequency series with entries exactly at the frequencies specified in
1252  * the sequence freqs (which can be unequally spaced). No zeros are added.
1253  *
1254  */
1256  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency-domain waveform h+ */
1257  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency-domain waveform hx */
1258  const REAL8Sequence *freqs, /**< input Frequency series [Hz] */
1259  REAL8 m1_SI, /**< mass of companion 1 (kg) */
1260  REAL8 m2_SI, /**< mass of companion 2 (kg) */
1261  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1262  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1263  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1264  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1265  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1266  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1267  const REAL8 distance, /**< Distance of source (m) */
1268  const REAL8 inclination, /**< inclination of source (rad) */
1269  const REAL8 phiRef, /**< Orbital phase (rad) at reference frequency */
1270  REAL8 fRef_In, /**< Reference frequency (Hz) */
1271  LALDict *lalParams /**< LAL Dictionary struct */
1272 )
1273  {
1274  UINT4 status = 0;
1275 
1276  const REAL8 m1_SI_init = m1_SI;
1277  const REAL8 m2_SI_init = m2_SI;
1278  const REAL8 chi1z_init = chi1z;
1279  const REAL8 chi2z_init = chi2z;
1280 
1281  /*
1282  Set initial values of masses and z-components of spins to pass to IMRPhenomXSetWaveformVariables() so it can swap the
1283  matter parameters (and masses and spins) appropriately if m1 < m2, since the masses and spin vectors will also be
1284  swapped by XLALIMRPhenomXPCheckMassesAndSpins() below.
1285  */
1286 
1287  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
1288  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
1289 
1290  XLAL_CHECK(freqs != NULL, XLAL_EFAULT, "Error: XLALSimIMRPhenomXPFrequencySequence *freqs is null.\n");
1291 
1292  /* Perform initial sanity checks */
1293  if(*hptilde) { XLAL_CHECK(NULL != hptilde, XLAL_EFAULT); }
1294  if(*hctilde) { XLAL_CHECK(NULL != hctilde, XLAL_EFAULT); }
1295  if(fRef_In < 0.0) { XLAL_ERROR(XLAL_EDOM, "fRef_In must be positive or set to 0 to ignore.\n"); }
1296  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
1297  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
1298  if(distance < 0.0) { XLAL_ERROR(XLAL_EDOM, "Distance must be positive and greater than 0.\n"); }
1299 
1300  /*
1301  Perform a basic sanity check on the region of the parameter space in which model is evaluated. Behaviour is as follows:
1302  - For mass ratios <= 20.0 and spins <= 0.99: no warning messages.
1303  - For 1000 > mass ratio > 20 and spins <= 0.99: print a warning message that we are extrapolating outside of *NR* calibration domain.
1304  - For mass ratios > 1000: throw a hard error that model is not valid.
1305  - For spins > 0.99: throw a warning that we are extrapolating the model to extremal
1306  - At high mass ratios > 20, the NNLO Euler angles may develop known pathologies.
1307  */
1308  REAL8 mass_ratio;
1309  if(m1_SI > m2_SI)
1310  {
1311  mass_ratio = m1_SI / m2_SI;
1312  }
1313  else
1314  {
1315  mass_ratio = m2_SI / m1_SI;
1316  }
1317  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"); }
1318  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
1319  if(sqrt(chi1x*chi1x + chi1y*chi1y + chi1z*chi1z) > 0.99 || sqrt(chi2x*chi2x + chi2y*chi2y + chi2z*chi2z) > 0.99) { XLAL_PRINT_WARNING("Warning: Extrapolating to extremal spins, model is not trusted.\n"); }
1320 
1321  /* If fRef is not provided, then set fRef to be the starting GW Frequency */
1322  const REAL8 fRef = (fRef_In == 0.0) ? freqs->data[0] : fRef_In;
1323 
1324  const REAL8 f_min_In = freqs->data[0];
1325  const REAL8 f_max_In = freqs->data[freqs->length - 1];
1326 
1328  XLAL_CHECK(
1329  (fRef >= f_min_In)&&(fRef <= f_max_In),
1330  XLAL_EFUNC,
1331  "Error: f_min = %.2f <= fRef = %.2f < f_max = %.2f required when using tuned angles.\n",f_min_In,fRef,f_max_In);
1332  }
1333 
1334  /* Use an auxiliar laldict to not overwrite the input argument */
1335  LALDict *lalParams_aux;
1336  /* setup mode array */
1337  if (lalParams == NULL)
1338  {
1339  lalParams_aux = XLALCreateDict();
1340  }
1341  else{
1342  lalParams_aux = XLALDictDuplicate(lalParams);
1343  }
1344 
1345  /*
1346  Passing deltaF = 0 implies that freqs is a frequency grid with non-uniform spacing.
1347  The function waveform then start at lowest given frequency.
1348  */
1350  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
1351 
1352  /* Initialize IMRPhenomX waveform struct and perform sanity check. */
1354  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
1355  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI_init, m2_SI_init, chi1z_init, chi2z_init, 0.0, fRef, phiRef, f_min_In, f_max_In, distance, inclination, lalParams_aux, 0);
1356  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
1357 
1358  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
1360  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
1361 
1362  /* If user chose SpinTaylor angles, set bounds for interpolation of angles */
1363  int pflag = XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams_aux);
1364  if(pflag==310||pflag==311||pflag==320||pflag==321)
1365  {
1366  pPrec->M_MIN = 2, pPrec->M_MAX = 2;
1367  }
1368 
1369 
1371  pWF,
1372  pPrec,
1373  m1_SI,
1374  m2_SI,
1375  chi1x,
1376  chi1y,
1377  chi1z,
1378  chi2x,
1379  chi2y,
1380  chi2z,
1381  lalParams_aux,
1382  PHENOMXDEBUG
1383  );
1384  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetAndSetPrecessionVariables failed.\n");
1385 
1386  /* Now call the core IMRPhenomXP waveform generator */
1387  status = IMRPhenomXPGenerateFD(hptilde, hctilde, freqs, pWF, pPrec, lalParams_aux);
1388  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXASFDCore failed to generate IMRPhenomX waveform.\n");
1389 
1390  LALFree(pPrec);
1391  LALFree(pWF);
1392  XLALDestroyDict(lalParams_aux);
1393 
1394  return XLAL_SUCCESS;
1395  }
1396 
1397  /*
1398  * Adapted from IMRPhenomPv2. Avoids clashes with IMRPhenomD/P namespace.
1399  *
1400  * Function to map LAL parameters
1401  * - masses,
1402  * - 6 spin components
1403  * - phiRef at f_ref
1404  * - inclination at f_ref
1405  *
1406  * Assumed to be in the source frame (L frame):
1407  * - LN vector points in the z direction, i.e. lnhat = (0,0,1)
1408  * - Separation vector n is in the x direction
1409  * - Spherical angles of the line of sight N are (incl,Pi/2-phiRef))
1410  *
1411  * The IMRPhenomXP intrinsic parameters inherit those from IMRPhenomP:
1412  * [chi1_l, chi2_l, chip, thetaJN, alpha0 and phi_aligned]
1413  *
1414  * All input masses and frequencies should be in SI units.
1415  *
1416  */
1418  REAL8 *chi1L, /**< [out] Dimensionless aligned spin on companion 1 */
1419  REAL8 *chi2L, /**< [out] Dimensionless aligned spin on companion 2 */
1420  REAL8 *chi_p, /**< [out] Effective precession parameter: Schmidt, Ohme, Hannam, PRD, 91,024043 (2015) */
1421  REAL8 *thetaJN, /**< [out] Angle between J0 and line of sight (z-direction) */
1422  REAL8 *alpha0, /**< [out] Initial value of alpha angle (azimuthal precession angle) */
1423  REAL8 *phi_aligned, /**< [out] Initial phase to feed the underlying aligned-spin model */
1424  REAL8 *zeta_polarization, /**< [out] Angle to rotate the polarizations */
1425  REAL8 m1_SI, /**< Mass of companion 1 (kg) */
1426  REAL8 m2_SI, /**< Mass of companion 2 (kg) */
1427  REAL8 f_ref, /**< Reference GW frequency (Hz) */
1428  REAL8 phiRef, /**< Reference phase (Hz) */
1429  REAL8 incl, /**< Inclination : angle between LN and the line of sight */
1430  REAL8 chi1x, /**< Initial value of chi1x: dimensionless spin of BH 1 in L frame */
1431  REAL8 chi1y, /**< Initial value of chi1y: dimensionless spin of BH 1 in L frame */
1432  REAL8 chi1z, /**< Initial value of chi1z: dimensionless spin of BH 1 in L frame */
1433  REAL8 chi2x, /**< Initial value of chi2x: dimensionless spin of BH 2 in L frame */
1434  REAL8 chi2y, /**< Initial value of chi2y: dimensionless spin of BH 2 in L frame */
1435  REAL8 chi2z, /**< Initial value of chi2z: dimensionless spin of BH 2 in L frame */
1436  LALDict *lalParams /**< LAL Dictionary */
1437  )
1438  {
1439  /* Perform the usual sanity checks... */
1440  XLAL_CHECK(chi1L != NULL, XLAL_EFAULT);
1441  XLAL_CHECK(chi2L != NULL, XLAL_EFAULT);
1442  XLAL_CHECK(chi_p != NULL, XLAL_EFAULT);
1443  XLAL_CHECK(thetaJN != NULL, XLAL_EFAULT);
1444  XLAL_CHECK(alpha0 != NULL, XLAL_EFAULT);
1445  XLAL_CHECK(phi_aligned != NULL, XLAL_EFAULT);
1446 
1447  XLAL_CHECK(f_ref > 0, XLAL_EDOM, "Error in XLALSimIMRPhenomXPCalculateModelParametersFromSourceFrame: Reference frequency must be positive.\n");
1448  XLAL_CHECK(m1_SI > 0, XLAL_EDOM, "Error in XLALSimIMRPhenomXPCalculateModelParametersFromSourceFrame: m1 must be positive.\n");
1449  XLAL_CHECK(m2_SI > 0, XLAL_EDOM, "Error in XLALSimIMRPhenomXPCalculateModelParametersFromSourceFrame: m2 must be positive.\n");
1450  XLAL_CHECK(fabs(chi1x*chi1x + chi1y*chi1y + chi1z*chi1z) <= 1.0, XLAL_EDOM, "Error in XLALSimIMRPhenomXPCalculateModelParametersFromSourceFrame: |S1/m1^2| must be <= 1.\n");
1451  XLAL_CHECK(fabs(chi2x*chi2x + chi2y*chi2y + chi2z*chi2z) <= 1.0, XLAL_EDOM, "Error in XLALSimIMRPhenomXPCalculateModelParametersFromSourceFrame: |S2/m2^2| must be <= 1.\n");
1452 
1453  /* Use an auxiliar laldict to not overwrite the input argument */
1454  LALDict *lalParams_aux;
1455  /* setup mode array */
1456  if (lalParams == NULL)
1457  {
1458  lalParams_aux = XLALCreateDict();
1459  }
1460  else{
1461  lalParams_aux = XLALDictDuplicate(lalParams);
1462  }
1463 
1464  /* Check if m1 > m2, swap the bodies otherwise. */
1465  INT4 status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
1466  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
1467 
1468 
1469  /* Initialize the useful powers of LAL_PI */
1471  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
1472 
1473  /* Initialize IMRPhenomX Waveform struct and check that it initialized correctly */
1475  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
1476  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1z, chi2z, 0.125, f_ref, phiRef, 30., 1024., 1e6*LAL_PC_SI, incl, lalParams_aux, PHENOMXDEBUG);
1477  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
1478 
1479  /* Initialize IMRPhenomX Precession struct and check that it generated successfully */
1481  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
1482 
1484  pWF,
1485  pPrec,
1486  m1_SI,
1487  m2_SI,
1488  chi1x,
1489  chi1y,
1490  chi1z,
1491  chi2x,
1492  chi2y,
1493  chi2z,
1494  lalParams_aux,
1495  PHENOMXDEBUG
1496  );
1497  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
1498 
1499  if(pPrec->IMRPhenomXPNRUseTunedAngles)
1500  {
1501  /* Generate PNR structs */
1502  IMRPhenomXWaveformStruct *pWF_SingleSpin = NULL;
1503  IMRPhenomXPrecessionStruct *pPrec_SingleSpin = NULL;
1504  IMRPhenomX_PNR_alpha_parameters *alphaParams = NULL;
1505  IMRPhenomX_PNR_beta_parameters *betaParams = NULL;
1506 
1508  &pWF_SingleSpin,
1509  &pPrec_SingleSpin,
1510  &alphaParams,
1511  &betaParams,
1512  pWF,
1513  pPrec,
1514  lalParams);
1515  XLAL_CHECK(
1516  XLAL_SUCCESS == status,
1517  XLAL_EFUNC,
1518  "Error: IMRPhenomX_PNR_PopulateStructs failed!\n");
1519 
1520  REAL8 betaPNR_ref = 0.0;
1521 
1522  REAL8 Mf_ref = pWF->MfRef;
1523  /* generate PNR angles */
1524  REAL8 q = pWF->q;
1525  REAL8 chi = pPrec->chi_singleSpin;
1526 
1527  UINT4 attach_MR_beta = IMRPhenomX_PNR_AttachMRBeta(betaParams);
1528  /* inside calibration region */
1529  if ((q <= pPrec->PNR_q_window_lower) && (chi <= pPrec->PNR_chi_window_lower))
1530  {
1531  /* First check to see if we attach the MR tuning to beta */
1532  if (attach_MR_beta) /* yes we do! */
1533  {
1534  betaPNR_ref = IMRPhenomX_PNR_GeneratePNRBetaAtMf(Mf_ref, betaParams, pWF, pPrec, pWF_SingleSpin, pPrec_SingleSpin);
1535  }
1536  else /* don't attach MR tuning to beta */
1537  {
1538  betaPNR_ref = IMRPhenomX_PNR_GeneratePNRBetaNoMR(Mf_ref, pWF, pPrec);
1539  }
1540  }
1541  /* inside transition region */
1542  else if ((q <= pPrec->PNR_q_window_upper) && (chi <= pPrec->PNR_chi_window_upper))
1543  {
1544  /* First check to see if we attach the MR tuning to beta */
1545  if (attach_MR_beta) /* yes we do! */
1546  {
1547  betaPNR_ref = IMRPhenomX_PNR_GenerateMergedPNRBetaAtMf(Mf_ref, betaParams, pWF, pPrec, pWF_SingleSpin, pPrec_SingleSpin);
1548  }
1549  else /* don't attach MR tuning to beta */
1550  {
1551  betaPNR_ref = IMRPhenomX_PNR_GeneratePNRBetaNoMR(Mf_ref, pWF, pPrec);
1552  }
1553  }
1554  /* fully in outside calibration region */
1555  else
1556  {
1557  betaPNR_ref = IMRPhenomX_PNR_GeneratePNRBetaNoMR(Mf_ref, pWF, pPrec);
1558  }
1559 
1560  status = IMRPhenomX_PNR_RemapThetaJSF(betaPNR_ref, pWF, pPrec, lalParams);
1561  XLAL_CHECK(
1562  XLAL_SUCCESS == status,
1563  XLAL_EFUNC,
1564  "Error: IMRPhenomX_PNR_RemapThetaJSF failed in IMRPhenomX_PNR_GeneratePNRAngles.");
1565 
1566  /* clean this up */
1568  &pWF_SingleSpin,
1569  &pPrec_SingleSpin,
1570  &alphaParams,
1571  &betaParams);
1572  }
1573 
1574  /* Aligned spins */
1575  *chi1L = chi1z; /* Dimensionless aligned spin on BH 1 */
1576  *chi2L = chi2z; /* Dimensionless aligned spin on BH 2 */
1577 
1578  *chi_p = pPrec->chi_p;
1579  *thetaJN = pPrec->thetaJN;
1580  *alpha0 = pPrec->alpha0;
1581  *phi_aligned = pPrec->phi0_aligned;
1582  *zeta_polarization = pPrec->zeta_polarization;
1583 
1584  LALFree(pWF);
1586  // Cleaning up
1587  LALFree(pPrec->pWF22AS);
1588  }
1589  LALFree(pPrec);
1590  XLALDestroyDict(lalParams_aux);
1591 
1592  return status;
1593  }
1594 
1595 
1596  /*
1597  * Prototype wrapper function:
1598  * Driver routine to calculate the MSA Euler angles in the frequency domain.
1599  *
1600  * All input parameters should be in SI units.
1601  *
1602  * Returns \f$\phi_z\f$, \f$\zeta\f$ and \f$\cos \theta_L \f$
1603  */
1605  REAL8Sequence **alpha_of_f, /**< [out] The azimuthal angle of L around J */
1606  REAL8Sequence **gamma_of_f, /**< [out] The third Euler angle describing L with respect to J. Fixed by minmal rotation condition. */
1607  REAL8Sequence **cosbeta_of_f, /**< [out] Cosine of polar angle between L and J */
1608  const REAL8Sequence *freqs, /**< Input Frequency series [Hz] */
1609  REAL8 m1_SI, /**< mass of companion 1 (kg) */
1610  REAL8 m2_SI, /**< mass of companion 2 (kg) */
1611  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1612  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1613  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1614  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1615  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1616  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1617  REAL8 inclination, /**< Inclination : angle between LN and the line of sight */
1618  REAL8 fRef_In, /**< Reference frequency (Hz) */
1619  INT4 mprime, /**< Spherical harmonic order m */
1620  LALDict *lalParams /**< LAL Dictionary struct */
1621 )
1622 {
1623 
1624  UINT4 status = 0;
1625 
1626  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
1627  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
1628 
1629  /* Perform initial sanity checks */
1630  if(fRef_In < 0.0) { XLAL_ERROR(XLAL_EDOM, "fRef_In must be positive or set to 0 to ignore.\n"); }
1631  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
1632  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
1633 
1634  REAL8 chi1L, chi2L;
1635  chi1L = chi1z;
1636  chi2L = chi2z;
1637 
1638  /* If fRef is not provided, then set fRef to be the starting GW Frequency */
1639  const REAL8 fRef = (fRef_In == 0.0) ? freqs->data[0] : fRef_In;
1640 
1641  // const REAL8 f_min_In = freqs->data[0];
1642  // const REAL8 f_max_In = freqs->data[freqs->length - 1];
1643 
1644  /* Use an auxiliar laldict to not overwrite the input argument */
1645  LALDict *lalParams_aux;
1646  /* setup mode array */
1647  if (lalParams == NULL)
1648  {
1649  lalParams_aux = XLALCreateDict();
1650  }
1651  else
1652  {
1653  lalParams_aux = XLALDictDuplicate(lalParams);
1654  }
1655 
1656  /* Initialize IMRPhenomX waveform struct and perform sanity check. */
1658  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
1659  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, 0.0, fRef, 0.0, freqs->data[0], freqs->data[freqs->length-1], 1.0, inclination, lalParams_aux, 0);
1660  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
1661 
1662 
1663  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
1665  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
1666 
1667  int pflag = XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams_aux);
1668  if (pflag == 300) pflag = 223;
1669 
1670  if(pflag != 220 && pflag != 221 && pflag != 222 && pflag != 223 && pflag != 224)
1671  {
1672  XLAL_ERROR(XLAL_EDOM, "Error: MSA system currently only supported for IMRPhenomXPrecVersion 220, 221, 222, 223 or 224.\n");
1673  }
1674 
1676  pWF,
1677  pPrec,
1678  m1_SI,
1679  m2_SI,
1680  chi1x,
1681  chi1y,
1682  chi1z,
1683  chi2x,
1684  chi2y,
1685  chi2z,
1686  lalParams_aux,
1687  PHENOMXDEBUG
1688  );
1689  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetAndSetPrecessionVariables failed.\n");
1690 
1691  REAL8 v = 0.0;
1692  vector vangles = {0.,0.,0.};
1693 
1694  *alpha_of_f = XLALCreateREAL8Sequence(freqs->length);
1695  *gamma_of_f = XLALCreateREAL8Sequence(freqs->length);
1696  *cosbeta_of_f = XLALCreateREAL8Sequence(freqs->length);
1697 
1698  for(UINT4 i = 0; i < freqs->length; i++)
1699  {
1700  // Input list of *gravitational-wave* frequencies not *orbital* frequencies*
1701  v = cbrt( freqs->data[i] * pPrec->piGM * (2.0 / mprime) );
1702  vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(v,pWF,pPrec);
1703 
1704  (*alpha_of_f)->data[i] = vangles.x - pPrec->alpha_offset;
1705  (*gamma_of_f)->data[i] = -(vangles.y - pPrec->epsilon_offset);
1706  (*cosbeta_of_f)->data[i] = vangles.z;
1707  }
1708 
1709  LALFree(pPrec);
1710  LALFree(pWF);
1711  XLALDestroyDict(lalParams_aux);
1712 
1713  return XLAL_SUCCESS;
1714  }
1715 
1716  /*
1717  * Prototype wrapper function:
1718 
1719  * Driver routine to calculate the NNLO PN Euler angles in the frequency domain.
1720  *
1721  * All input parameters should be in SI units.
1722  *
1723  * Returns \f$\alpha\f$, \f$\cos \beta\f$ and \f$\gamma\f$
1724  */
1726  REAL8Sequence **alpha_of_f, /**< [out] Azimuthal angle of L w.r.t J */
1727  REAL8Sequence **gamma_of_f, /**< [out] Third Euler angle describing L w.r.t J, fixed by minimal rotation condition */
1728  REAL8Sequence **cosbeta_of_f, /**< [out] Cosine of polar angle between L and J */
1729  const REAL8Sequence *freqs, /**< Input Frequency series [Hz] */
1730  REAL8 m1_SI, /**< mass of companion 1 (kg) */
1731  REAL8 m2_SI, /**< mass of companion 2 (kg) */
1732  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1733  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1734  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
1735  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1736  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1737  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
1738  REAL8 inclination, /**< Inclination : angle between LN and the line of sight */
1739  REAL8 fRef_In, /**< Reference frequency (Hz) */
1740  INT4 mprime, /**< Spherical harmonic order m */
1741  LALDict *lalParams /**< LAL Dictionary struct */
1742 )
1743 {
1744 
1745  UINT4 status = 0;
1746 
1747  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
1748  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
1749 
1750  #if PHENOMXPDEBUG == 1
1751  printf("\nm1 : %.6f\n",m1_SI);
1752  printf("m2 : %.6f\n",m2_SI);
1753  printf("chi1x : %.6f\n",chi1x);
1754  printf("chi1y : %.6f\n",chi1y);
1755  printf("chi1z : %.6f\n",chi1z);
1756  printf("chi2x : %.6f\n",chi2x);
1757  printf("chi2y : %.6f\n",chi2y);
1758  printf("chi2z : %.6f\n\n",chi2z);
1759  #endif
1760 
1761  /* Perform initial sanity checks */
1762  if(fRef_In < 0.0) { XLAL_ERROR(XLAL_EDOM, "fRef_In must be positive or set to 0 to ignore.\n"); }
1763  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
1764  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
1765 
1766  REAL8 chi1L, chi2L;
1767  chi1L = chi1z;
1768  chi2L = chi2z;
1769 
1770  /* If fRef is not provided, then set fRef to be the starting GW Frequency */
1771  const REAL8 fRef = (fRef_In == 0.0) ? freqs->data[0] : fRef_In;
1772 
1773  // const REAL8 f_min_In = freqs->data[0];
1774  // const REAL8 f_max_In = freqs->data[freqs->length - 1];
1775 
1776  /* Use an auxiliar laldict to not overwrite the input argument */
1777  LALDict *lalParams_aux;
1778  /* setup mode array */
1779  if (lalParams == NULL)
1780  {
1781  lalParams_aux = XLALCreateDict();
1782  }
1783  else
1784  {
1785  lalParams_aux = XLALDictDuplicate(lalParams);
1786  }
1787 
1788  /* Initialize IMRPhenomX waveform struct and perform sanity check. */
1790  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
1791  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, 0.0, fRef, 0.0, freqs->data[0], freqs->data[freqs->length-1], 1.0, inclination, lalParams_aux, 0);
1792  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
1793 
1794  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
1796  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
1797 
1798  /* The precessing prescription needs to be NNLO */
1799  if (XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(lalParams_aux) > 200 ){
1801  }
1802 
1803 
1805  pWF,
1806  pPrec,
1807  m1_SI,
1808  m2_SI,
1809  chi1x,
1810  chi1y,
1811  chi1z,
1812  chi2x,
1813  chi2y,
1814  chi2z,
1815  lalParams_aux,
1816  PHENOMXDEBUG
1817  );
1818  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetAndSetPrecessionVariables failed.\n");
1819 
1820  /* Spin variable used to calculate \cos \beta */
1821  REAL8 s, s2;
1822 
1823  /* Orbital velocity and orbital frequency */
1824  REAL8 v, omega, logomega, omega_cbrt, omega_cbrt2;
1825 
1826  /* PN Orbital angular momenta */
1827  REAL8 L = 0.0;
1828 
1829  *alpha_of_f = XLALCreateREAL8Sequence(freqs->length);
1830  *gamma_of_f = XLALCreateREAL8Sequence(freqs->length);
1831  *cosbeta_of_f = XLALCreateREAL8Sequence(freqs->length);
1832 
1833  for(UINT4 i = 0; i < freqs->length; i++)
1834  {
1835  /* Orbital frequency and velocity */
1836  omega = freqs->data[i] * pPrec->piGM * (2.0 / mprime);
1837  logomega = log(omega);
1838  omega_cbrt = cbrt(omega);
1839  omega_cbrt2 = omega_cbrt * omega_cbrt;
1840  v = omega_cbrt;
1841 
1842  L = XLALSimIMRPhenomXLPNAnsatz(v, pWF->eta/v, pPrec->L0, pPrec->L1, pPrec->L2, pPrec->L3, pPrec->L4, pPrec->L5, pPrec->L6, pPrec->L7, pPrec->L8, pPrec->L8L);
1843 
1844  (*alpha_of_f)->data[i] = IMRPhenomX_PN_Euler_alpha_NNLO(pPrec,omega,omega_cbrt2,omega_cbrt,logomega);
1845 
1846  /* \gamma = - \epsilon */
1847  (*gamma_of_f)->data[i] = -IMRPhenomX_PN_Euler_epsilon_NNLO(pPrec,omega,omega_cbrt2,omega_cbrt,logomega);
1848 
1849  s = pPrec->Sperp / (L + pPrec->SL);
1850  s2 = s*s;
1851 
1852  (*cosbeta_of_f)->data[i] = copysign(1.0, L + pPrec->SL) / sqrt(1.0 + s2);
1853  }
1854 
1855  LALFree(pPrec);
1856  LALFree(pWF);
1857  XLALDestroyDict(lalParams_aux);
1858 
1859  return XLAL_SUCCESS;
1860  }
1861 
1862  /** @} */
1863  /** @} */
1864 
1865  /* *********************************************************************************
1866  *
1867  * The following private function generates an IMRPhenomXP frequency-domain waveform
1868  * - Precessing spins
1869  * - Only the 22 mode in the co-precessing frame
1870  * - Physical parameters are passed via the waveform struct
1871  * *********************************************************************************
1872  */
1874  COMPLEX16FrequencySeries **hptilde, /**< [out] FD waveform */
1875  COMPLEX16FrequencySeries **hctilde, /**< [out] FD waveform */
1876  const REAL8Sequence *freqs_In, /**< Input frequency grid */
1877  IMRPhenomXWaveformStruct *pWF, /**< IMRPhenomX Waveform Struct */
1878  IMRPhenomXPrecessionStruct *pPrec, /**< IMRPhenomXP Waveform Struct */
1879  LALDict *lalParams /**< LAL Dictionary Structure */
1880 )
1881 {
1882  #if PHENOMXPDEBUG == 1
1883  printf("\n **** Now in IMRPhenomXPGenerateFD... **** \n");
1884  #endif
1885 
1886  /* Set tidal version */
1887  NRTidal_version_type NRTidal_version ;
1888  NRTidal_version=IMRPhenomX_SetTidalVersion(lalParams);
1889 
1890  /* Set LIGOTimeGPS */
1891  LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO; // = {0,0}
1892 
1893  /* Initialize useful powers of LAL_PI */
1895  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
1896 
1897  /* Inherit minimum and maximum frequencies to generate wavefom from input frequency grid */
1898  double f_min = freqs_In->data[0];
1899  double f_max = freqs_In->data[freqs_In->length - 1];
1900 
1901  /* Size of array */
1902  size_t npts = 0;
1903 
1904  /* Index shift between freqs and the frequency series */
1905  UINT4 offset = 0;
1906 
1907  /* Initialize frequency sequence */
1908  REAL8Sequence *freqs = NULL;
1909 
1910  /* Initialize tidal corrections (following tidal code modified from LALSimIMRPhenomP.c) */
1911  REAL8Sequence *phi_tidal = NULL;
1912  REAL8Sequence *amp_tidal = NULL;
1913  REAL8Sequence *planck_taper = NULL;
1914 
1915  /* Set matter parameters (set to zero in pWF if NRTidal additions are not turned on) */
1916  REAL8 lambda1 = pWF->lambda1;
1917  REAL8 lambda2 = pWF->lambda2;
1918 
1919  /* New variables needed for the NRTidalv2 model */
1920  REAL8 X_A = pWF->m1; // Already scaled by Mtot
1921  REAL8 X_B = pWF->m2; // Ibid.
1922  REAL8 pfaN = 3./(128.*X_A*X_B);
1923 
1924  /* If deltaF is non-zero then we need to generate a uniformly sampled frequency grid of spacing deltaF. Start at f = 0. */
1925  if(pWF->deltaF > 0)
1926  {
1927  /* Return the closest power of 2 */
1928  npts = (size_t) (f_max / pWF->deltaF) + 1;
1929 
1930  /* Debug information */
1931  #if PHENOMXPDEBUG == 1
1932  printf("npts = %zu\n",npts);
1933  printf("fMin = %.4f\n",f_min);
1934  printf("fMax = %.4f\n",f_max);
1935  printf("dF = %.4f\n",pWF->deltaF);
1936  #endif
1937 
1938  /* Coalescence time is fixed to t=0, shift by overall length in time. */
1939  XLAL_CHECK(XLALGPSAdd(&ligotimegps_zero, -1. / pWF->deltaF), XLAL_EFUNC, "Failed to shift the coalescence time to t=0. Tried to apply a shift of -1/df with df = %g.\n", pWF->deltaF);
1940 
1941  /* Initialize the htilde frequency series */
1942  *hptilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform",&ligotimegps_zero,0.0,pWF->deltaF,&lalStrainUnit,npts);
1943  *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform",&ligotimegps_zero,0.0,pWF->deltaF,&lalStrainUnit,npts);
1944 
1945  /* Check that frequency series generated okay */
1946  XLAL_CHECK(*hptilde,XLAL_ENOMEM,"Failed to allocate COMPLEX16FrequencySeries h_+ of length %zu for f_max = %f, deltaF = %g.\n",npts,f_max,pWF->deltaF);
1947  XLAL_CHECK(*hctilde,XLAL_ENOMEM,"Failed to allocate COMPLEX16FrequencySeries h_x of length %zu for f_max = %f, deltaF = %g.\n",npts,f_max,pWF->deltaF);
1948 
1949  /* Frequencies will be set using only the lower and upper bounds that we passed */
1950  size_t iStart = (size_t) (f_min / pWF->deltaF);
1951  size_t iStop = (size_t) (f_max / pWF->deltaF) + 1;
1952 
1953  XLAL_CHECK ( (iStop <= npts) && (iStart <= iStop), XLAL_EDOM,
1954  "minimum freq index %zu and maximum freq index %zu do not fulfill 0<=ind_min<=ind_max<=htilde->data>length=%zu.\n", iStart, iStop, npts);
1955 
1956  /* Allocate memory for frequency array and terminate if this fails */
1957  freqs = XLALCreateREAL8Sequence(iStop - iStart);
1958  if (!freqs)
1959  {
1960  XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
1961  }
1962 
1963  /* Populate frequency array */
1964  for (UINT4 i = iStart; i < iStop; i++)
1965  {
1966  freqs->data[i-iStart] = i * pWF->deltaF;
1967  }
1968  offset = iStart;
1969  }
1970  else
1971  {
1972  /* freqs is a frequency grid with non-uniform spacing, so we start at the lowest given frequency */
1973  npts = freqs_In->length;
1974  *hptilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &ligotimegps_zero, f_min, pWF->deltaF, &lalStrainUnit, npts);
1975  *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &ligotimegps_zero, f_min, pWF->deltaF, &lalStrainUnit, npts);
1976 
1977  XLAL_CHECK (*hptilde, XLAL_ENOMEM, "Failed to allocated waveform COMPLEX16FrequencySeries h_+ of length %zu from sequence.\n", npts);
1978  XLAL_CHECK (*hctilde, XLAL_ENOMEM, "Failed to allocated waveform COMPLEX16FrequencySeries h_x of length %zu from sequence.\n", npts);
1979 
1980  offset = 0;
1981  freqs = XLALCreateREAL8Sequence(freqs_In->length);
1982 
1983  /* Allocate memory for frequency array and terminate if this fails */
1984  if (!freqs)
1985  {
1986  XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
1987  }
1988 
1989  /* Populate frequency array */
1990  for (UINT4 i = 0; i < freqs_In->length; i++)
1991  {
1992  freqs->data[i] = freqs_In->data[i];
1993  }
1994  }
1995 
1996  memset((*hptilde)->data->data, 0, npts * sizeof(COMPLEX16));
1997  XLALUnitMultiply(&((*hptilde)->sampleUnits), &((*hptilde)->sampleUnits), &lalSecondUnit);
1998 
1999  memset((*hctilde)->data->data, 0, npts * sizeof(COMPLEX16));
2000  XLALUnitMultiply(&((*hctilde)->sampleUnits), &((*hctilde)->sampleUnits), &lalSecondUnit);
2001 
2002  /* Check if LAL dictionary exists. If not, create a LAL dictionary. */
2003  UNUSED INT4 lalParams_In = 0;
2004  if(lalParams == NULL)
2005  {
2006  lalParams_In = 1;
2007  lalParams = XLALCreateDict();
2008  }
2009 
2010  #if PHENOMXPDEBUG == 1
2011  printf("\n\n **** Initializing amplitude struct... **** \n\n");
2012  #endif
2013 
2014  // numerical angles;
2015  REAL8Sequence *alpha = NULL;
2016  REAL8Sequence *gamma = NULL;
2017  REAL8Sequence *cosbeta = NULL;
2018 
2019  if(pPrec->precessing_tag==3){
2020 
2021  status = IMRPhenomXPSpinTaylorAnglesIMR(&alpha,&cosbeta,&gamma,freqs,pWF,pPrec,lalParams);
2022  XLAL_CHECK(status == XLAL_SUCCESS, XLAL_EFUNC, "IMRPhenomXPSpinTaylorAnglesIMR failed.");
2023 
2024  }
2025 
2026 
2027  /* Allocate and initialize the PhenomX 22 amplitude coefficients struct */
2028  IMRPhenomXAmpCoefficients *pAmp22;
2029  pAmp22 = XLALMalloc(sizeof(IMRPhenomXAmpCoefficients));
2031  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetAmplitudeCoefficients failed.\n");
2032 
2033  #if PHENOMXPDEBUG == 1
2034  printf("\n\n **** Amplitude struct initialized. **** \n\n");
2035  printf("\n\n **** Initializing phase struct... **** \n\n");
2036  #endif
2037 
2038  /* Allocate and initialize the PhenomX 22 phase coefficients struct */
2039  IMRPhenomXPhaseCoefficients *pPhase22;
2040  pPhase22 = XLALMalloc(sizeof(IMRPhenomXPhaseCoefficients));
2041  status = IMRPhenomXGetPhaseCoefficients(pWF,pPhase22);
2042  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXGetPhaseCoefficients failed.\n");
2043  if(NRTidal_version!=NoNRT_V) IMRPhenomXGetTidalPhaseCoefficients(pWF,pPhase22,NRTidal_version);
2044 
2045  #if PHENOMXPDEBUG == 1
2046  printf("\n\n **** Phase struct initialized. **** \n\n");
2047  #endif
2048 
2049  /* Initialize a struct containing useful powers of Mf at fRef */
2050  IMRPhenomX_UsefulPowers powers_of_MfRef;
2051  status = IMRPhenomX_Initialize_Powers(&powers_of_MfRef,pWF->MfRef);
2052  XLAL_CHECK(XLAL_SUCCESS == status, status, "IMRPhenomX_Initialize_Powers failed for MfRef.\n");
2053 
2054  /*
2055  Time shift so peak amplitude is near t ~ 0.
2056  */
2057 
2058  /* Linear time and phase shifts so that model peaks near t ~ 0 */
2059  /* lina provides an additional phase shift, which we currently don't use, but we keep */
2060  /* the variable in case we change our mind about phase alignement in the future */
2061  REAL8 lina = 0;
2062 
2063  /* Get phase connection coefficients = phase offsets between calibration regions to achieve C^1 phase */
2064 
2066 
2067  /* Apply time shift, see IMRPhenomX_TimeShift_22 for details of current implementation */
2068  double linb = IMRPhenomX_TimeShift_22(pPhase22, pWF);
2069  // extra contribution to phi(fRef) due to tidal corrections
2070  double phiTfRef = 0.;
2071 
2072  // correct for time and phase shifts due to tidal phase
2073  REAL8 f_final=freqs->data[freqs->length-1];
2074 
2075  if(NRTidal_version!=NoNRT_V){
2076  REAL8 f_merger = XLALSimNRTunedTidesMergerFrequency(pWF->Mtot, pWF->kappa2T, pWF->q);
2077  if(f_merger<f_final)
2078  f_final = f_merger;
2079 
2080  IMRPhenomX_UsefulPowers powers_of_ffinal;
2081  REAL8 Mf_final = f_final*pWF->M_sec;
2082  status = IMRPhenomX_Initialize_Powers(&powers_of_ffinal,Mf_final);
2083  XLAL_CHECK(XLAL_SUCCESS == status, status, "IMRPhenomX_Initialize_Powers failed for f_final.\n");
2084  REAL8 dphi_fmerger=1/pWF->eta*IMRPhenomX_dPhase_22(Mf_final, &powers_of_ffinal, pPhase22, pWF)+linb-IMRPhenomX_TidalPhaseDerivative(&powers_of_ffinal, pWF, pPhase22, NRTidal_version);
2085  REAL8 tshift = -dphi_fmerger;
2086  linb+=tshift;
2087  // tidal phase will be subtracted from the BBH phase
2088  phiTfRef = -IMRPhenomX_TidalPhase(&powers_of_MfRef, pWF, pPhase22, NRTidal_version);
2089  }
2090 
2091 
2092  /* Inverse of the symmetric mass ratio */
2093  REAL8 inveta = (1.0 / pWF->eta);
2094 
2095  /* Construct reference phase, see discussion in Appendix A of arXiv:2001.11412 */
2096  pWF->phifRef = -(inveta * IMRPhenomX_Phase_22(pWF->MfRef, &powers_of_MfRef, pPhase22, pWF) + linb*pWF->MfRef + lina+phiTfRef) + 2.0*pWF->phi0 + LAL_PI_4;
2097 
2098  /*
2099  Here we declare explicit REAL8 variables for main loop in order to avoid numerous
2100  pointer calls.
2101  */
2102  REAL8 C1IM = pPhase22->C1Int;
2103  REAL8 C2IM = pPhase22->C2Int;
2104  REAL8 C1RD = pPhase22->C1MRD;
2105  REAL8 C2RD = pPhase22->C2MRD;
2106 
2107  REAL8 fPhaseIN = pPhase22->fPhaseMatchIN;
2108  REAL8 fPhaseIM = pPhase22->fPhaseMatchIM;
2109  REAL8 fAmpIN = pAmp22->fAmpMatchIN;
2110  REAL8 fAmpIM = pAmp22->fAmpRDMin;
2111 
2112  /* add in PNR-specific code */
2113 
2114  int PNRUseTunedAngles = pPrec->IMRPhenomXPNRUseTunedAngles;
2115 
2116  REAL8Sequence* alphaPNR = NULL;
2117  REAL8Sequence* betaPNR = NULL;
2118  REAL8Sequence* gammaPNR = NULL;
2119 
2120  if(PNRUseTunedAngles) /* Check for PNR angles */
2121  {
2122  alphaPNR = XLALCreateREAL8Sequence(freqs->length);
2123  if (!alphaPNR)
2124  {
2125  XLAL_ERROR(XLAL_EFUNC, "alphaPNR array allocation failed in LALSimIMRPhenomX.c");
2126  }
2127  betaPNR = XLALCreateREAL8Sequence(freqs->length);
2128  if (!betaPNR)
2129  {
2130  XLAL_ERROR(XLAL_EFUNC, "betaPNR array allocation failed in LALSimIMRPhenomX.c");
2131  }
2132  gammaPNR = XLALCreateREAL8Sequence(freqs->length);
2133  if (!gammaPNR)
2134  {
2135  XLAL_ERROR(XLAL_EFUNC, "gammaPNR array allocation failed in LALSimIMRPhenomX.c");
2136  }
2137 
2139  alphaPNR, betaPNR, gammaPNR,
2140  freqs, pWF, pPrec,
2141  lalParams);
2142  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNR_GeneratePNRAngles failed.\n");
2143 
2144  #if DEBUG == 1
2145  // Save angles into a file
2146  FILE *fileangle2;
2147  char fileSpec2[40];
2148  sprintf(fileSpec2, "angles_PNR.dat");
2149 
2150  printf("\nOutput angle file: %s\r\n", fileSpec2);
2151  fileangle2 = fopen(fileSpec2,"w");
2152 
2153  fprintf(fileangle2,"# q = %.16e m1 = %.16e m2 = %.16e chi1 = %.16e chi2 = %.16e Mtot = %.16e distance = %.16e\n", pWF->q, pWF->m1, pWF->m2, pWF->chi1L, pWF->chi2L, pWF->Mtot, pWF->distance/LAL_PC_SI/1e6);
2154  fprintf(fileangle2,"#fHz alpha beta gamma\n");
2155 
2156  for (UINT4 idx = 0; idx < freqs->length; idx++)
2157  {
2158  fprintf(fileangle2, "%.16e %.16e %.16e %.16e\n", freqs->data[idx], alphaPNR->data[idx], betaPNR->data[idx], gammaPNR->data[idx]);
2159  }
2160  fclose(fileangle2);
2161  #endif
2162  }
2163 
2164  #if PHENOMXPDEBUG == 1
2165  printf("\n\n **** Phase struct initialized. **** \n\n");
2166  printf("C1IM = %.4f\n",C1IM);
2167  printf("C2IM = %.4f\n",C2IM);
2168  printf("C1RD = %.4f\n",C1RD);
2169  printf("C2RD = %.4f\n",C2RD);
2170  printf("fIN = %.4f\n",fPhaseIN);
2171  printf("fIM = %.4f\n",fPhaseIM);
2172  printf("thetaJN = %.4f\n",pPrec->thetaJN);
2173  #endif
2174 
2175  /* amplitude definition as in XAS */
2176 
2177  REAL8 Amp0 = pWF->amp0 * pWF->ampNorm;
2178 
2179 
2180 
2181  /* initial_status used to track */
2182  UINT4 initial_status = XLAL_SUCCESS;
2183 
2184  /* Approximate frequency of the peak */
2185  REAL8 fmax = pAmp22->fAmpRDMin;
2186 
2187  /* Initialize a struct containing useful powers of fmax */
2188  IMRPhenomX_UsefulPowers powers_of_fmax;
2189  initial_status = IMRPhenomX_Initialize_Powers(&powers_of_fmax,fmax);
2190  if(initial_status != XLAL_SUCCESS)
2191  {
2192  status = initial_status;
2193  XLALPrintError("IMRPhenomX_Initialize_Powers failed for fmax, initial_status=%d",initial_status);
2194  }
2195 
2196  #if DEBUG == 1
2197  // Save anles into a file
2198  FILE *fileangle;
2199  char fileSpec[40];
2200  sprintf(fileSpec, "angles_XP.dat");
2201 
2202  printf("\nOutput angle file: %s\r\n", fileSpec);
2203  fileangle = fopen(fileSpec,"w");
2204 
2205  fprintf(fileangle,"# q = %.16e m1 = %.16e m2 = %.16e chi1 = %.16e chi2 = %.16e Mtot = %.16e distance = %.16e\n", pWF->q, pWF->m1, pWF->m2, pWF->chi1L, pWF->chi2L, pWF->Mtot, pWF->distance/LAL_PC_SI/1e6);
2206  fprintf(fileangle,"#fHz cexp_i_alpha(re im) cexp_i_epsilon(re im) cexp_i_betah(re im)\n");
2207 
2208  fclose(fileangle);
2209  #endif
2210 
2211  if (NRTidal_version == NRTidal_V || NRTidal_version == NRTidalv2_V) {
2212  int ret = 0;
2213  UINT4 L_fCut = freqs->length;
2214  phi_tidal = XLALCreateREAL8Sequence(L_fCut);
2215  amp_tidal = XLALCreateREAL8Sequence(L_fCut);
2216  planck_taper = XLALCreateREAL8Sequence(L_fCut);
2217  /* Get FD tidal phase correction and amplitude factor */
2218  ret = XLALSimNRTunedTidesFDTidalPhaseFrequencySeries(phi_tidal, amp_tidal, planck_taper, freqs, pWF->m1_SI, pWF->m2_SI, lambda1, lambda2, NRTidal_version);
2219  XLAL_CHECK(XLAL_SUCCESS == ret, ret, "XLALSimNRTunedTidesFDTidalPhaseFrequencySeries Failed.");
2220  }
2221 
2222 
2223  int AntisymmetricWaveform = pPrec->IMRPhenomXAntisymmetricWaveform;
2224 
2225  /** declare all variables used in anti-symmetric waveform calculation*/
2226  REAL8Sequence *kappa = NULL;
2227  REAL8 A0 = 0.0;
2228  REAL8 phi_A0 = 0.0;
2229  REAL8 phi_B0 = 0.0;
2230 
2231  REAL8 phi_antiSym = 0.0;
2232  REAL8 amp_antiSym = 0.0;
2233  double MfT = 0.85 * pWF->fRING; /* note that fRING is already in dimensionaless units */
2234 
2235  if(AntisymmetricWaveform) /* Check for antisymmetric waveform flag */
2236  {
2237  if (!PNRUseTunedAngles)
2238  {
2239  XLAL_ERROR(XLAL_EFUNC, "Error: Antisymmetric waveform generation not supported without PNR angles, please turn on PNR angles to produce waveform with asymmetries in the (2,2) and (2,-2) modes\n");
2240  }
2241  else
2242  {
2243  kappa = XLALCreateREAL8Sequence(freqs->length);
2244  if (!kappa)
2245  {
2246  XLAL_ERROR(XLAL_EFUNC, "antisymmetric waveform amplitude ratio array allocation failed in LALSimIMRPhenomX.c");
2247  }
2248  status = IMRPhenomX_PNR_GenerateAntisymmetricAmpRatio(kappa, freqs, pWF, pPrec);
2249  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomX_PNRAntisymmetricAmpRatio failed.\n");
2250 
2252  &A0, &phi_A0, &phi_B0, MfT, lina, linb, inveta, pWF,pPrec,pPhase22);
2253 
2254  }
2255  }
2256 
2257  /* Now loop over frequencies to generate waveform: h(f) = A(f) * Exp[I phi(f)] */
2258  #pragma omp parallel for
2259  for (UINT4 idx = 0; idx < freqs->length; idx++)
2260  {
2261  double Mf = pWF->M_sec * freqs->data[idx];
2262  UINT4 jdx = idx + offset;
2263 
2264  COMPLEX16 hcoprec = 0.0; /* Co-precessing waveform */
2265  COMPLEX16 hcoprec_antiSym = 0.0; /* Co-precessing anti-symmetric waveform */
2266  COMPLEX16 hplus = 0.0; /* h_+ */
2267  COMPLEX16 hcross = 0.0; /* h_x */
2268 
2269  /* We do not want to generate the waveform at frequencies > f_max (default = 0.3 Mf) */
2270  if(Mf <= (pWF->f_max_prime * pWF->M_sec))
2271  {
2272  /* Initialize a struct containing useful powers of Mf */
2273  IMRPhenomX_UsefulPowers powers_of_Mf;
2274  initial_status = IMRPhenomX_Initialize_Powers(&powers_of_Mf,Mf);
2275  if(initial_status != XLAL_SUCCESS)
2276  {
2277  status = initial_status;
2278  XLALPrintError("IMRPhenomX_Initialize_Powers failed for Mf, initial_status=%d\n",initial_status);
2279  }
2280  else
2281  {
2282  /* Generate amplitude and phase at Mf */
2283 
2284  // initialize amplitude and phase
2285  REAL8 amp = 0.0;
2286  REAL8 phi = 0.0;
2287 
2288  /* Here we explicitly call the functions which treat the non-overlapping */
2289  /* inspiral, intermediate and ringdown frequency regions for the non-precessing waveform. */
2290 
2291  /* Get phase */
2292  if(Mf < fPhaseIN)
2293  {
2294  phi = IMRPhenomX_Inspiral_Phase_22_AnsatzInt(Mf, &powers_of_Mf, pPhase22);
2295  }
2296  else if(Mf > fPhaseIM)
2297  {
2298  phi = IMRPhenomX_Ringdown_Phase_22_AnsatzInt(Mf, &powers_of_Mf, pWF, pPhase22) + C1RD + (C2RD * Mf);
2299  }
2300  else
2301  {
2302  phi = IMRPhenomX_Intermediate_Phase_22_AnsatzInt(Mf, &powers_of_Mf, pWF, pPhase22) + C1IM + (C2IM * Mf);
2303  }
2304  /* Scale phase by 1/eta and apply phase and time shifts */
2305  phi *= inveta;
2306  phi += linb*Mf + lina + pWF->phifRef;
2307 
2308  /* Get amplitude */
2309  if(Mf < fAmpIN)
2310  {
2311  amp = IMRPhenomX_Inspiral_Amp_22_Ansatz(Mf, &powers_of_Mf, pWF, pAmp22);
2312  }
2313  else if(Mf > fAmpIM)
2314  {
2315  amp = IMRPhenomX_Ringdown_Amp_22_Ansatz(Mf, pWF, pAmp22);
2316  }
2317  else
2318  {
2319  amp = IMRPhenomX_Intermediate_Amp_22_Ansatz(Mf, &powers_of_Mf, pWF, pAmp22);
2320  }
2321 
2322  /* Add NRTidal phase, if selected, code adapted from LALSimIMRPhenomP.c */
2323 
2324  if (NRTidal_version == NRTidal_V || NRTidal_version == NRTidalv2_V) {
2325 
2326  REAL8 phaseTidal = phi_tidal->data[idx];
2327  double ampTidal = amp_tidal->data[idx];
2328  double window = planck_taper->data[idx];
2329 
2330  /* Add spin-induced quadrupole moment terms to tidal phasing */
2331 
2332  /* 2PN terms */
2333  phaseTidal += pfaN * pPhase22->c2PN_tidal* powers_of_lalpi.m_one_third * powers_of_Mf.m_one_third;
2334  /* 3PN terms */
2335  phaseTidal += pfaN * pPhase22->c3PN_tidal* powers_of_lalpi.one_third* powers_of_Mf.one_third;
2336 
2337  /* 3.5PN terms are only in NRTidalv2 */
2338  if (NRTidal_version == NRTidalv2_V) {
2339  phaseTidal += pfaN * pPhase22->c3p5PN_tidal * powers_of_lalpi.two_thirds * powers_of_Mf.two_thirds;
2340  }
2341 
2342  /* Waveform in co-precessing frame with NRTidal terms included: h(f) = [A(f) + A_tidal(f)] * Exp{I [phi(f) - phi_tidal(f)]} * window(f) */
2343  hcoprec = pWF->amp0 * (pWF->ampNorm * powers_of_Mf.m_seven_sixths * amp + 2*sqrt(1/5.)*powers_of_lalpi.sqrt * ampTidal) * cexp(I * (phi - phaseTidal)) * window;
2344  }
2345 
2346  else if (NRTidal_version == NoNRT_V) {
2347  /* Waveform in co-precessing frame: h(f) = A(f) * Exp[I phi(f)] */
2348  hcoprec = Amp0 * powers_of_Mf.m_seven_sixths * amp * cexp(I * phi);
2349  }
2350 
2351  else {
2352  XLAL_PRINT_INFO("Warning: Only NRTidal, NRTidalv2, and NoNRT NRTidal_version values allowed and NRTidal is not implemented completely in IMRPhenomX*.");
2353  }
2354 
2355  /* Transform modes from co-precessing frame to inertial frame */
2356  /* Only do this if the coprecessing model is not desired */
2357  if( pWF->IMRPhenomXReturnCoPrec )
2358  {
2359  //
2360  hplus = 0.5 * (hcoprec);
2361  hcross = -0.5 * I * (hcoprec);
2362 
2363  }
2364  else
2365  {
2366 
2367  if(PNRUseTunedAngles) /* Look for PNR flag */
2368  {
2369  pPrec->alphaPNR = alphaPNR->data[idx];
2370  pPrec->betaPNR = betaPNR->data[idx];
2371  pPrec->gammaPNR = gammaPNR->data[idx];
2372  }
2373 
2374 
2375  if(pPrec->precessing_tag==3)
2376  IMRPhenomXPTwistUp22_NumericalAngles(hcoprec, alpha->data[idx], cosbeta->data[idx], gamma->data[idx], pPrec, &hplus, &hcross);
2377  else
2378  IMRPhenomXPTwistUp22(Mf,hcoprec,pWF,pPrec,&hplus,&hcross);
2379 
2380  /********************************/
2381  /*** anti-symmetric waveform ***/
2382  /*******************************/
2383 
2384  if(AntisymmetricWaveform && PNRUseTunedAngles)
2385  {
2386  /* phase of anti-symmetric waveform*/
2387  if(Mf < MfT)
2388  {
2389  phi_antiSym = phi/2 + alphaPNR->data[idx] + A0 *Mf + phi_A0;
2390  }
2391  else
2392  {
2393  phi_antiSym = phi + phi_B0;
2394  }
2395 
2396  COMPLEX16 hplus_antiSym = 0.0;
2397  COMPLEX16 hcross_antiSym = 0.0;
2398  amp_antiSym = cabs(hcoprec)*kappa->data[idx];
2399  hcoprec_antiSym = amp_antiSym* cexp(I * phi_antiSym);
2400  pPrec->PolarizationSymmetry = -1;
2401  IMRPhenomXPTwistUp22(Mf,hcoprec_antiSym,pWF,pPrec,&hplus_antiSym, &hcross_antiSym);
2402  pPrec->PolarizationSymmetry = 1;
2403  hplus += hplus_antiSym;
2404  hcross += hcross_antiSym;
2405  }
2406  }
2407 
2408 
2409  /* Populate h_+ and h_x */
2410  ((*hptilde)->data->data)[jdx] = hplus;
2411  ((*hctilde)->data->data)[jdx] = hcross;
2412  }
2413  }
2414  else
2415  {
2416  /* Mf > Mf_max, so return 0 */
2417  ((*hptilde)->data->data)[jdx] = 0.0 + I*0.0;
2418  ((*hctilde)->data->data)[jdx] = 0.0 + I*0.0;
2419  }
2420  }
2421 
2423 XLALDestroyREAL8Sequence(cosbeta);
2425 
2426 
2427  if(pPrec->precessing_tag==3)
2428  {
2429  LALFree(pPrec->alpha_params);
2430  LALFree(pPrec->beta_params);
2431 
2432  gsl_spline_free(pPrec->alpha_spline);
2433  gsl_spline_free(pPrec->cosbeta_spline);
2434  gsl_spline_free(pPrec->gamma_spline);
2435 
2436  gsl_interp_accel_free(pPrec->alpha_acc);
2437  gsl_interp_accel_free(pPrec->gamma_acc);
2438  gsl_interp_accel_free(pPrec->cosbeta_acc);
2439 
2440  }
2441 
2442 
2443  /*
2444  Loop over h+ and hx and rotate waveform by 2 \zeta.
2445 
2446  Note that we do this here rather than in LALSimInpsiral.c as
2447  \zeta_polarization is part of the pPrec struct.
2448 
2449  */
2450  COMPLEX16 PhPpolp, PhPpolc;
2451  REAL8 cosPolFac, sinPolFac;
2452 
2453  /* If \zeta is non-zero, we need to rotate h+ and hx */
2454  if(fabs(pPrec->zeta_polarization) > 0.0)
2455  {
2456  cosPolFac = cos(2.0 * pPrec->zeta_polarization);
2457  sinPolFac = sin(2.0 * pPrec->zeta_polarization);
2458 
2459  for (UINT4 i = 0; i < (*hptilde)->data->length; i++)
2460  {
2461  PhPpolp = (*hptilde)->data->data[i];
2462  PhPpolc = (*hctilde)->data->data[i];
2463 
2464  (*hptilde)->data->data[i] = (cosPolFac * PhPpolp) + (sinPolFac * PhPpolc);
2465  (*hctilde)->data->data[i] = (cosPolFac * PhPpolc) - (sinPolFac * PhPpolp);
2466  }
2467  }
2468 
2469  /* Free allocated memory */
2470  LALFree(pAmp22);
2471  LALFree(pPhase22);
2472  XLALDestroyREAL8Sequence(freqs);
2473 
2474  // Free allocated memory for tidal extension
2475  XLALDestroyREAL8Sequence(phi_tidal);
2476  XLALDestroyREAL8Sequence(amp_tidal);
2477  XLALDestroyREAL8Sequence(planck_taper);
2478 
2479  if(PNRUseTunedAngles){
2480  XLALDestroyREAL8Sequence(alphaPNR);
2481  XLALDestroyREAL8Sequence(betaPNR);
2482  XLALDestroyREAL8Sequence(gammaPNR);
2483  }
2484 
2485  if(AntisymmetricWaveform){
2486  XLALDestroyREAL8Sequence(kappa);
2487  }
2488 
2489  if(lalParams_In == 1)
2490  {
2491  XLALDestroyDict(lalParams);
2492  }
2493 
2494  return status;
2495 }
2496 
2497 
2498 /* ~~~~~~~~~~ Effective Ringdown Frequency ~~~~~~~~~~ */
2499 REAL8 XLALSimPhenomPNRfRingEff( REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams ) {
2500  UINT4 status;
2501 
2502  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2503  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2504 
2505  /* Perform initial sanity checks */
2506  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
2507  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
2508 
2509  /* Use an auxiliar laldict to not overwrite the input argument */
2510  LALDict *lalParams_aux;
2511  /* setup mode array */
2512  if (lalParams == NULL)
2513  {
2514  lalParams_aux = XLALCreateDict();
2515  }
2516  else{
2517  lalParams_aux = XLALDictDuplicate(lalParams);
2518  }
2519 
2520  /* Spins aligned with the orbital angular momenta */
2521  const REAL8 chi1L = chi1z;
2522  const REAL8 chi2L = chi2z;
2523 
2524  const REAL8 deltaF = 0.0001;
2525  const REAL8 f_min = 20;
2526  const REAL8 f_max = 1024;
2527  const REAL8 distance = 1.0;
2528  const REAL8 inclination = 0.0;
2529  const REAL8 fRef = f_min;
2530  const REAL8 phiRef = 0.0;
2531 
2532  /* Initialize useful powers of LAL_PI - this is used in the code called by IMRPhenomXPGenerateFD */
2534  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
2535 
2536  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
2538  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2539  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, 0);
2540  // status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, 20.0, 20.0, 10.0, 1024.0, 3.085677581491367e24, 0, lalParams_aux, 0);
2541  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2542 
2543  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
2545  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2546 
2548  pWF,
2549  pPrec,
2550  m1_SI,
2551  m2_SI,
2552  chi1x,
2553  chi1y,
2554  chi1z,
2555  chi2x,
2556  chi2y,
2557  chi2z,
2558  lalParams_aux,
2560  );
2561  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2562 
2563  //
2564  REAL8 fRING22_prec = pWF->fRING22_prec - 2 * pWF->fRINGEffShiftDividedByEmm;
2565  // return pWF->fRING22_prec - 2 * pWF->fRINGEffShiftDividedByEmm;
2566 
2567  /* free up memory allocation */
2568  LALFree(pPrec);
2569  LALFree(pWF);
2570  XLALDestroyDict(lalParams_aux);
2571 
2572  return fRING22_prec;
2573 
2574 }
2575 
2576 
2577 /* ~~~~~~~~~~ fRINGEffShiftDividedByEmm ~~~~~~~~~~ */
2578 REAL8 XLALSimPhenomPNRfRINGEffShiftDividedByEmm( REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams ) {
2579  UINT4 status;
2580 
2581  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2582  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2583 
2584  /* Perform initial sanity checks */
2585  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
2586  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
2587 
2588  /* Use an auxiliar laldict to not overwrite the input argument */
2589  LALDict *lalParams_aux;
2590  /* setup mode array */
2591  if (lalParams == NULL)
2592  {
2593  lalParams_aux = XLALCreateDict();
2594  }
2595  else{
2596  lalParams_aux = XLALDictDuplicate(lalParams);
2597  }
2598 
2599 /* Spins aligned with the orbital angular momenta */
2600  const REAL8 chi1L = chi1z;
2601  const REAL8 chi2L = chi2z;
2602 
2603  const REAL8 deltaF = 0.0001;
2604  const REAL8 f_min = 20;
2605  const REAL8 f_max = 1024;
2606  const REAL8 distance = 1.0;
2607  const REAL8 inclination = 0.0;
2608  const REAL8 fRef = f_min;
2609  const REAL8 phiRef = 0.0;
2610 
2611  /* Initialize useful powers of LAL_PI - this is used in the code called by IMRPhenomXPGenerateFD */
2613  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
2614 
2615  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
2617  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2618  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, 0);
2619  // status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, 20.0, 20.0, 10.0, 1024.0, 3.085677581491367e24, 0, lalParams_aux, 0);
2620  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2621 
2622  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
2624  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2625 
2627  pWF,
2628  pPrec,
2629  m1_SI,
2630  m2_SI,
2631  chi1x,
2632  chi1y,
2633  chi1z,
2634  chi2x,
2635  chi2y,
2636  chi2z,
2637  lalParams_aux,
2639  );
2640  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2641 
2642  REAL8 fRINGEffShiftDividedByEmm = pWF->fRINGEffShiftDividedByEmm;
2643 
2644  /* free up memory allocation */
2645  LALFree(pPrec);
2646  LALFree(pWF);
2647  XLALDestroyDict(lalParams_aux);
2648 
2649  //
2650  return fRINGEffShiftDividedByEmm;
2651 
2652 }
2653 
2654 
2655 
2656 
2657 /* ~~~~~~~~~~ Final Beta ~~~~~~~~~~ */
2658 REAL8 XLALSimPhenomPNRbetaRD( REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams ) {
2659 
2660  UINT4 status;
2661  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2662  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2663 
2664  /* Perform initial sanity checks */
2665  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
2666  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
2667 
2668  /* Use an auxiliar laldict to not overwrite the input argument */
2669  LALDict *lalParams_aux;
2670  /* setup mode array */
2671  if (lalParams == NULL)
2672  {
2673  lalParams_aux = XLALCreateDict();
2674  }
2675  else{
2676  lalParams_aux = XLALDictDuplicate(lalParams);
2677  }
2678 
2679 /* Spins aligned with the orbital angular momenta */
2680  const REAL8 chi1L = chi1z;
2681  const REAL8 chi2L = chi2z;
2682 
2683  const REAL8 deltaF = 0.0001;
2684  const REAL8 f_min = 20;
2685  const REAL8 f_max = 1024;
2686  const REAL8 distance = 1.0;
2687  const REAL8 inclination = 0.0;
2688  const REAL8 fRef = f_min;
2689  const REAL8 phiRef = 0.0;
2690 
2691  /* Initialize useful powers of LAL_PI - this is used in the code called by IMRPhenomXPGenerateFD */
2693  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
2694 
2695  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
2697  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2698  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, 0);
2699  // status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, 20.0, 20.0, 10.0, 1024.0, 3.085677581491367e24, 0, lalParams_aux, 0);
2700  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2701 
2702  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
2704  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2705 
2707  pWF,
2708  pPrec,
2709  m1_SI,
2710  m2_SI,
2711  chi1x,
2712  chi1y,
2713  chi1z,
2714  chi2x,
2715  chi2y,
2716  chi2z,
2717  lalParams_aux,
2719  );
2720  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2721 
2722  //
2723  REAL8 betaRD = pWF->betaRD;
2724 
2725  /* free up memory allocation */
2726  LALFree(pPrec);
2727  LALFree(pWF);
2728  XLALDestroyDict(lalParams_aux);
2729 
2730  //
2731  return betaRD;
2732 
2733 }
2734 
2735 
2736 
2737 
2738 /* ~~~~~~~~~~ Final spin ~~~~~~~~~~ */
2739 REAL8 XLALSimPhenomPNRafinal_prec( REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams ) {
2740  UINT4 status;
2741 
2742  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2743  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2744 
2745  /* Perform initial sanity checks */
2746  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
2747  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
2748 
2749  /* Use an auxiliar laldict to not overwrite the input argument */
2750  LALDict *lalParams_aux;
2751  /* setup mode array */
2752  if (lalParams == NULL)
2753  {
2754  lalParams_aux = XLALCreateDict();
2755  }
2756  else{
2757  lalParams_aux = XLALDictDuplicate(lalParams);
2758  }
2759 
2760 /* Spins aligned with the orbital angular momenta */
2761  const REAL8 chi1L = chi1z;
2762  const REAL8 chi2L = chi2z;
2763 
2764  const REAL8 deltaF = 0.0001;
2765  const REAL8 f_min = 20;
2766  const REAL8 f_max = 1024;
2767  const REAL8 distance = 1.0;
2768  const REAL8 inclination = 0.0;
2769  const REAL8 fRef = f_min;
2770  const REAL8 phiRef = 0.0;
2771 
2772  /* Initialize useful powers of LAL_PI - this is used in the code called by IMRPhenomXPGenerateFD */
2774  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
2775 
2776  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
2778  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2779  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, 0);
2780  // status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, 20.0, 20.0, 10.0, 1024.0, 3.085677581491367e24, 0, lalParams_aux, 0);
2781  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2782 
2783  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
2785  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2786 
2788  pWF,
2789  pPrec,
2790  m1_SI,
2791  m2_SI,
2792  chi1x,
2793  chi1y,
2794  chi1z,
2795  chi2x,
2796  chi2y,
2797  chi2z,
2798  lalParams_aux,
2800  );
2801  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2802 
2803  //
2804  REAL8 afinal_prec = pWF->afinal_prec;
2805 
2806  /* free up memory allocation */
2807  LALFree(pPrec);
2808  LALFree(pWF);
2809  XLALDestroyDict(lalParams_aux);
2810 
2811  //
2812  return afinal_prec;
2813 
2814 }
2815 
2816 
2817 
2818 
2819 /* ~~~~~~~~~~ Final spin ~~~~~~~~~~ */
2820 REAL8 XLALSimPhenomPNRafinal_nonprec( REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams ) {
2821  UINT4 status;
2822 
2823  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2824  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2825 
2826  /* Perform initial sanity checks */
2827  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
2828  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
2829 
2830  /* Use an auxiliar laldict to not overwrite the input argument */
2831  LALDict *lalParams_aux;
2832  /* setup mode array */
2833  if (lalParams == NULL)
2834  {
2835  lalParams_aux = XLALCreateDict();
2836  }
2837  else{
2838  lalParams_aux = XLALDictDuplicate(lalParams);
2839  }
2840 
2841 /* Spins aligned with the orbital angular momenta */
2842  const REAL8 chi1L = chi1z;
2843  const REAL8 chi2L = chi2z;
2844 
2845  const REAL8 deltaF = 0.0001;
2846  const REAL8 f_min = 20;
2847  const REAL8 f_max = 1024;
2848  const REAL8 distance = 1.0;
2849  const REAL8 inclination = 0.0;
2850  const REAL8 fRef = f_min;
2851  const REAL8 phiRef = 0.0;
2852 
2853  /* Initialize useful powers of LAL_PI - this is used in the code called by IMRPhenomXPGenerateFD */
2855  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
2856 
2857  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
2859  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2860  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, 0);
2861  // status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, 20.0, 20.0, 10.0, 1024.0, 3.085677581491367e24, 0, lalParams_aux, 0);
2862  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2863 
2864  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
2866  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2867 
2869  pWF,
2870  pPrec,
2871  m1_SI,
2872  m2_SI,
2873  chi1x,
2874  chi1y,
2875  chi1z,
2876  chi2x,
2877  chi2y,
2878  chi2z,
2879  lalParams_aux,
2881  );
2882  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2883 
2884  REAL8 afinal_nonprec = pWF->afinal_nonprec;
2885 
2886  /* free up memory allocation */
2887  LALFree(pPrec);
2888  LALFree(pWF);
2889  XLALDestroyDict(lalParams_aux);
2890 
2891  //
2892  return afinal_nonprec;
2893 
2894 }
2895 
2896 
2897 
2898 
2899 
2900 
2901 
2902 /* ~~~~~~~~~~ Final spin ~~~~~~~~~~ */
2903 REAL8 XLALSimPhenomPNRafinal( REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams ) {
2904  UINT4 status;
2905 
2906  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2907  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2908 
2909  /* Perform initial sanity checks */
2910  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
2911  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
2912 
2913  /* Use an auxiliar laldict to not overwrite the input argument */
2914  LALDict *lalParams_aux;
2915  /* setup mode array */
2916  if (lalParams == NULL)
2917  {
2918  lalParams_aux = XLALCreateDict();
2919  }
2920  else{
2921  lalParams_aux = XLALDictDuplicate(lalParams);
2922  }
2923 
2924 /* Spins aligned with the orbital angular momenta */
2925  const REAL8 chi1L = chi1z;
2926  const REAL8 chi2L = chi2z;
2927 
2928  const REAL8 deltaF = 0.0001;
2929  const REAL8 f_min = 20;
2930  const REAL8 f_max = 1024;
2931  const REAL8 distance = 1.0;
2932  const REAL8 inclination = 0.0;
2933  const REAL8 fRef = f_min;
2934  const REAL8 phiRef = 0.0;
2935 
2936  /* Initialize useful powers of LAL_PI - this is used in the code called by IMRPhenomXPGenerateFD */
2938  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
2939 
2940  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
2942  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
2943  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, 0);
2944  // status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, 20.0, 20.0, 10.0, 1024.0, 3.085677581491367e24, 0, lalParams_aux, 0);
2945  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
2946 
2947  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
2949  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
2950 
2952  pWF,
2953  pPrec,
2954  m1_SI,
2955  m2_SI,
2956  chi1x,
2957  chi1y,
2958  chi1z,
2959  chi2x,
2960  chi2y,
2961  chi2z,
2962  lalParams_aux,
2964  );
2965  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
2966 
2967 
2968  //
2969  REAL8 afinal = pWF->afinal;
2970 
2971  /* free up memory allocation */
2972  LALFree(pPrec);
2973  LALFree(pWF);
2974  XLALDestroyDict(lalParams_aux);
2975 
2976  return afinal;
2977 
2978 }
2979 
2980 
2981 
2982 /* ~~~~~~~~~~ Window function ~~~~~~~~~~ */
2983 REAL8 XLALSimPhenomPNRwindow( REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams ) {
2984  UINT4 status;
2985 
2986  status = XLALIMRPhenomXPCheckMassesAndSpins(&m1_SI,&m2_SI,&chi1x,&chi1y,&chi1z,&chi2x,&chi2y,&chi2z);
2987  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: XLALIMRPhenomXPCheckMassesAndSpins failed.\n");
2988 
2989  /* Perform initial sanity checks */
2990  if(m1_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m1 must be positive.\n"); }
2991  if(m2_SI <= 0.0) { XLAL_ERROR(XLAL_EDOM, "m2 must be positive.\n"); }
2992 
2993  /* Use an auxiliar laldict to not overwrite the input argument */
2994  LALDict *lalParams_aux;
2995  /* setup mode array */
2996  if (lalParams == NULL)
2997  {
2998  lalParams_aux = XLALCreateDict();
2999  }
3000  else{
3001  lalParams_aux = XLALDictDuplicate(lalParams);
3002  }
3003 
3004 /* Spins aligned with the orbital angular momenta */
3005  const REAL8 chi1L = chi1z;
3006  const REAL8 chi2L = chi2z;
3007 
3008  const REAL8 deltaF = 0.0001;
3009  const REAL8 f_min = 20;
3010  const REAL8 f_max = 1024;
3011  const REAL8 distance = 1.0;
3012  const REAL8 inclination = 0.0;
3013  const REAL8 fRef = f_min;
3014  const REAL8 phiRef = 0.0;
3015 
3016  /* Initialize useful powers of LAL_PI - this is used in the code called by IMRPhenomXPGenerateFD */
3018  XLAL_CHECK(XLAL_SUCCESS == status, status, "Failed to initialize useful powers of LAL_PI.\n");
3019 
3020  /* Initialize IMR PhenomX Waveform struct and check that it initialized correctly */
3022  pWF = XLALMalloc(sizeof(IMRPhenomXWaveformStruct));
3023  status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, fRef, phiRef, f_min, f_max, distance, inclination, lalParams_aux, 0);
3024  // status = IMRPhenomXSetWaveformVariables(pWF, m1_SI, m2_SI, chi1L, chi2L, deltaF, 20.0, 20.0, 10.0, 1024.0, 3.085677581491367e24, 0, lalParams_aux, 0);
3025  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetWaveformVariables failed.\n");
3026 
3027  /* Initialize IMR PhenomX Precession struct and check that it generated successfully */
3029  pPrec = XLALMalloc(sizeof(IMRPhenomXPrecessionStruct));
3030 
3032  pWF,
3033  pPrec,
3034  m1_SI,
3035  m2_SI,
3036  chi1x,
3037  chi1y,
3038  chi1z,
3039  chi2x,
3040  chi2y,
3041  chi2z,
3042  lalParams_aux,
3044  );
3045  XLAL_CHECK(XLAL_SUCCESS == status, XLAL_EFUNC, "Error: IMRPhenomXSetPrecessionVariables failed.\n");
3046 
3047  //
3048  REAL8 pnr_window = pWF->pnr_window;
3049 
3050  /* free up memory allocation */
3051  LALFree(pPrec);
3052  LALFree(pWF);
3053  XLALDestroyDict(lalParams_aux);
3054 
3055  return pnr_window;
3056 
3057 }
void XLALDestroyDict(LALDict *dict)
LALDict * XLALDictDuplicate(LALDict *old)
LALDict * XLALCreateDict(void)
#define LALFree(p)
int XLALSimNRTunedTidesFDTidalPhaseFrequencySeries(const REAL8Sequence *phi_tidal, const REAL8Sequence *amp_tidal, const REAL8Sequence *planck_taper, const REAL8Sequence *fHz, REAL8 m1_SI, REAL8 m2_SI, REAL8 lambda1, REAL8 lambda2, NRTidal_version_type NRTidal_version)
Function to call the frequency domain tidal correction over an array of input frequencies.
double XLALSimNRTunedTidesMergerFrequency(const REAL8 mtot_MSUN, const REAL8 kappa2T, const REAL8 q)
compute the merger frequency of a BNS system.
static size_t NextPow2(const size_t n)
REAL8 XLALSimPhenomPNRafinal_nonprec(REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams)
REAL8 XLALSimPhenomPNRwindow(REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams)
IMRPhenomX_UsefulPowers powers_of_lalpi
int IMRPhenomXASGenerateFD(COMPLEX16FrequencySeries **htilde22, const REAL8Sequence *freqs_In, IMRPhenomXWaveformStruct *pWF, LALDict *lalParams)
int IMRPhenomXPGenerateFD(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, const REAL8Sequence *freqs_In, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
REAL8 XLALSimPhenomPNRafinal(REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams)
#define PHENOMXPDEBUG
#define PHENOMXDEBUG
REAL8 XLALSimPhenomPNRbetaRD(REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams)
REAL8 XLALSimPhenomPNRfRingEff(REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams)
int IMRPhenomXCheckForUniformFrequencies(REAL8Sequence *frequencies, REAL8 df)
REAL8 XLALSimPhenomPNRafinal_prec(REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams)
REAL8 XLALSimPhenomPNRfRINGEffShiftDividedByEmm(REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams)
int IMRPhenomX_PNR_GenerateAntisymmetricPhaseCoefficients(REAL8 *A0, REAL8 *phi_A0, REAL8 *phi_B0, const double MfT, double lina, double linb, double inveta, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, IMRPhenomXPhaseCoefficients *pPhase22)
Anti-symmetric phase coefficients/offsets.
int IMRPhenomX_PNR_GenerateAntisymmetricAmpRatio(REAL8Sequence *kappa, const REAL8Sequence *freqs, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
GENERATE Antisymmetric amplitude ratio.
UINT4 IMRPhenomX_PNR_AttachMRBeta(const IMRPhenomX_PNR_beta_parameters *betaParams)
Determine whether to attach the MR contributions to beta.
REAL8 IMRPhenomX_PNR_GeneratePNRBetaNoMR(REAL8 Mf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
This function evaluates only the rescaled inspiral beta given in Eq.
REAL8 IMRPhenomX_PNR_GenerateMergedPNRBetaAtMf(REAL8 Mf, const IMRPhenomX_PNR_beta_parameters *betaParams, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, IMRPhenomXWaveformStruct *pWF_SingleSpin, IMRPhenomXPrecessionStruct *pPrec_SingleSpin)
This function generates beta with the tuned angles and PN expressions blended during merger-ringdown.
REAL8 IMRPhenomX_PNR_GeneratePNRBetaAtMf(REAL8 Mf, const IMRPhenomX_PNR_beta_parameters *betaParams, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, IMRPhenomXWaveformStruct *pWF_SingleSpin, IMRPhenomXPrecessionStruct *pPrec_SingleSpin)
This function evaluates Eqs.
void IMRPhenomX_PNR_FreeStructs(IMRPhenomXWaveformStruct **pWF_SingleSpin, IMRPhenomXPrecessionStruct **pPrec_SingleSpin, IMRPhenomX_PNR_alpha_parameters **alphaParams, IMRPhenomX_PNR_beta_parameters **betaParams)
INT4 IMRPhenomX_PNR_PopulateStructs(IMRPhenomXWaveformStruct **pWF_SingleSpin, IMRPhenomXPrecessionStruct **pPrec_SingleSpin, IMRPhenomX_PNR_alpha_parameters **alphaParams, IMRPhenomX_PNR_beta_parameters **betaParams, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
These two functions create and populate the required parameter structs for PNR.
void IMRPhenomX_PNR_EnforceXASPhaseAlignment(double *linb, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPhaseCoefficients *pPhase)
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...
static double IMRPhenomX_Inspiral_Phase_22_AnsatzInt(double Mf, IMRPhenomX_UsefulPowers *powers_of_Mf, IMRPhenomXPhaseCoefficients *pPhase)
Ansatz for the inspiral phase.
static double IMRPhenomX_Inspiral_Amp_22_Ansatz(double Mf, IMRPhenomX_UsefulPowers *powers_of_Mf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXAmpCoefficients *pAmp)
static double IMRPhenomX_Intermediate_Phase_22_AnsatzInt(double f, IMRPhenomX_UsefulPowers *powers_of_f, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPhaseCoefficients *pPhase)
static double IMRPhenomX_Intermediate_Amp_22_Ansatz(double ff, IMRPhenomX_UsefulPowers *powers_of_f, IMRPhenomXWaveformStruct *pWF, IMRPhenomXAmpCoefficients *pAmp)
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)
NRTidal_version_type IMRPhenomX_SetTidalVersion(LALDict *lalParams)
double IMRPhenomX_TimeShift_22(IMRPhenomXPhaseCoefficients *pPhase, IMRPhenomXWaveformStruct *pWF)
REAL8 IMRPhenomX_TidalPhaseDerivative(IMRPhenomX_UsefulPowers *powers_of_Mf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPhaseCoefficients *pPhase, NRTidal_version_type NRTidal_version)
void IMRPhenomX_Phase_22_ConnectionCoefficients(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPhaseCoefficients *pPhase)
int IMRPhenomXGetAmplitudeCoefficients(IMRPhenomXWaveformStruct *pWF, IMRPhenomXAmpCoefficients *pAmp)
int IMRPhenomX_Initialize_Powers(IMRPhenomX_UsefulPowers *p, REAL8 number)
double IMRPhenomX_Phase_22(double ff, IMRPhenomX_UsefulPowers *powers_of_f, IMRPhenomXPhaseCoefficients *pPhase, IMRPhenomXWaveformStruct *pWF)
void IMRPhenomXGetTidalPhaseCoefficients(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPhaseCoefficients *pPhase, NRTidal_version_type NRTidal_version)
REAL8 IMRPhenomX_TidalPhase(IMRPhenomX_UsefulPowers *powers_of_Mf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPhaseCoefficients *pPhase, NRTidal_version_type NRTidal_version)
int IMRPhenomXGetPhaseCoefficients(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPhaseCoefficients *pPhase)
double IMRPhenomX_dPhase_22(double ff, IMRPhenomX_UsefulPowers *powers_of_f, IMRPhenomXPhaseCoefficients *pPhase, IMRPhenomXWaveformStruct *pWF)
int IMRPhenomXPSpinTaylorAnglesIMR(REAL8Sequence **alphaFS, REAL8Sequence **cosbetaFS, REAL8Sequence **gammaFS, REAL8Sequence *freqsIN, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *LALparams)
This function evaluates the SpinTaylor Euler angles on a frequency grid passed by the user.
int IMRPhenomXPTwistUp22(const REAL8 Mf, const COMPLEX16 hAS, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, COMPLEX16 *hp, COMPLEX16 *hc)
Core twisting up routine, see Section III A of arXiv:2004.06503.
double IMRPhenomX_PN_Euler_epsilon_NNLO(IMRPhenomXPrecessionStruct *pPrec, const double omega, const double omega_cbrt2, const double omega_cbrt, const double logomega)
Internal function to calculate epsilon using pre-cached NNLO PN expressions.
REAL8 XLALSimIMRPhenomXLPNAnsatz(REAL8 v, REAL8 LNorm, REAL8 L0, REAL8 L1, REAL8 L2, REAL8 L3, REAL8 L4, REAL8 L5, REAL8 L6, REAL8 L7, REAL8 L8, REAL8 L8L)
This is a convenient wrapper function for PN orbital angular momentum.
double IMRPhenomX_PN_Euler_alpha_NNLO(IMRPhenomXPrecessionStruct *pPrec, const double omega, const double omega_cbrt2, const double omega_cbrt, const double logomega)
Internal function to calculate alpha using pre-cached NNLO PN expressions.
vector IMRPhenomX_Return_phi_zeta_costhetaL_MSA(const double v, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
Wrapper to generate , and at a given frequency.
int IMRPhenomXPTwistUp22_NumericalAngles(const COMPLEX16 hAS, REAL8 alpha, REAL8 cos_beta, REAL8 gamma, IMRPhenomXPrecessionStruct *pPrec, COMPLEX16 *hp, COMPLEX16 *hc)
Core twisting up routine for SpinTaylor angles.
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:
static double IMRPhenomX_Ringdown_Phase_22_AnsatzInt(double ff, IMRPhenomX_UsefulPowers *powers_of_f, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPhaseCoefficients *pPhase)
static double IMRPhenomX_Ringdown_Amp_22_Ansatz(double ff, IMRPhenomXWaveformStruct *pWF, IMRPhenomXAmpCoefficients *pAmp)
INT4 XLALIMRPhenomXPCheckMassesAndSpins(REAL8 *m1, REAL8 *m2, REAL8 *chi1x, REAL8 *chi1y, REAL8 *chi1z, REAL8 *chi2x, REAL8 *chi2y, REAL8 *chi2z)
Check if m1 > m2.
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPrecVersion(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupPhenomXPNRUseTunedAngles(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPhenomXPrecVersion(LALDict *params, INT4 value)
#define fprintf
int s
Definition: bh_qnmode.c:137
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
double duration
COMPLEX16FrequencySeries * XLALResizeCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series, int first, size_t length)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
#define LAL_MSUN_SI
#define LAL_PI
#define LAL_MTSUN_SI
#define LAL_PC_SI
#define LAL_PI_4
#define LIGOTIMEGPSZERO
double complex COMPLEX16
double REAL8
uint32_t UINT4
int32_t INT4
void * XLALMalloc(size_t n)
NRTidal_version_type
Definition: LALSimIMR.h:80
@ NRTidal_V
version NRTidal: based on https://arxiv.org/pdf/1706.02969.pdf
Definition: LALSimIMR.h:81
@ NoNRT_V
special case for PhenomPv2 BBH baseline
Definition: LALSimIMR.h:85
@ NRTidalv2_V
version NRTidalv2: https://arxiv.org/abs/1905.06011
Definition: LALSimIMR.h:82
double XLALSimIMRPhenomXASDuration(const REAL8 m1_SI, const REAL8 m2_SI, const REAL8 chi1L, const REAL8 chi2L, const REAL8 f_start)
Compute the duration of IMRPhenomXAS using the approximate SPA relation .
int XLALSimIMRPhenomXASGenerateFD(COMPLEX16FrequencySeries **htilde22, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1L, REAL8 chi2L, REAL8 distance, REAL8 f_min, REAL8 f_max, REAL8 deltaF, REAL8 phi0, REAL8 fRef_In, LALDict *lalParams)
Driver routine to calculate an IMRPhenomX aligned-spin, inspiral-merger-ringdown phenomenological wav...
int XLALSimIMRPhenomXASFrequencySequence(COMPLEX16FrequencySeries **htilde22, const REAL8Sequence *freqs, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1L, REAL8 chi2L, REAL8 distance, REAL8 phi0, REAL8 fRef_In, LALDict *lalParams)
Compute waveform in LAL format at specified frequencies for the IMRPhenomX model.
int XLALSimIMRPhenomXPMSAAngles(REAL8Sequence **alpha_of_f, REAL8Sequence **gamma_of_f, REAL8Sequence **cosbeta_of_f, const REAL8Sequence *freqs, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, REAL8 inclination, REAL8 fRef_In, INT4 mprime, LALDict *lalParams)
int XLALSimIMRPhenomXPGenerateFD(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, 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, REAL8 f_min, REAL8 f_max, const REAL8 deltaF, REAL8 fRef_In, LALDict *lalParams)
int XLALSimIMRPhenomXPFrequencySequence(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, const REAL8 distance, const REAL8 inclination, const REAL8 phiRef, REAL8 fRef_In, LALDict *lalParams)
Compute waveform in LAL format at specified frequencies for the IMRPhenomXP model.
int IMRPhenomX_PNR_GeneratePNRAngles(REAL8Sequence *alphaPNR, REAL8Sequence *betaPNR, REAL8Sequence *gammaPNR, const REAL8Sequence *freqs, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, LALDict *lalParams)
Generate the tuned precession angles outlined in arXiv:2107.08876.
int XLALSimIMRPhenomXPCalculateModelParametersFromSourceFrame(REAL8 *chi1L, REAL8 *chi2L, REAL8 *chi_p, REAL8 *thetaJN, REAL8 *alpha0, REAL8 *phi_aligned, REAL8 *zeta_polarization, REAL8 m1_SI, REAL8 m2_SI, REAL8 f_ref, REAL8 phiRef, REAL8 incl, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, LALDict *lalParams)
int XLALSimIMRPhenomXPPNAngles(REAL8Sequence **alpha_of_f, REAL8Sequence **gamma_of_f, REAL8Sequence **cosbeta_of_f, const REAL8Sequence *freqs, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, REAL8 inclination, REAL8 fRef_In, INT4 mprime, LALDict *lalParams)
static const INT4 q
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
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(...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EDOM
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
string status
double alpha
Definition: sgwb.c:106
PhenomXPbetaMRD * beta_params
Parameters needed for analytical MRD continuation of cosbeta.
REAL8 zeta_polarization
Angle to rotate the polarizations.
REAL8 thetaJN
Angle between J0 and line of sight (z-direction)
IMRPhenomXWaveformStruct * pWF22AS
PhenomXPalphaMRD * alpha_params
Parameters needed for analytical MRD continuation of alpha.
REAL8 chi_singleSpin
Magnitude of effective single spin used for tapering two-spin angles, Eq.
REAL8 phi0_aligned
Initial phase to feed the underlying aligned-spin model.
REAL8 * data
double f_min
Definition: unicorn.c:22
double f_max
Definition: unicorn.c:23