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