LALSimulation  5.4.0.1-fe68b98
LALSimIMRPhenomInternalUtils.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2017 Sebastian Khan
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 /**
21  * \author Sebastian Khan
22  *
23  * \file
24  *
25  * \brief Internal (not SWIG'd) Auxiliary functions for phenomenological model development
26  *
27  * Helper functions for phenomenological waveform models
28  * Cannot be used through python SWIG wrapping.
29  * NOTE: The convention for naming functions in there is to use
30  * the prefix 'PhenomInternal_'
31  * This is to avoid the use of defining functions as static and
32  * therefore not able to be used by other modules by including
33  * LALSimIMRPhenomInternalUtils.h.
34  */
35 
37 
38 // /**
39 // * Example how to write an internal phenom function
40 // */
41 // void PhenomInternal_UtilsTest(){
42 // printf("Hello! I am the PhenomInternal_UtilsTest function\n");
43 // }
44 
45 // This function determines whether x and y are approximately equal to a relative accuracy epsilon.
46 // Note that x and y are compared to relative accuracy, so this function is not suitable for testing whether a value is approximately zero.
47 
48 /**
49  * This function determines whether x and y are approximately equal to a
50  * relative accuracy epsilon.
51  * Note that x and y are compared to relative accuracy, so this function is not
52  * suitable for testing whether a value is approximately zero.
53  */
55 {
56  return !gsl_fcmp(x, y, epsilon);
57 }
58 
59 /**
60  * If x and X are approximately equal to relative accuracy epsilon
61  * then set x = X.
62  * If X = 0 then use an absolute comparison.
63  */
64 void PhenomInternal_nudge(REAL8 *x, REAL8 X, REAL8 epsilon)
65 {
66  if (X != 0.0)
67  {
68  if (PhenomInternal_approx_equal(*x, X, epsilon))
69  {
70  XLAL_PRINT_INFO("Nudging value %.15g to %.15g\n", *x, X);
71  *x = X;
72  }
73  }
74  else
75  {
76  if (fabs(*x - X) < epsilon)
77  *x = X;
78  }
79 }
80 
81 /**
82  * Return the closest higher power of 2
83  */
84 size_t PhenomInternal_NextPow2(const size_t n)
85 {
86  // use pow here, not bit-wise shift, as the latter seems to run against an upper cutoff long before SIZE_MAX, at least on some platforms
87  return (size_t)pow(2, ceil(log2(n)));
88 }
89 
90 /**
91  * Given m1 with aligned-spin chi1z and m2 with aligned-spin chi2z.
92  * Enforce that m1 >= m2 and swap spins accordingly.
93  * Enforce that the primary object (heavier) is indexed by 1.
94  * To be used with aligned-spin waveform models.
95  * There is another function for precessing waveform models.
96  * This is currently not used
97  */
99  REAL8 *m1, /**< [out] mass of body 1 */
100  REAL8 *m2, /**< [out] mass of body 2 */
101  REAL8 *chi1z, /**< [out] aligned-spin component of body 1 */
102  REAL8 *chi2z /**< [out] aligned-spin component of body 2 */
103 )
104 {
105  REAL8 chi1z_tmp, chi2z_tmp, m1_tmp, m2_tmp;
106  if (*m1 > *m2)
107  {
108  chi1z_tmp = *chi1z;
109  chi2z_tmp = *chi2z;
110  m1_tmp = *m1;
111  m2_tmp = *m2;
112  }
113  else
114  { /* swap spins and masses */
115  chi1z_tmp = *chi2z;
116  chi2z_tmp = *chi1z;
117  m1_tmp = *m2;
118  m2_tmp = *m1;
119  }
120  *m1 = m1_tmp;
121  *m2 = m2_tmp;
122  *chi1z = chi1z_tmp;
123  *chi2z = chi2z_tmp;
124 
125  if (*m1 < *m2)
126  XLAL_ERROR(XLAL_EDOM, "XLAL_ERROR in EnforcePrimaryIsm1. When trying\
127  to enfore that m1 should be the larger mass.\
128  After trying to enforce this m1 = %f and m2 = %f\n", *m1, *m2);
129 
130  return XLAL_SUCCESS;
131 }
132 
133 /**
134  * Given m1 with spins (chi1x, chi1y, chi1z) and m2 with spins (chi2x,chi2y,chi2z).
135  * Enforce that m1 >= m2 and swap spins accordingly.
136  * Enforce that the primary object (heavier) is indexed by 1.
137  * To be used with precessing-spin waveform models.
138  */
140  REAL8 *m1, /**< [out] mass of body 1 */
141  REAL8 *m2, /**< [out] mass of body 2 */
142  REAL8 *chi1x, /**< [out] x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
143  REAL8 *chi1y, /**< [out] y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
144  REAL8 *chi1z, /**< [out] z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
145  REAL8 *chi2x, /**< [out] x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
146  REAL8 *chi2y, /**< [out] y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
147  REAL8 *chi2z /**< [out] z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
148 )
149 {
150  REAL8 m1_tmp, m2_tmp;
151  REAL8 chi1x_tmp, chi1y_tmp, chi1z_tmp;
152  REAL8 chi2x_tmp, chi2y_tmp, chi2z_tmp;
153  if (*m1 > *m2)
154  {
155  chi1x_tmp = *chi1x;
156  chi1y_tmp = *chi1y;
157  chi1z_tmp = *chi1z;
158 
159  chi2x_tmp = *chi2x;
160  chi2y_tmp = *chi2y;
161  chi2z_tmp = *chi2z;
162 
163  m1_tmp = *m1;
164  m2_tmp = *m2;
165  }
166  else
167  { /* swap spins and masses */
168  chi1x_tmp = *chi2x;
169  chi1y_tmp = *chi2y;
170  chi1z_tmp = *chi2z;
171 
172  chi2x_tmp = *chi1x;
173  chi2y_tmp = *chi1y;
174  chi2z_tmp = *chi1z;
175 
176  m1_tmp = *m2;
177  m2_tmp = *m1;
178  }
179  *m1 = m1_tmp;
180  *m2 = m2_tmp;
181  *chi1x = chi1x_tmp;
182  *chi1y = chi1y_tmp;
183  *chi1z = chi1z_tmp;
184 
185  *chi2x = chi2x_tmp;
186  *chi2y = chi2y_tmp;
187  *chi2z = chi2z_tmp;
188 
189  if (*m1 < *m2)
190  XLAL_ERROR(XLAL_EDOM, "XLAL_ERROR in EnforcePrimaryIsm1. When trying\
191  to enfore that m1 should be the larger mass.\
192  After trying to enforce this m1 = %f and m2 = %f\n",
193  *m1, *m2);
194 
195  return XLAL_SUCCESS;
196 }
197 
198 /**
199  * atan2 wrapper that returns 0 when both magnitudes of x and y are
200  * below tol, otherwise it returns atan2(x, y)
201  */
203 {
204  REAL8 c;
205  if (fabs(a) < tol && fabs(b) < tol)
206  c = 0.;
207  else
208  c = atan2(a, b);
209  return c;
210 }
211 
212 
213 /**
214  * function to convert from 3d cartesian components to polar angles and vector magnitude.
215  * https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
216  */
218  REAL8 *polar,
219  REAL8 *azimuthal,
220  REAL8 *magnitude,
221  REAL8 x,
222  REAL8 y,
223  REAL8 z
224 )
225 {
226  /* TODO: check that this is the correct convention */
227  *magnitude = sqrt( x*x + y*y + z*z );
228  if (PhenomInternal_approx_equal(*magnitude, 0, 1e-9)){
229  *polar = 0.;
230  *azimuthal = 0.;
231  } else {
232  *polar = acos( z / *magnitude );
234  }
235 }
236 
237 
238 /**
239  * Wrapper function for XLALOrbitalAngMom3PNSpinning.
240  * Used to compute the orbital angular momentum at 3PN order
241  * at a single frequency.
242  * We assume that Lhat = (0,0,1)
243  */
245  const double f_orb_hz, /**< Orbtial frequency (Hz) */
246  const double m1, /**< mass of primary in SI (kg) */
247  const double m2, /**< mass of secondary in SI (kg) */
248  const double s1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
249  const double s1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
250  const double s1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
251  const double s2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
252  const double s2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
253  const double s2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
254  const double f_0, /**< Reference Gravitational Wave frequency (Hz) */
255  const int ExpansionOrder /**< Keep terms upto ExpansionOrder in precession angles phi_z and zeta (1,2,3,4,5 or -1 for all orders) */
256 )
257 {
258 
259  const double mul = 1.0; /* Cosine of Polar angle of orbital angular momentum */
260  const double phl = 0.0; /* Azimuthal angle of orbital angular momentum */
261  double mu1 = 0.0;
262  double ph1 = 0.0;
263  double ch1 = 0.0;
264  double mu2 = 0.0;
265  double ph2 = 0.0;
266  double ch2 = 0.0;
267 
268  REAL8Sequence *L_norm_3PN_Seq = XLALCreateREAL8Sequence(1);
269  REAL8Sequence *freqs_seq = XLALCreateREAL8Sequence(1);
270 
271  L_norm_3PN_Seq->data[0] = 0.;
272  freqs_seq->data[0] = f_orb_hz;
273 
274  PhenomInternal_ComputeIMRPhenomPv3CartesianToPolar(&mu1, &ph1, &ch1, s1x, s1y, s1z);
275  PhenomInternal_ComputeIMRPhenomPv3CartesianToPolar(&mu2, &ph2, &ch2, s2x, s2y, s2z);
276 
277  int retcode = XLALOrbitalAngMom3PNSpinning(
278  L_norm_3PN_Seq, freqs_seq,
279  m1, m2,
280  mul, phl,
281  cos(mu1), ph1, ch1,
282  cos(mu2), ph2, ch2,
283  f_0, ExpansionOrder);
284  XLAL_CHECK(retcode == XLAL_SUCCESS, XLAL_EFUNC, "XLALOrbitalAngMom3PNSpinning failed.");
285 
286  double L_norm_3PN = L_norm_3PN_Seq->data[0];
287 
288  XLALDestroyREAL8Sequence(L_norm_3PN_Seq);
289  XLALDestroyREAL8Sequence(freqs_seq);
290 
291  return L_norm_3PN;
292 }
293 
294 /**
295  * Formula to predict the total radiated energy. Equation 3.7 and 3.8 arXiv:1508.07250
296  * Input parameter s defined around Equation 3.7 and 3.8.
297  */
298 static double EradRational0815_s(double eta, double s)
299 {
300  double eta2 = eta * eta;
301  double eta3 = eta2 * eta;
302 
303  return (eta * (0.055974469826360077 + 0.5809510763115132 * eta - 0.9606726679372312 * eta2 + 3.352411249771192 * eta3) *
304  (1. + (-0.0030302335878845507 - 2.0066110851351073 * eta + 7.7050567802399215 * eta2) * s)) /
305  (1. + (-0.6714403054720589 - 1.4756929437702908 * eta + 7.304676214885011 * eta2) * s);
306 }
307 
308 /**
309  * Wrapper function for EradRational0815_s.
310  */
311 double PhenomInternal_EradRational0815(double eta, double chi1, double chi2)
312 {
313  // Convention m1 >= m2
314  double Seta = sqrt(1.0 - 4.0 * eta);
315  double m1 = 0.5 * (1.0 + Seta);
316  double m2 = 0.5 * (1.0 - Seta);
317  double m1s = m1 * m1;
318  double m2s = m2 * m2;
319  // arXiv:1508.07250
320  double s = (m1s * chi1 + m2s * chi2) / (m1s + m2s);
321 
322  return EradRational0815_s(eta, s);
323 }
324 
325 /**
326  * helper function to multiple hlm with Ylm.
327  * Adapted from LALSimIMREOBNRv2HMROMUtilities.c
328  */
330  COMPLEX16FrequencySeries *hptilde,
331  COMPLEX16FrequencySeries *hctilde,
332  COMPLEX16FrequencySeries *hlmtilde,
333  REAL8 theta,
334  REAL8 phi,
335  INT4 l,
336  INT4 m,
337  INT4 sym)
338 {
339  COMPLEX16 Y;
340  UINT4 j;
341  COMPLEX16 hlm; /* helper variable that contain a single point of hlmtilde */
342 
343  INT4 minus1l; /* (-1)^l */
344  if (l % 2)
345  minus1l = -1;
346  else
347  minus1l = 1;
348  if (sym)
349  { /* Equatorial symmetry: add in -m mode */
350  Y = XLALSpinWeightedSphericalHarmonic(theta, phi, -2, l, m);
351  COMPLEX16 Ymstar = conj(XLALSpinWeightedSphericalHarmonic(theta, phi, -2, l, -m));
352  COMPLEX16 factorp = 0.5 * (Y + minus1l * Ymstar);
353  COMPLEX16 factorc = -I * 0.5 * (Y - minus1l * Ymstar);
354  for (j = 0; j < hlmtilde->data->length; ++j)
355  {
356  hlm = (hlmtilde->data->data[j]);
357  hptilde->data->data[j] += factorp * hlm;
358  hctilde->data->data[j] += factorc * hlm;
359  }
360  }
361  else
362  { /* not adding in the -m mode */
363  Y = XLALSpinWeightedSphericalHarmonic(theta, phi, -2, l, m);
364  COMPLEX16 factorp = 0.5 * Y;
365  COMPLEX16 factorc = -I * factorp;
366  for (j = 0; j < hlmtilde->data->length; ++j)
367  {
368  hlm = (hlmtilde->data->data[j]);
369  hptilde->data->data[j] += factorp * hlm;
370  hctilde->data->data[j] += factorc * hlm;
371  }
372  }
373 
374  return XLAL_SUCCESS;
375 }
int XLALOrbitalAngMom3PNSpinning(REAL8Sequence *L_norm_3PN, REAL8Sequence *f_orb_hz, const double m1, const double m2, const double mul, const double phl, double mu1, double ph1, double ch1, double mu2, double ph2, double ch2, const double f_0, const int ExpansionOrder)
Standalone function to compute the magnitude of L divided by GMsquare_over_c to 3PN order with spin t...
void PhenomInternal_ComputeIMRPhenomPv3CartesianToPolar(REAL8 *polar, REAL8 *azimuthal, REAL8 *magnitude, REAL8 x, REAL8 y, REAL8 z)
function to convert from 3d cartesian components to polar angles and vector magnitude.
double PhenomInternal_OrbAngMom3PN(const double f_orb_hz, const double m1, const double m2, const double s1x, const double s1y, const double s1z, const double s2x, const double s2y, const double s2z, const double f_0, const int ExpansionOrder)
Wrapper function for XLALOrbitalAngMom3PNSpinning.
double PhenomInternal_EradRational0815(double eta, double chi1, double chi2)
Wrapper function for EradRational0815_s.
int PhenomInternal_IMRPhenomHMFDAddMode(COMPLEX16FrequencySeries *hptilde, COMPLEX16FrequencySeries *hctilde, COMPLEX16FrequencySeries *hlmtilde, REAL8 theta, REAL8 phi, INT4 l, INT4 m, INT4 sym)
helper function to multiple hlm with Ylm.
bool PhenomInternal_approx_equal(REAL8 x, REAL8 y, REAL8 epsilon)
This function determines whether x and y are approximately equal to a relative accuracy epsilon.
int PhenomInternal_AlignedSpinEnforcePrimaryIsm1(REAL8 *m1, REAL8 *m2, REAL8 *chi1z, REAL8 *chi2z)
Given m1 with aligned-spin chi1z and m2 with aligned-spin chi2z.
static double EradRational0815_s(double eta, double s)
Formula to predict the total radiated energy.
size_t PhenomInternal_NextPow2(const size_t n)
Return the closest higher power of 2.
REAL8 PhenomInternal_atan2tol(REAL8 a, REAL8 b, REAL8 tol)
atan2 wrapper that returns 0 when both magnitudes of x and y are below tol, otherwise it returns atan...
int PhenomInternal_PrecessingSpinEnforcePrimaryIsm1(REAL8 *m1, REAL8 *m2, REAL8 *chi1x, REAL8 *chi1y, REAL8 *chi1z, REAL8 *chi2x, REAL8 *chi2y, REAL8 *chi2z)
Given m1 with spins (chi1x, chi1y, chi1z) and m2 with spins (chi2x,chi2y,chi2z).
void PhenomInternal_nudge(REAL8 *x, REAL8 X, REAL8 epsilon)
If x and X are approximately equal to relative accuracy epsilon then set x = X.
#define PhenomInternal_MAX_TOL_ATAN
Tolerance used below which numbers are treated as zero for the calculation of atan2.
#define c
int s
Definition: bh_qnmode.c:137
int l
Definition: bh_qnmode.c:135
double e
Definition: bh_ringdown.c:117
double theta
Definition: bh_sphwf.c:118
double complex COMPLEX16
double REAL8
uint32_t UINT4
int32_t INT4
static const INT4 m
static const INT4 a
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
COMPLEX16 XLALSpinWeightedSphericalHarmonic(REAL8 theta, REAL8 phi, int s, int l, int m)
#define XLAL_PRINT_INFO(...)
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
XLAL_SUCCESS
XLAL_EFUNC
XLAL_EDOM
list x
list y
COMPLEX16Sequence * data
COMPLEX16 * data
REAL8 * data