LALSimulation  5.4.0.1-fe68b98
LALSimIMRPhenomX_PNR_beta.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2022 Cardiff University
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with with program; see the file COPYING. If not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301 USA
18  */
19 
20 #ifdef __cplusplus
21 extern "C"
22 {
23 #endif
24 
25  /**
26  * \author Eleanor Hamilton, Sebastian Khan, Jonathan E. Thompson
27  *
28  */
29 
30 #include <lal/LALStdlib.h>
31 #include <lal/LALConstants.h>
32 #include <lal/LALDatatypes.h>
33 #include <lal/XLALError.h>
34 
36 
37 #ifndef _OPENMP
38 #define omp ignore
39 #endif
40 
41 #ifndef PHENOMXHMDEBUG
42 #define DEBUG 0
43 #else
44 #define DEBUG 1
45 #endif
46 
47  /**
48  * This function evaluates Eqs. 60 and 61 of arXiv:2107.08876.
49  */
51  REAL8 Mf, /**< geometric frequency */
52  const IMRPhenomX_PNR_beta_parameters *betaParams, /**< beta parameter struct */
53  IMRPhenomXWaveformStruct *pWF, /**< PhenomX waveform struct */
54  IMRPhenomXPrecessionStruct *pPrec, /**< PhenomX precession struct */
55  IMRPhenomXWaveformStruct *pWF_SingleSpin, /**< PhenomX waveform struct with approximate single spin */
56  IMRPhenomXPrecessionStruct *pPrec_SingleSpin /**< PhenomX waveform struct with approximate single spin */
57  )
58  {
59  /* get beta connection frequencies */
60  REAL8 Mf_beta_lower = betaParams->Mf_beta_lower;
61  REAL8 Mf_beta_upper = betaParams->Mf_beta_upper;
62 
63  if (Mf <= Mf_beta_lower)
64  { /* Below Mf_beta_lower, we use a rescaled form of the PN beta.
65  * In the case of a two-spin system, the PN beta used may have the two-spin
66  * oscillations tapered away to connect nicely at Mf_beta_lower */
67  REAL8 betaPN = IMRPhenomX_PNR_GetPNBetaAtFreq(Mf, betaParams, pWF, pPrec, pWF_SingleSpin, pPrec_SingleSpin);
68  REAL8 beta_waveform = IMRPhenomX_PNR_PNWaveformBetaWrapper(Mf, betaPN, pWF, pPrec);
69  REAL8 rescaled_beta = beta_waveform * IMRPhenomX_PNR_rescale_beta_expression(Mf, betaParams);
70  return IMRPhenomX_PNR_arctan_window(rescaled_beta);
71  }
72 
73  if (Mf >= Mf_beta_upper)
74  { /* above Mf_beta_upper, attach the final value of beta*/
75  REAL8 final_beta = IMRPhenomX_PNR_MR_beta_expression(Mf_beta_upper, betaParams);
76  return IMRPhenomX_PNR_arctan_window(final_beta);
77  }
78 
79  /* in between we evaluate the MR Ansatz */
80  REAL8 MR_beta = IMRPhenomX_PNR_MR_beta_expression(Mf, betaParams);
81  return IMRPhenomX_PNR_arctan_window(MR_beta);
82  }
83 
84  /**
85  * This function evaluates only the rescaled inspiral beta given in
86  * Eq. 41 of arXiv:2107.08876, without attaching the MR model or tapering
87  * the two-spin oscillations.
88  */
90  REAL8 Mf, /**< geometric frequency */
91  IMRPhenomXWaveformStruct *pWF, /**< PhenomX wavefrom struct */
92  IMRPhenomXPrecessionStruct *pPrec /**< PhenomX precession struct */
93  )
94  {
95  /* generate scaled PN beta without MR contributions */
96  REAL8 pn_beta = IMRPhenomX_PNR_GetPNBetaAtFreq_fulltwospin(Mf, pWF, pPrec);
97  REAL8 pn_waveform_beta = IMRPhenomX_PNR_PNWaveformBetaWrapper(Mf, pn_beta, pWF, pPrec);
98 
99  return IMRPhenomX_PNR_arctan_window(pn_waveform_beta);
100  }
101 
102  /**
103  * This function generates beta with the tuned angles and PN expressions blended during merger-ringdown
104  */
106  REAL8 Mf, /**< geometric frequency */
107  const IMRPhenomX_PNR_beta_parameters *betaParams, /**< beta parameter struct */
108  IMRPhenomXWaveformStruct *pWF, /**< PhenomX waveform struct */
109  IMRPhenomXPrecessionStruct *pPrec, /**< PhenomX precession struct */
110  IMRPhenomXWaveformStruct *pWF_SingleSpin, /**< PhenomX waveform struct with approximate single spin */
111  IMRPhenomXPrecessionStruct *pPrec_SingleSpin /**< PhenomX waveform struct with approximate single spin */
112  )
113  {
114  /* evaluate blending window */
115  double pnr_window = IMRPhenomX_PNR_AnglesWindow(pWF, pPrec);
116  double msa_window = 1-pnr_window;
117 
118  /* get beta connection frequencies */
119  REAL8 Mf_beta_lower = betaParams->Mf_beta_lower;
120  REAL8 Mf_beta_upper = betaParams->Mf_beta_upper;
121 
122  if (Mf <= Mf_beta_lower)
123  { /* Below Mf_beta_lower, we use a rescaled form of the PN beta.
124  * In the case of a two-spin system, the PN beta used may have the two-spin
125  * oscillations tapered away to connect nicely at Mf_beta_lower */
126  REAL8 betaPN = IMRPhenomX_PNR_GetPNBetaAtFreq(Mf, betaParams, pWF, pPrec, pWF_SingleSpin, pPrec_SingleSpin);
127  REAL8 beta_waveform = IMRPhenomX_PNR_PNWaveformBetaWrapper(Mf, betaPN, pWF, pPrec);
128  REAL8 rescaled_beta = beta_waveform * IMRPhenomX_PNR_rescale_beta_expression(Mf, betaParams);
129  return IMRPhenomX_PNR_arctan_window(pnr_window * rescaled_beta + msa_window * beta_waveform);
130  }
131 
132  if (Mf >= Mf_beta_upper)
133  { /* above Mf_beta_upper, attach the final value of beta*/
134  REAL8 betaPN = IMRPhenomX_PNR_GetPNBetaAtFreq(Mf, betaParams, pWF, pPrec, pWF_SingleSpin, pPrec_SingleSpin);
135  REAL8 beta_waveform = IMRPhenomX_PNR_PNWaveformBetaWrapper(Mf, betaPN, pWF, pPrec);
136  REAL8 final_beta = IMRPhenomX_PNR_MR_beta_expression(Mf_beta_upper, betaParams);
137  return IMRPhenomX_PNR_arctan_window(pnr_window * final_beta + msa_window * beta_waveform);
138  }
139 
140  /* in between we evaluate the MR Ansatz */
141  REAL8 betaPN = IMRPhenomX_PNR_GetPNBetaAtFreq(Mf, betaParams, pWF, pPrec, pWF_SingleSpin, pPrec_SingleSpin);
142  REAL8 beta_waveform = IMRPhenomX_PNR_PNWaveformBetaWrapper(Mf, betaPN, pWF, pPrec);
143  REAL8 MR_beta = IMRPhenomX_PNR_MR_beta_expression(Mf, betaParams);
144  return IMRPhenomX_PNR_arctan_window(pnr_window * MR_beta + msa_window * beta_waveform);
145  }
146 
147  /**
148  * We evaluate beta at the final Mf_beta_upper connection frequency
149  * to approximate the final value of beta during ringdown. This
150  * is required to analytically approximate the effective ringdown frequency. FIXME: add citation
151  */
152 
154  IMRPhenomXWaveformStruct *pWF, /**< PhenomX waveform struct */
155  IMRPhenomXPrecessionStruct *pPrec) /**< PhenomX precession struct */
156  {
157  /* may we continue? */
158  XLAL_CHECK(pWF != NULL, XLAL_EFAULT);
159  XLAL_CHECK(pPrec != NULL, XLAL_EFAULT);
160 
161  /* get effective single spin parameters */
162  REAL8 eta = pWF->eta;
163  REAL8 chi = pPrec->chi_singleSpin;
165 
166  /* approximate orientation of final spin */
167  REAL8 costhetaf = pPrec->costheta_final_singleSpin;
168 
169  REAL8 betafinal = IMRPhenomX_PNR_arctan_window( acos(costhetaf) - IMRPhenomX_PNR_beta_Bf_coefficient(eta, chi, costheta) );
170 
171  return betafinal;
172  }
173 
174  /**
175  * A wrapper to produce either the NNLO or MSA beta depending on the IMRPhenomXPrecVersion.
176  *
177  * Should the MSA angle be called, we taper any potential oscillations induced by a time-varying
178  * total spin magnitude so that we return an effective single-spin value for beta at the
179  * lower connection frequency. This is described in Sec. 6C of arXiv:2107.08876
180  */
181 
183  REAL8 Mf, /**< geometric frequency */
184  const IMRPhenomX_PNR_beta_parameters *betaParams, /**< beta parameter struct */
185  IMRPhenomXWaveformStruct *pWF, /**< PhenomX waveform struct */
186  IMRPhenomXPrecessionStruct *pPrec, /**< PhenomX precession struct */
187  IMRPhenomXWaveformStruct *pWF_SingleSpin, /**< PhenomX waveform struct with approximate single spin */
188  IMRPhenomXPrecessionStruct *pPrec_SingleSpin /**< PhenomX waveform struct with approximate single spin */
189  )
190  {
191 
192  REAL8 beta;
193 
194  /* get PN expansion parameter v = (pi M f)^(1/3) */
195  const double omega = LAL_PI * Mf;
196  const double omega_cbrt = cbrt(omega);
197 
198  switch (pPrec->IMRPhenomXPrecVersion)
199  {
200  /* ~~~~~ Use NNLO PN Euler Angles - Appendix G of arXiv:2004.06503 and https://dcc.ligo.org/LIGO-T1500602 ~~~~~ */
201  case 101:
202  case 102:
203  case 103:
204  case 104:
205  {
206  REAL8 L = XLALSimIMRPhenomXLPNAnsatz(omega_cbrt, pWF->eta / omega_cbrt, pPrec->L0, pPrec->L1, pPrec->L2, pPrec->L3, pPrec->L4, pPrec->L5, pPrec->L6, pPrec->L7, pPrec->L8, pPrec->L8L);
207 
208  /*
209  Comment from IMRPhenomX_precession.c:
210  We ignore the sign of L + SL below:
211  s := Sp / (L + SL)
212  */
213  REAL8 s = pPrec->Sperp / (L + pPrec->SL);
214  REAL8 s2 = s * s;
215  beta = acos(copysign(1.0, L + pPrec->SL) / sqrt(1.0 + s2));
216 
217  break;
218  }
219  case 220:
220  case 221:
221  case 222:
222  case 223:
223  case 224:
224  {
225  vector vangles = {0., 0., 0.};
226 
227  /* ~~~~~ Euler Angles from Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967 ~~~~~ */
228  /* First we grab the full MSA angle with possible two-spin oscillations */
229  vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(omega_cbrt, pWF, pPrec);
230  REAL8 beta_full = acos(vangles.z);
231 
232  /* lower connection frequency as target for taper */
233  REAL8 Mf_beta_lower = betaParams->Mf_beta_lower;
234 
235  /* check if we have meaningful two-spin contributions */
236  if (IMRPhenomX_PNR_CheckTwoSpin(pPrec))
237  { /* we do */
238  /* compute an effective single-spin beta that averages through oscillations */
239  const vector vangles_SingleSpin = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(omega_cbrt, pWF_SingleSpin, pPrec_SingleSpin);
240  REAL8 beta_SingleSpin = acos(vangles_SingleSpin.z);
241 
242  /* if we are below the connection frequency, taper the oscillations */
243  if (Mf <= Mf_beta_lower)
244  {
245  /* tapering is described in Eq. 45 of arXiv:2107.08876 */
246  REAL8 oscillations = beta_full - beta_SingleSpin;
247  REAL8 envelope = cos(2.0 * LAL_PI * Mf / (4.0 * Mf_beta_lower)) * cos(2.0 * LAL_PI * Mf / (4.0 * Mf_beta_lower));
248  beta = (beta_SingleSpin + oscillations * envelope);
249  }
250  else
251  { /* otherwise just return single-spin beta */
252  beta = beta_SingleSpin;
253  }
254  }
255  else
256  { /* no oscillations, just return full beta */
257  beta = beta_full;
258  }
259 
260  break;
261  }
262  default:
263  {
264  XLAL_ERROR(XLAL_EINVAL, "Error: IMRPhenomXPrecessionVersion not recognized in IMRPhenomX_PNR_GetPNBetaAtFreq.\n");
265  break;
266  }
267  }
268 
269  return beta;
270  }
271 
272  /**
273  * A wrapper to produce either the NNLO or MSA beta depending on the IMRPhenomXPrecVersion.
274  *
275  * This version does not modify the two-spin oscillations.
276  */
277 
279  REAL8 Mf, /**< geometric frequency */
280  IMRPhenomXWaveformStruct *pWF, /**< PhenomX waveform struct */
281  IMRPhenomXPrecessionStruct *pPrec /**< PhenomX precession struct */
282  )
283  {
284  REAL8 beta;
285 
286  /* get PN expansion parameter v = (pi M f)^(1/3) */
287  const double omega = LAL_PI * Mf;
288  const double omega_cbrt = cbrt(omega);
289 
290  switch (pPrec->IMRPhenomXPrecVersion)
291  {
292  /* ~~~~~ Use NNLO PN Euler Angles - Appendix G of arXiv:2004.06503 and https://dcc.ligo.org/LIGO-T1500602 ~~~~~ */
293  case 101:
294  case 102:
295  case 103:
296  case 104:
297  {
298 
299  const REAL8 L = XLALSimIMRPhenomXLPNAnsatz(omega_cbrt, pWF->eta / omega_cbrt, pPrec->L0, pPrec->L1, pPrec->L2, pPrec->L3, pPrec->L4, pPrec->L5, pPrec->L6, pPrec->L7, pPrec->L8, pPrec->L8L);
300 
301  /* Comment from IMRPhenomX_precession.c:
302  We ignore the sign of L + SL below:
303  s := Sp / (L + SL)
304  */
305  REAL8 s = pPrec->Sperp / (L + pPrec->SL);
306  REAL8 s2 = s * s;
307  beta = acos(copysign(1.0, L + pPrec->SL) / sqrt(1.0 + s2));
308 
309  break;
310  }
311  case 220:
312  case 221:
313  case 222:
314  case 223:
315  case 224:
316  {
317  vector vangles = {0., 0., 0.};
318 
319  /* ~~~~~ Euler Angles from Chatziioannou et al, PRD 95, 104004, (2017), arXiv:1703.03967 ~~~~~ */
320  vangles = IMRPhenomX_Return_phi_zeta_costhetaL_MSA(omega_cbrt, pWF, pPrec);
321 
322  beta = acos(vangles.z);
323 
324  break;
325  }
326  default:
327  {
328  XLAL_ERROR(XLAL_EINVAL, "Error: IMRPhenomXPrecessionVersion not recognized in IMRPhenomX_PNR_GetPNBetaAtFreq_fulltwospin.\n");
329  break;
330  }
331  }
332 
333  return beta;
334  }
335 
336  /**
337  * A wrapper to generate the "waveform" PN beta from Eq. 41 of arXiv:2107.08876.
338  *
339  * The wrapper goes through the trouble of computing the frequency-dependent Sperp
340  * given by Eq. 47 of arXiv:2107.08876.
341  */
342 
344  REAL8 Mf, /**< geometric frequency */
345  REAL8 pn_beta, /**< MSA or NNLO beta */
346  IMRPhenomXWaveformStruct *pWF, /**< PhenomX waveform struct */
347  IMRPhenomXPrecessionStruct *pPrec /**< PhenomX precession struct */
348  )
349  {
350  /* grab needed parameters */
351  REAL8 M = pWF->Mtot;
352  REAL8 m1 = pWF->m1 * M;
353  REAL8 m2 = pWF->m2 * M;
354  REAL8 J0 = pPrec->J0;
355  REAL8 L0 = pPrec->LRef;
356  REAL8 chi_eff = pWF->chiEff;
357 
358  /* compute derived terms */
359  REAL8 chi_parr = (m1 + m2) * chi_eff / m1;
360 
361  /* get orbital angular momentum */
362  REAL8 v = cbrt(LAL_PI * Mf);
363  REAL8 L_norm = pWF->eta / v;
364  REAL8 L_3PN = M*M*XLALSimIMRPhenomXLPNAnsatz(v, L_norm, pPrec->L0, pPrec->L1, pPrec->L2, pPrec->L3, pPrec->L4, pPrec->L5, pPrec->L6, pPrec->L7, pPrec->L8, pPrec->L8L);
365 
366  /* compute spin and costheta in Eqs. 18-19 of arXiv:2107.08876 at given Mf */
367  REAL8 chi_temp = IMRPhenomX_PNR_chi_calc(m1, L_3PN, J0, L0, chi_parr, pn_beta);
368  REAL8 costheta_temp = chi_parr / chi_temp;
369 
370  /* evaluate Eq. 41 of arXiv:2107.08876 */
371  return IMRPhenomX_PNR_PNWaveformBeta(Mf, pn_beta, m1, m2, chi_temp, costheta_temp);
372  }
373 
374  /**
375  * The magnitude of the effective total spin is computed from
376  * the total and orbital angular momenta, J0 and L0 resp., along with the opening
377  * angle, beta, between them.
378  *
379  * This procedure is outlined in Eqs. 47 and 18 of arXiv:2107.08876.
380  */
381 
383  REAL8 m1, /**< mass of primary (Msun) */
384  REAL8 L, /**< magnitude of L and Mf */
385  REAL8 J0, /**< initial magnitude of J at Mf_ref */
386  REAL8 L0, /**< initial magnitude of L at Mf_ref */
387  REAL8 chi_parr, /**< combined spin parallel to L0 */
388  REAL8 beta /**< PN opening angle, either MSA or NNLO */
389  )
390  {
391  /* compute frequency-dependent Sperp and scale by mass */
392  REAL8 S_perp = (J0 - (L0 - L)) * sin(beta);
393  REAL8 chi_p = S_perp / (m1 * m1);
394 
395  /* find magnitude */
396  REAL8 chi = sqrt(chi_parr * chi_parr + chi_p * chi_p);
397 
398  return chi;
399  }
400 
401  /**
402  * The "waveform" PN beta from Eq. 41 of arXiv:2107.08876.
403  *
404  * This function maps the dynamics iota to a version of beta
405  * more closely resembling the angle associated with the optimal
406  * emission direction described in Sec. 6B of arXiv:2107.08876.
407  */
408 
410  REAL8 Mf, /**< geometric frequency */
411  REAL8 iota, /**< dynamics precession cone opening angle */
412  REAL8 m1, /**< mass of primary (scaled to total mass 1) */
413  REAL8 m2, /**< mass of secondary (scaled to total mass 1) */
414  REAL8 chi, /**< effective single spin magnitude */
415  REAL8 costheta /**< effective single spin polar angle (rad) */
416  )
417  {
418 
419  /* calculate combinations of masses */
420  REAL8 M = m1 + m2;
421  REAL8 nu = (m1 * m2) / (M * M);
422  REAL8 delta = (m1 - m2) / M;
423 
424  /* get polar angle */
425  REAL8 theta = acos(costheta);
426 
427  /* calculate invariant velocity */
428  REAL8 w_orb = LAL_PI * Mf;
429  REAL8 v = pow(w_orb, 1.0 / 3.0);
430  REAL8 v2 = v * v;
431  REAL8 v3 = v * v2;
432 
433  /* calculate needed trig functions */
434  REAL8 cos_iota = cos(iota);
435  REAL8 sin_iota = sin(iota);
436 
437  REAL8 cos_half_iota = cos(iota / 2.0);
438  REAL8 sin_half_iota = sin(iota / 2.0);
439 
440  REAL8 cos_theta = cos(theta);
441  REAL8 sin_theta = sin(theta);
442 
443  /* compute numerator expansion of h21/h22: N0 + N2 * v**2 + N3 * v**3*/
444  REAL8 N0 = 84.0 * sin_iota;
445  REAL8 N2 = 2.0 * (55.0 * nu - 107.0) * sin_iota;
446  REAL8 N3 = -7.0 * (5.0 * nu + 6.0 * delta + 6.0) * chi * (2.0 * cos_iota - 1.0) * sin_theta + 56.0 * (3.0 * LAL_PI - (1.0 + delta - nu) * chi * cos_theta) * sin_iota;
447 
448  REAL8 N = (N0 + N2 * v2 + N3 * v3) / cos_half_iota;
449 
450  /* compute denominator expansion of h21/h22: D0 + D2 * v**2 + D3 * v**3*/
451  REAL8 D0 = 84.0 * cos_half_iota;
452  REAL8 D2 = 2.0 * (55.0 * nu - 107.0) * cos_half_iota;
453  REAL8 D3 = 14.0 * 4.0 * (3.0 * LAL_PI + (nu - 1.0 - delta) * chi * cos_theta) * cos_half_iota + 14.0 * (6.0 + 6.0 * delta + 5.0 * nu) * chi * sin_theta * sin_half_iota;
454 
455  REAL8 D = D0 + D2 * v2 + D3 * v3;
456 
457  /* evaluate Eq. 41 of arXiv:2107.08876. NOTE: there's a typo in arXiv:2107.08876
458  * whereby the factor of 2 in the denominator of Eq. 39 was dropped. */
459  return 2.0 * XLALSimIMRPhenomXatan2tol(N, 2.0 * D, MAX_TOL_ATAN);
460  }
461 
462  /**
463  * This function evaluates the Ansatz coefficients of beta outlined in
464  * Eq. 49 of arXiv:2107.08876.
465  *
466  * See the discussion in Sec. 8D of arXiv:2107.08876 for an explanation of
467  * the condition on B4.
468  *
469  * The definition of B0 has since changed and now depends on the angle of
470  * the final spin; see discussion in the technical document. FIXME: add citation
471  */
472 
474  IMRPhenomX_PNR_beta_parameters *betaParams, /**< beta parameter struct */
475  IMRPhenomXWaveformStruct *pWF, /**< PhenomX waveform struct */
476  IMRPhenomXPrecessionStruct *pPrec /**< PhenomX precession struct */
477  )
478  {
479 
480  /* get effective single spin parameters */
481  REAL8 eta = pWF->eta;
482  REAL8 chi = pPrec->chi_singleSpin;
484 
485  /* approximate orientation of final spin */
486  REAL8 costhetaf = pPrec->costheta_final_singleSpin;
487 
488  /* ensure B4 is sufficiently large */
490  if (B4 <= 175.0)
491  {
492  B4 = 175.0;
493  }
494 
495  betaParams->B0 = acos(costhetaf) - IMRPhenomX_PNR_beta_B0_coefficient(eta, chi, costheta);
496  betaParams->B1 = IMRPhenomX_PNR_beta_B1_coefficient(eta, chi, costheta);
497  betaParams->B2 = IMRPhenomX_PNR_beta_B2_coefficient(eta, chi, costheta);
498  betaParams->B3 = betaParams->B2 * IMRPhenomX_PNR_beta_B3_coefficient(eta, chi, costheta);
499  betaParams->B4 = B4;
500  betaParams->B5 = IMRPhenomX_PNR_beta_B5_coefficient(eta, chi, costheta);
501 
502  return XLAL_SUCCESS;
503  }
504 
505  /**
506  * These three functions produce the inspiral rescaling
507  * of beta described in Sec. 8B of arXiv:2107.08876.
508  *
509  * - IMRPhenomX_PNR_beta_rescaling_1 computes b1 in Eq. 54
510  * - IMRPhenomX_PNR_beta_rescaling_2 computes b2 in Eq. 55
511  * - IMRPhenomX_PNR_rescale_beta_expression combines the results
512  * to produce Eq. 53.
513  */
514 
516  REAL8 Mf, /**< geometric frequency */
517  REAL8 beta1, /**< PN beta evaluated at Mf */
518  REAL8 beta2, /**< MR beta evaluated at Mf */
519  REAL8 dbeta1, /**< derivative of PN beta at Mf */
520  REAL8 dbeta2 /**< derivative of MR beta at Mf */
521  )
522  {
523 
524  REAL8 D = beta1 * beta1 * Mf;
525  REAL8 N = -2.0 * beta1 * (beta2 - beta1) + (beta1 * dbeta2 - beta2 * dbeta1) * Mf;
526 
527  return -N / D;
528  }
529 
531  REAL8 Mf, /**< geometric frequency */
532  REAL8 beta1, /**< PN beta evaluated at Mf */
533  REAL8 beta2, /**< MR beta evaluated at Mf */
534  REAL8 dbeta1, /**< derivative of PN beta at Mf */
535  REAL8 dbeta2 /**< derivative of MR beta at Mf */
536  )
537  {
538 
539  REAL8 D = (beta1 * Mf) * (beta1 * Mf);
540  REAL8 N = beta1 * (beta2 - beta1) - (beta1 * dbeta2 - beta2 * dbeta1) * Mf;
541 
542  return -N / D;
543  }
544 
546  REAL8 Mf, /**< geometric frequency */
547  const IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
548  )
549  {
550 
551  REAL8 b1 = betaParams->beta_rescale_1;
552  REAL8 b2 = betaParams->beta_rescale_2;
553 
554  return 1.0 + b1 * Mf + b2 * Mf * Mf;
555  }
556 
557  /**
558  * These four functions produce the MR Ansatz
559  * of beta described in Sec. 7A of arXiv:2107.08876.
560  *
561  * - IMRPhenomX_PNR_MR_beta_expression computes the MR Ansatz
562  * detailed in Eq. 49
563  * - IMRPhenomX_PNR_MR_dbeta_expression computes the first derivative of Eq. 49
564  * - IMRPhenomX_PNR_MR_ddbeta_expression computes the second derivative of Eq. 49
565  * - IMRPhenomX_PNR_MR_dddbeta_expression computes the third derivative of Eq. 49
566  */
568  REAL8 Mf, /**< geometric frequency */
569  const IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
570  )
571  {
572 
573  REAL8 B0 = betaParams->B0;
574  REAL8 B1 = betaParams->B1;
575  REAL8 B2 = betaParams->B2;
576  REAL8 B3 = betaParams->B3;
577  REAL8 B4 = betaParams->B4;
578  REAL8 B5 = betaParams->B5;
579 
580  return B0 + (B1 + B2 * Mf + B3 * Mf * Mf) / (1.0 + B4 * (Mf + B5) * (Mf + B5));
581  }
582 
583  /**
584  * expression for first derivative of beta in merger-ringdown regime
585  */
587  REAL8 Mf, /**< geometric frequency */
588  const IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
589  )
590  {
591 
592  REAL8 B1 = betaParams->B1;
593  REAL8 B2 = betaParams->B2;
594  REAL8 B3 = betaParams->B3;
595  REAL8 B4 = betaParams->B4;
596  REAL8 B5 = betaParams->B5;
597 
598  return ((2.0 * B3 * B4 * B5 - B2 * B4) * Mf * Mf + (2.0 * B3 - 2.0 * B1 * B4 + 2.0 * B3 * B4 * B5 * B5) * Mf + (B2 - 2.0 * B1 * B4 * B5 + B2 * B4 * B5 * B5)) / pow((1.0 + B4 * (Mf + B5) * (Mf + B5)), 2);
599  }
600 
601  /**
602  * expression for second derivative of beta in merger-ringdown regime
603  */
605  REAL8 Mf, /**< geometric frequency */
606  const IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
607  )
608  {
609 
610  REAL8 B1 = betaParams->B1;
611  REAL8 B2 = betaParams->B2;
612  REAL8 B3 = betaParams->B3;
613  REAL8 B4 = betaParams->B4;
614  REAL8 B5 = betaParams->B5;
615 
616  REAL8 a = B2 * B4 * B4 - 2.0 * B3 * B4 * B4 * B5;
617  REAL8 b = -3.0 * B3 * B4 + 3.0 * B1 * B4 * B4 - 3.0 * B3 * B4 * B4 * B5 * B5;
618  REAL8 c = -3.0 * B2 * B4 + 6.0 * B1 * B4 * B4 * B5 - 3.0 * B2 * B4 * B4 * B5 * B5;
619  REAL8 d = B3 - B1 * B4 - 2.0 * B2 * B4 * B5 + 2.0 * B3 * B4 * B5 * B5 + 3.0 * B1 * B4 * B4 * B5 * B5 - 2.0 * B2 * B4 * B4 * B5 * B5 * B5 + B3 * B4 * B4 * B5 * B5 * B5 * B5;
620 
621  return 2.0 * (a * Mf * Mf * Mf + b * Mf * Mf + c * Mf + d) / pow((1.0 + B4 * (B5 + Mf) * (B5 + Mf)), 3);
622  }
623 
624  /**
625  * expression for third derivative of beta in merger-ringdown regime
626  */
628  REAL8 Mf, /**< geometric frequency */
629  const IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
630  )
631  {
632 
633  REAL8 B1 = betaParams->B1;
634  REAL8 B2 = betaParams->B2;
635  REAL8 B3 = betaParams->B3;
636  REAL8 B4 = betaParams->B4;
637  REAL8 B5 = betaParams->B5;
638 
639  REAL8 a = -B2 * B4 * B4 + 2.0 * B3 * B4 * B4 * B5;
640  REAL8 b = 4.0 * B3 * B4 - 4.0 * B1 * B4 * B4 + 4.0 * B3 * B4 * B4 * B5 * B5;
641  REAL8 c = 6.0 * B2 * B4 - 12.0 * B1 * B4 * B4 * B5 + 6.0 * B2 * B4 * B4 * B5 * B5;
642  REAL8 d = -4.0 * B3 + 4.0 * B1 * B4 + 8.0 * B2 * B4 * B5 - 8.0 * B3 * B4 * B5 * B5 - 12.0 * B1 * B4 * B4 * B5 * B5 + 8.0 * B2 * B4 * B4 * B5 * B5 * B5 - 4.0 * B3 * B4 * B4 * B5 * B5 * B5 * B5;
643  REAL8 e = -B2 - 2.0 * B3 * B5 + 4.0 * B1 * B4 * B5 + 2.0 * B2 * B4 * B5 * B5 - 4.0 * B3 * B4 * B5 * B5 * B5 - 4.0 * B1 * B4 * B4 * B5 * B5 * B5 + 3.0 * B2 * B4 * B4 * B5 * B5 * B5 * B5 - 2.0 * B3 * B4 * B4 * B5 * B5 * B5 * B5 * B5;
644 
645  return 6.0 * B4 * (a * Mf * Mf * Mf * Mf + b * Mf * Mf * Mf + c * Mf * Mf + d * Mf + e) / pow((1.0 + B4 * (B5 + Mf) * (B5 + Mf)), 4);
646  }
647 
648  /**
649  * Here we work through the construction of the connection frequency
650  * for beta, outlined in Sec. 8B of arXiv:2107.08876, along with
651  * discussion in Sec. 8D.
652  *
653  * In particular, this function performs the following tasks in order:
654  * - compute the inflection frequency required to get beta_inf
655  * - get derivative of beta at that frequency
656  * - get the extremal frequencies of beta
657  * - choose the lower and upper connection frequencies
658  *
659  */
661  IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
662  )
663  {
664  /* check for initialization */
665  XLAL_CHECK(betaParams != NULL, XLAL_EFAULT);
666 
667  REAL8 B1 = betaParams->B1;
668  REAL8 B2 = betaParams->B2;
669  REAL8 B3 = betaParams->B3;
670  REAL8 B4 = betaParams->B4;
671  REAL8 B5 = betaParams->B5;
672 
673  REAL8 Mf_inflection;
674  REAL8 root_term;
675  REAL8 Mf_plus;
676  REAL8 Mf_minus;
677  REAL8 ddbeta_Mf_plus;
678  REAL8 Mf_at_minimum;
679  REAL8 Mf_at_maximum;
680  REAL8 Mf_low;
681  REAL8 Mf_IM;
682  REAL8 Mf_MR;
683  REAL8 ddbeta;
684  REAL8 dddbeta;
685  REAL8 dbeta_inflection;
686  REAL8 chosen_dbeta;
687  REAL8 d1;
688  REAL8 d2;
689  REAL8 delta;
690 
691  /* calculate inflection frequency to get beta_inf */
692  Mf_inflection = IMRPhenomX_PNR_single_inflection_point(betaParams);
693 
694  /* calculate the derivative of beta at the inflection, dbeta_inf*/
695  dbeta_inflection = IMRPhenomX_PNR_MR_dbeta_expression(Mf_inflection, betaParams);
696 
697  /* next we seek to compute Mf_max used in Eq. 57 of arXiv:2107.08876 */
698  /* start by calculating the two extrema of beta FIXME: add documentation */
699  root_term = B4 * (B2 - 2.0 * B3 * B5) * (B2 - 2.0 * B1 * B4 * B5 + B2 * B4 * B5 * B5) + (B3 - B1 * B4 + B3 * B4 * B5 * B5) * (B3 - B1 * B4 + B3 * B4 * B5 * B5);
700 
701  Mf_plus = ((B3 - B1 * B4 + B3 * B4 * B5 * B5) + sqrt(root_term)) / (B4 * (B2 - 2.0 * B3 * B5));
702  Mf_minus = ((B3 - B1 * B4 + B3 * B4 * B5 * B5) - sqrt(root_term)) / (B4 * (B2 - 2.0 * B3 * B5));
703 
704  /* classify extrema as either maximum or minimum depending on sign of second derivative */
705  ddbeta_Mf_plus = IMRPhenomX_PNR_MR_ddbeta_expression(Mf_plus, betaParams);
706 
707  if (ddbeta_Mf_plus > 0.0)
708  {
709  /* smiley face, Mf_plus is the minimum */
710  Mf_at_minimum = Mf_plus;
711  Mf_at_maximum = Mf_minus;
712  }
713  else
714  {
715  /* frowny face, Mf_plus is the maximum */
716  Mf_at_minimum = Mf_minus;
717  Mf_at_maximum = Mf_plus;
718  }
719 
720  /* calculate the second and third derivatives at the maximum frequency */
721  ddbeta = IMRPhenomX_PNR_MR_ddbeta_expression(Mf_at_maximum, betaParams);
722  dddbeta = IMRPhenomX_PNR_MR_dddbeta_expression(Mf_at_maximum, betaParams);
723 
724  /* ensure that the sign of dbeta_inflection is maintained in Eq. 56 of arXiv:2107.08876,
725  * even though dbeta_inflection is squared */
726  REAL8 sign = 0.0;
727  if (dbeta_inflection > 0.0)
728  {
729  sign = +1.0;
730  }
731  else
732  {
733  sign = -1.0;
734  }
735  /* compute Eq. 56 of arXiv:2107.08876 */
736  chosen_dbeta = sign * ((dbeta_inflection / 100.0) * (dbeta_inflection / 100.0)) * 25.0;
737 
738  /* compute the two possible square roots for Eq. 57 of arXiv:2107.08876 */
739  d1 = 1.0 / dddbeta * (-ddbeta + sqrt(ddbeta * ddbeta + 2.0 * dddbeta * chosen_dbeta));
740  d2 = 1.0 / dddbeta * (-ddbeta - sqrt(ddbeta * ddbeta + 2.0 * dddbeta * chosen_dbeta));
741 
742  /* select the most appropriate case. The logic is as follows:
743  * - if both d1 and d2 are positive, select the smallest
744  * - if d2 < 0 and d1 > 0, choose d1
745  * - if d1 < 0, choose d2 */
746  if (d1 > 0.0)
747  {
748  if (d2 > 0.0)
749  {
750  /* choose the smallest positive solution */
751  delta = (d1 < d2) ? d1 : d2;
752  }
753  else
754  {
755  delta = d1;
756  }
757  }
758  else
759  {
760  delta = d2;
761  }
762 
763  /* specify the connection frequency based on Eq. 58 of arXiv:2107.08876
764  * for cases where the turnover is not present within the fitting region.
765  * We will decide whether to use this or not below. */
766  if (Mf_inflection >= 0.06)
767  {
768  Mf_low = Mf_inflection - 0.03;
769  }
770  else
771  {
772  Mf_low = 3.0 * Mf_inflection / 5.0;
773  }
774 
775  /* quantify the shape of beta for this case: see Fig. 10 of arXiv:2107.08876 for
776  * visualization of the three different cases.
777  *
778  * We start by checking to see if the minimum happens at a higher frequency to the maximum
779  * and if the chosen inflection point is also at higher frequency than the maximum. In this case,
780  * we are in the first two panels of Fig. 10. */
781  if ((Mf_at_minimum > Mf_at_maximum) || (Mf_inflection > Mf_at_maximum))
782  {
783  /* If the maximum occurs at a higher frequency than in Eq. 58, pick Eq. 57 as the connection frequency.
784  * Otherwise use Eq. 58. */
785  if (Mf_at_maximum >= Mf_low)
786  {
787  Mf_IM = Mf_at_maximum + delta;
788  }
789  else
790  {
791  Mf_IM = Mf_low;
792  }
793  }
794  else /* Here we are in the right-most panel of Fig. 10 */
795  {
796  /* ensure that we have enough room to start below the minimum frequency */
797  if (Mf_at_minimum > 0.06)
798  {
799  Mf_IM = Mf_at_minimum - 0.03;
800  }
801  else
802  {
803  Mf_IM = 3.0 * Mf_at_minimum / 5.0;
804  }
805  }
806 
807  /* Specify the upper connection frequency where we either transition to a constant
808  * value for beta at the minimum to enforce zero slope, or we are in the first panel
809  * of Fig. 10 where beta will asymptote to some value.
810  * In the latter case, we simply set the upper connection frequency to be very large */
811  if (Mf_at_minimum > Mf_inflection)
812  {
813  Mf_MR = Mf_at_minimum;
814  }
815  else
816  {
817  Mf_MR = 100.;
818  }
819 
820  /* failsafe if the lower connection frequency is negative, then we won't be using the MR fit */
821  if ((Mf_IM < 0.0) || isnan(Mf_IM))
822  {
823  betaParams->Mf_beta_lower = 100.0;
824  betaParams->Mf_beta_upper = 100.0;
825  }
826  else
827  {
828  betaParams->Mf_beta_lower = Mf_IM;
829  betaParams->Mf_beta_upper = Mf_MR;
830  }
831 
832  return XLAL_SUCCESS;
833  }
834 
835  /**
836  * Compute the three roots of a depressed cubic given by
837  * Eq. 68 of arXiv:2107.08876.
838  *
839  */
841  const IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
842  )
843  {
844 
845  REAL8 B1 = betaParams->B1;
846  REAL8 B2 = betaParams->B2;
847  REAL8 B3 = betaParams->B3;
848  REAL8 B4 = betaParams->B4;
849  REAL8 B5 = betaParams->B5;
850 
851  COMPLEX16 a;
852  COMPLEX16 b;
853  COMPLEX16 c;
854  COMPLEX16 d;
855  COMPLEX16 r;
856  COMPLEX16 s;
857  COMPLEX16 phi;
858  static COMPLEX16 f[3];
859 
860  /* cubic ax^3 + bx^2 + cx + d FIXME: add documentation */
861  a = 2 * (B2 * B4 * B4 - 2 * B3 * B4 * B4 * B5);
862  b = 6 * (-B3 * B4 + B1 * B4 * B4 - B3 * B4 * B4 * B5 * B5);
863  c = 6 * (-B2 * B4 + 2 * B1 * B4 * B4 * B5 - B2 * B4 * B4 * B5 * B5);
864  d = 2 * (B3 - B1 * B4 - 2 * B2 * B4 * B5 + 2 * B3 * B4 * B5 * B5 + 3 * B1 * B4 * B4 * B5 * B5 - 2 * B2 * B4 * B4 * B5 * B5 * B5 + B3 * B4 * B4 * B5 * B5 * B5 * B5);
865 
866  /* convert to depressed cubic t^3 + rt +s, t = x + b/3a */
867  r = (3 * a * c - b * b) / (3 * a * a);
868  s = (2 * b * b * b - 9 * a * b * c + 27 * a * a * d) / (27 * a * a * a);
869 
870  phi = acos(((3 * s) / (2 * r)) * csqrt(-3 / r));
871 
872  for (int n = 0; n < 3; n++)
873  {
874  f[n] = 2 * csqrt(-r / 3) * cos((phi - 2 * n * LAL_PI) / 3) - b / (3 * a);
875  }
876 
877  return f;
878  }
879 
880  /**
881  * Compute the two roots of a depressed cubic given by
882  * Eq. 68 of arXiv:2107.08876 when the cubic term vanishes.
883  *
884  */
886  const IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
887  )
888  {
889 
890  REAL8 B1 = betaParams->B1;
891  REAL8 B2 = betaParams->B2;
892  REAL8 B3 = betaParams->B3;
893  REAL8 B4 = betaParams->B4;
894  REAL8 B5 = betaParams->B5;
895 
896  REAL8 b;
897  REAL8 c;
898  REAL8 d;
899 
900  static COMPLEX16 f[2];
901 
902  /* cubic ax^3 + bx^2 + cx + d, here a=0 */
903  b = 6 * (-B3 * B4 + B1 * B4 * B4 - B3 * B4 * B4 * B5 * B5);
904  c = 6 * (-B2 * B4 + 2 * B1 * B4 * B4 * B5 - B2 * B4 * B4 * B5 * B5);
905  d = 2 * (B3 - B1 * B4 - 2 * B2 * B4 * B5 + 2 * B3 * B4 * B5 * B5 + 3 * B1 * B4 * B4 * B5 * B5 - 2 * B2 * B4 * B4 * B5 * B5 * B5 + B3 * B4 * B4 * B5 * B5 * B5 * B5);
906 
907  f[0] = (1 / (2 * b)) * (-c + csqrt(c * c - 4 * b * d));
908  f[1] = (1 / (2 * b)) * (-c - csqrt(c * c - 4 * b * d));
909 
910  return f;
911  }
912 
913  /**
914  * Compute the roots of a depressed cubic given by
915  * Eq. 68 of arXiv:2107.08876, then select the correct
916  * root to use for the inflection frequency based on
917  * Eq. 69 and discussion in Sec. 8D.
918  *
919  */
921  const IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
922  )
923  {
924 
925  REAL8 B1 = betaParams->B1;
926  REAL8 B2 = betaParams->B2;
927  REAL8 B3 = betaParams->B3;
928  REAL8 B4 = betaParams->B4;
929  REAL8 B5 = betaParams->B5;
930 
931  // cubic terms
932  REAL8 a;
933  REAL8 b;
934  REAL8 c;
935  REAL8 d;
936 
937  int i;
938 
939  REAL8 grad;
940  REAL8 f_inflection = 0.0;
941 
942  /* cubic ax^3 + bx^2 + cx + d */
943  a = 2.0 * (B2 * B4 * B4 - 2.0 * B3 * B4 * B4 * B5);
944  b = 6.0 * (-B3 * B4 + B1 * B4 * B4 - B3 * B4 * B4 * B5 * B5);
945  c = 6.0 * (-B2 * B4 + 2.0 * B1 * B4 * B4 * B5 - B2 * B4 * B4 * B5 * B5);
946  d = 2.0 * (B3 - B1 * B4 - 2.0 * B2 * B4 * B5 + 2.0 * B3 * B4 * B5 * B5 + 3.0 * B1 * B4 * B4 * B5 * B5 - 2.0 * B2 * B4 * B4 * B5 * B5 * B5 + B3 * B4 * B4 * B5 * B5 * B5 * B5);
947 
948  /* treat cases where co-efficients of cubic term is zero (or less than 10^-10 to account for numerical error) */
949  if (fabs(a) < 1e-10)
950  {
951  /* co-efficient of quadratic term is also zero */
952  if (fabs(b) < 2e-10)
953  {
954  /* calculate single inflection point */
955  f_inflection = -d / c;
956  }
957  else
958  {
959  COMPLEX16 *f_inf;
960 
961  /* calculate 2 inflection points */
962  f_inf = IMRPhenomX_PNR_two_inflection_points(betaParams);
963 
964  /* choose inflection point with negative gradient */
965  for (i = 0; i < 2; i++)
966  {
967  grad = IMRPhenomX_PNR_MR_dbeta_expression(f_inf[i], betaParams);
968  if (grad < 0.0)
969  {
970  f_inflection = f_inf[i];
971  }
972  }
973  }
974  }
975  /* treat cases with 3 roots */
976  else
977  {
978  COMPLEX16 *f_inf;
979  REAL8 f_temp = 0;
980  REAL8 f_IM = 0;
981  int w = 0; // initialise counter
982 
983  /* calculate all 3 inflection points */
984  f_inf = IMRPhenomX_PNR_three_inflection_points(betaParams);
985 
986  /* check for substantial imaginary component and select real root if this component exists */
987  for (i = 0; i < 3; i++)
988  {
989  f_IM = cimag(f_inf[i]);
990  if (f_IM < 1e-10)
991  {
992  w += 1;
993  f_temp = creal(f_inf[i]);
994  }
995  }
996  /* case where we have just one real root */
997  if (w == 1)
998  {
999  f_inflection = f_temp;
1000  }
1001  /* case where we have all 3 real roots */
1002  else
1003  {
1004  if (a < 0.0)
1005  {
1006  /* take the central root */
1007  f_inflection = creal(f_inf[1]);
1008  }
1009  else
1010  {
1011  /* enforce Eq. 69 */
1012  if (b / (3.0 * a) > B5 / 2.0 - (2141.0 / 90988.0)) // FIXME: add documentation
1013  {
1014  f_inflection = creal(f_inf[0]);
1015  }
1016  else
1017  {
1018  f_inflection = creal(f_inf[2]);
1019  }
1020  }
1021  }
1022  }
1023 
1024  return f_inflection;
1025  }
1026 
1027  /**
1028  * This function combines several functions together to
1029  * compute the connection frequencies and the inspiral rescaling.
1030  *
1031  */
1033  IMRPhenomX_PNR_beta_parameters *betaParams, /**< beta parameter struct */
1034  IMRPhenomXWaveformStruct *pWF, /**< PhenomX waveform struct */
1035  IMRPhenomXPrecessionStruct *pPrec, /**< PhenomX precession struct */
1036  IMRPhenomXWaveformStruct *pWF_SingleSpin, /**< PhenomX waveform struct with single-spin mapping */
1037  IMRPhenomXPrecessionStruct *pPrec_SingleSpin /**< PhenomX precession struct with single-spin mapping */
1038  )
1039  {
1040  /* may we continue? Single-spin structs might be NULL */
1041  XLAL_CHECK(betaParams != NULL, XLAL_EFAULT);
1042  XLAL_CHECK(pWF != NULL, XLAL_EFAULT);
1043  XLAL_CHECK(pPrec != NULL, XLAL_EFAULT);
1044 
1045  /* define frequency spacing over which to calculate derivatives */
1046  /* here set to be 0.0005Mf */
1047  REAL8 dMf = 0.0005;
1048 
1049  /* generate beta connection frequencies*/
1051 
1052  /* evaluate expressions for beta at lower connection frequency */
1053  REAL8 Mf_beta_lower = betaParams->Mf_beta_lower;
1054 
1055  /* get the values of PN and MR beta at Mf_beta_lower, as well as around that
1056  * frequency for the centered FD derivatives */
1057  REAL8 beta_temp = IMRPhenomX_PNR_GetPNBetaAtFreq(Mf_beta_lower - dMf, betaParams, pWF, pPrec, pWF_SingleSpin, pPrec_SingleSpin);
1058  REAL8 b1 = IMRPhenomX_PNR_PNWaveformBetaWrapper(Mf_beta_lower - dMf, beta_temp, pWF, pPrec);
1059 
1060  beta_temp = IMRPhenomX_PNR_GetPNBetaAtFreq(Mf_beta_lower, betaParams, pWF, pPrec, pWF_SingleSpin, pPrec_SingleSpin);
1061  REAL8 beta_lower = IMRPhenomX_PNR_PNWaveformBetaWrapper(Mf_beta_lower, beta_temp, pWF, pPrec);
1062 
1063  beta_temp = IMRPhenomX_PNR_GetPNBetaAtFreq(Mf_beta_lower + dMf, betaParams, pWF, pPrec, pWF_SingleSpin, pPrec_SingleSpin);
1064  REAL8 b3 = IMRPhenomX_PNR_PNWaveformBetaWrapper(Mf_beta_lower + dMf, beta_temp, pWF, pPrec);
1065 
1066  REAL8 b4 = IMRPhenomX_PNR_MR_beta_expression(Mf_beta_lower - dMf, betaParams);
1067  REAL8 beta_upper = IMRPhenomX_PNR_MR_beta_expression(Mf_beta_lower, betaParams);
1068  REAL8 b6 = IMRPhenomX_PNR_MR_beta_expression(Mf_beta_lower + dMf, betaParams);
1069 
1070  /* calculate derivative of alpha at connection frequencies using central finite difference method */
1071  REAL8 derivative_beta_lower = (b3 - b1) / (2.0 * dMf);
1072  REAL8 derivative_beta_upper = (b6 - b4) / (2.0 * dMf);
1073 
1074  /* compute the inspiral rescaling */
1075  REAL8 beta_rescale_1 = IMRPhenomX_PNR_beta_rescaling_1(Mf_beta_lower, beta_lower, beta_upper, derivative_beta_lower, derivative_beta_upper);
1076  REAL8 beta_rescale_2 = IMRPhenomX_PNR_beta_rescaling_2(Mf_beta_lower, beta_lower, beta_upper, derivative_beta_lower, derivative_beta_upper);
1077 
1078  beta_rescale_1 = isnan(beta_rescale_1) ? 0.0 : beta_rescale_1;
1079  beta_rescale_2 = isnan(beta_rescale_2) ? 0.0 : beta_rescale_2;
1080 
1081  /* save beta values to the parameter dict */
1082  betaParams->beta_lower = beta_lower;
1083  betaParams->beta_upper = beta_upper;
1084  betaParams->derivative_beta_lower = derivative_beta_lower;
1085  betaParams->derivative_beta_upper = derivative_beta_upper;
1086  betaParams->beta_rescale_1 = beta_rescale_1;
1087  betaParams->beta_rescale_2 = beta_rescale_2;
1088 
1089  return XLAL_SUCCESS;
1090  }
1091 
1092  /**
1093  * Utility function to compute the arctan windowing described
1094  * in Eq. 62 of arXiv:2107.08876.
1095  *
1096  */
1098  REAL8 beta /**< beta angle (rad) */
1099  )
1100  {
1101  /* specify the width as described in Sec. 8C of arXiv:2107.08876*/
1102  REAL8 window_border = 0.01;
1103 
1104  /* if beta is close to zero or PI, window it */
1105  if ((beta <= window_border) || (beta >= LAL_PI - window_border))
1106  {
1107  /* precompute some things, then evaluate the arctan */
1108  REAL8 p = 0.002;
1109  REAL8 PI_by_2 = 1.570796326794897;
1110  REAL8 PI_by_2_1mp = 1.569378278348018;
1111  REAL8 PI_by_2_1oq = 7.308338225719002e97;
1112  REAL8 sign = (beta < PI_by_2) ? -1.0 : 1.0;
1113 
1114  return sign * PI_by_2_1mp * pow(atan2(pow(beta - PI_by_2, 1.0 / p), PI_by_2_1oq), p) + PI_by_2;
1115  }
1116 
1117  return beta;
1118  }
1119 
1120  /**
1121  * Determine whether to attach the MR contributions to beta.
1122  * - If the connection frequency is too low, < 0.009
1123  * - If the connection frequency is 100.0
1124  * - if the final Ansatz value of beta is negative at the
1125  * lower connection frequency
1126  *
1127  */
1129  const IMRPhenomX_PNR_beta_parameters *betaParams /**< beta parameter struct */
1130  )
1131  {
1132  /* check for initialization */
1133  XLAL_CHECK(betaParams != NULL, XLAL_EFAULT);
1134 
1135  if ((betaParams->Mf_beta_lower < 0.009) || (betaParams->Mf_beta_lower == 100.0) || (betaParams->beta_upper <= 0.0) || (betaParams->beta_upper > 5.0 * (betaParams->B0 + 0.1)))
1136  {
1137  return 0;
1138  }
1139 
1140  return 1;
1141  }
1142 
1143 #ifdef __cplusplus
1144 }
1145 #endif
const double b1
const double b2
#define MAX_TOL_ATAN
Tolerance used below which numbers are treated as zero for the calculation of atan2.
static double double delta
REAL8 IMRPhenomX_PNR_MR_dbeta_expression(REAL8 Mf, const IMRPhenomX_PNR_beta_parameters *betaParams)
expression for first derivative of beta in merger-ringdown regime
UINT4 IMRPhenomX_PNR_AttachMRBeta(const IMRPhenomX_PNR_beta_parameters *betaParams)
Determine whether to attach the MR contributions to beta.
COMPLEX16 * IMRPhenomX_PNR_three_inflection_points(const IMRPhenomX_PNR_beta_parameters *betaParams)
Compute the three roots of a depressed cubic given by Eq.
REAL8 IMRPhenomX_PNR_MR_ddbeta_expression(REAL8 Mf, const IMRPhenomX_PNR_beta_parameters *betaParams)
expression for second derivative of beta in merger-ringdown regime
REAL8 IMRPhenomX_PNR_GeneratePNRBetaNoMR(REAL8 Mf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
This function evaluates only the rescaled inspiral beta given in Eq.
REAL8 IMRPhenomX_PNR_MR_dddbeta_expression(REAL8 Mf, const IMRPhenomX_PNR_beta_parameters *betaParams)
expression for third derivative of beta in merger-ringdown regime
REAL8 IMRPhenomX_PNR_GenerateRingdownPNRBeta(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
We evaluate beta at the final Mf_beta_upper connection frequency to approximate the final value of be...
REAL8 IMRPhenomX_PNR_GetPNBetaAtFreq(REAL8 Mf, const IMRPhenomX_PNR_beta_parameters *betaParams, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, IMRPhenomXWaveformStruct *pWF_SingleSpin, IMRPhenomXPrecessionStruct *pPrec_SingleSpin)
A wrapper to produce either the NNLO or MSA beta depending on the IMRPhenomXPrecVersion.
REAL8 IMRPhenomX_PNR_arctan_window(REAL8 beta)
Utility function to compute the arctan windowing described in Eq.
int IMRPhenomX_PNR_precompute_beta_coefficients(IMRPhenomX_PNR_beta_parameters *betaParams, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
This function evaluates the Ansatz coefficients of beta outlined in Eq.
REAL8 IMRPhenomX_PNR_PNWaveformBetaWrapper(REAL8 Mf, REAL8 pn_beta, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
A wrapper to generate the "waveform" PN beta from Eq.
REAL8 IMRPhenomX_PNR_MR_beta_expression(REAL8 Mf, const IMRPhenomX_PNR_beta_parameters *betaParams)
These four functions produce the MR Ansatz of beta described in Sec.
REAL8 IMRPhenomX_PNR_GetPNBetaAtFreq_fulltwospin(REAL8 Mf, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
A wrapper to produce either the NNLO or MSA beta depending on the IMRPhenomXPrecVersion.
REAL8 IMRPhenomX_PNR_beta_rescaling_2(REAL8 Mf, REAL8 beta1, REAL8 beta2, REAL8 dbeta1, REAL8 dbeta2)
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_chi_calc(REAL8 m1, REAL8 L, REAL8 J0, REAL8 L0, REAL8 chi_parr, REAL8 beta)
The magnitude of the effective total spin is computed from the total and orbital angular momenta,...
int IMRPhenomX_PNR_BetaConnectionFrequencies(IMRPhenomX_PNR_beta_parameters *betaParams)
Here we work through the construction of the connection frequency for beta, outlined in Sec.
int IMRPhenomX_PNR_beta_connection_parameters(IMRPhenomX_PNR_beta_parameters *betaParams, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec, IMRPhenomXWaveformStruct *pWF_SingleSpin, IMRPhenomXPrecessionStruct *pPrec_SingleSpin)
This function combines several functions together to compute the connection frequencies and the inspi...
REAL8 IMRPhenomX_PNR_rescale_beta_expression(REAL8 Mf, const IMRPhenomX_PNR_beta_parameters *betaParams)
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.
REAL8 IMRPhenomX_PNR_beta_rescaling_1(REAL8 Mf, REAL8 beta1, REAL8 beta2, REAL8 dbeta1, REAL8 dbeta2)
These three functions produce the inspiral rescaling of beta described in Sec.
COMPLEX16 * IMRPhenomX_PNR_two_inflection_points(const IMRPhenomX_PNR_beta_parameters *betaParams)
Compute the two roots of a depressed cubic given by Eq.
REAL8 IMRPhenomX_PNR_PNWaveformBeta(REAL8 Mf, REAL8 iota, REAL8 m1, REAL8 m2, REAL8 chi, REAL8 costheta)
The "waveform" PN beta from Eq.
REAL8 IMRPhenomX_PNR_single_inflection_point(const IMRPhenomX_PNR_beta_parameters *betaParams)
Compute the roots of a depressed cubic given by Eq.
REAL8 IMRPhenomX_PNR_beta_B4_coefficient(REAL8 eta, REAL8 chi, REAL8 costheta)
REAL8 IMRPhenomX_PNR_beta_B1_coefficient(REAL8 eta, REAL8 chi, REAL8 costheta)
REAL8 IMRPhenomX_PNR_beta_B5_coefficient(REAL8 eta, REAL8 chi, REAL8 costheta)
REAL8 IMRPhenomX_PNR_beta_B3_coefficient(REAL8 eta, REAL8 chi, REAL8 costheta)
REAL8 IMRPhenomX_PNR_beta_B2_coefficient(REAL8 eta, REAL8 chi, REAL8 costheta)
REAL8 IMRPhenomX_PNR_beta_Bf_coefficient(REAL8 eta, REAL8 chi, REAL8 costheta)
REAL8 IMRPhenomX_PNR_beta_B0_coefficient(REAL8 eta, REAL8 chi, REAL8 costheta)
expressions for each of the co-efficients that appear in the merger-ringdown expression for beta
REAL8 IMRPhenomX_PNR_AnglesWindow(IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
INT4 IMRPhenomX_PNR_CheckTwoSpin(IMRPhenomXPrecessionStruct *pPrec)
This function quickly checks to see if we expect two-spin effects to be present in the inspiral prece...
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.
vector IMRPhenomX_Return_phi_zeta_costhetaL_MSA(const double v, IMRPhenomXWaveformStruct *pWF, IMRPhenomXPrecessionStruct *pPrec)
Wrapper to generate , and at a given frequency.
REAL8 XLALSimIMRPhenomXatan2tol(REAL8 a, REAL8 b, REAL8 tol)
INT4 D0(REAL8 *f, REAL8 dx, INT4 n, REAL8 *df)
XLALWignerdMatrix, XLALWignerDMatrix in <lal/SphericalHarmonics.h>
INT4 D2(REAL8 *f, REAL8 dx, INT4 n, REAL8 *d2f)
static vector d(const double L_norm, const double J_norm, const vector roots)
Internal function that returns the coefficients "d_0", "d_2" and "d_4" from 1703.03967 corresponding ...
static double beta(const double a, const double b, const sysq *system)
Internal function that computes the spin-orbit couplings.
#define c
int s
Definition: bh_qnmode.c:137
REAL8 M
Definition: bh_qnmode.c:133
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
double theta
Definition: bh_sphwf.c:118
const double w
const double costheta
#define LAL_PI
double complex COMPLEX16
double REAL8
uint32_t UINT4
static const INT4 r
static const INT4 a
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EINVAL
list p
int N
REAL8 costheta_singleSpin
Polar angle of effective single spin, Eq.
INT4 IMRPhenomXPrecVersion
Flag to set version of Euler angles used.
REAL8 chi_singleSpin
Magnitude of effective single spin used for tapering two-spin angles, Eq.
REAL8 costheta_final_singleSpin
Polar angle of approximate final spin, see technical document FIXME: add reference.