LALSimulation  5.4.0.1-fe68b98
LALSimIMRPhenomPv3HM.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 PhenomPv3HM model
26  *
27  * Inspiral-merger and ringdown phenomenological, frequecny domain
28  * waveform model for spinning precessing binary black holes systems.
29  * Models not only the dominant (l,|m|) = (2,2) modes
30  * but also some of the sub-domant modes too in the co-precessing frame.
31  *
32  * The model has been validated against precessing NR simulations up to mass-ratio 6
33  * but due to lack of precessing NR simulations above mass-ratio 6 we cannot validate it's accuracy.
34  *
35  * Tested Range: up to mass-ratio 6, any spin magnitude and orientation.
36  * Usage Range: up to mass-ratio 20, any spin magnitude and orientation.
37  */
38 
39 #include <lal/Date.h>
40 #include <lal/Sequence.h>
41 #include <lal/LALConstants.h>
42 #include <lal/FrequencySeries.h>
43 #include <lal/Units.h>
44 #include <lal/SphericalHarmonics.h>
45 
47 #include "LALSimIMRPhenomUtils.h"
48 
49 #include "LALSimIMRPhenomPv3HM.h"
50 
51 #define L_MAX_PLUS_1 5
52 #define PHENOM_DEFAULT_MAXF 0.5
53 
54 /**
55  * read in a LALDict.
56  * If ModeArray in LALDict is NULL then create a ModrArray
57  * with the default modes in PhenomPv3HM.
58  * If ModeArray is not NULL then use the modes supplied by user.
59  */
61  LALDict *extraParams)
62 {
63 
64  /* setup ModeArray */
65  if (extraParams == NULL)
66  extraParams = XLALCreateDict();
67  LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(extraParams);
68  if (ModeArray == NULL)
69  { /* Default behaviour */
70  /* TODO: Move this into a function */
71  XLAL_PRINT_INFO("Using default modes for PhenomPv3HM.\n");
72  ModeArray = XLALSimInspiralCreateModeArray();
73  /* Only need to define the positive m modes/
74  * The negative m modes are automatically added.
75  */
76  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 2);
77  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 1);
78  XLALSimInspiralModeArrayActivateMode(ModeArray, 3, 3);
79  XLALSimInspiralModeArrayActivateMode(ModeArray, 3, 2);
80  XLALSimInspiralModeArrayActivateMode(ModeArray, 4, 4);
81  XLALSimInspiralModeArrayActivateMode(ModeArray, 4, 3);
82  // XLALSimInspiralModeArrayPrintModes(ModeArray);
83  /* Don't forget to insert ModeArray back into extraParams. */
84  XLALSimInspiralWaveformParamsInsertModeArray(extraParams, ModeArray);
85  }
86  else
87  {
88  XLAL_PRINT_INFO("Using custom modes for PhenomPv3HM.\n");
89  }
90 
91  XLALDestroyValue(ModeArray);
92  /*TODO: Add an error check here somehow?*/
93 
94  return extraParams;
95 }
96 
97 /**
98  * Reads in a ModeArray and checks that it is valid.
99  * i.e., that it contains the 2,2 mode
100  * and may only contain the modes in the model
101  * i.e., 22, 21, 33, 32, 44, 43
102  * Only checks upto ell=8 though.
103  */
104 static int IMRPhenomPv3HM_check_mode_array(LALValue *ModeArray)
105 {
106  // if 22 mode not active -> error
107  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, 2, 2) != 1)
108  {
109  XLAL_ERROR(XLAL_EFUNC, "(2,2) mode required\n");
110  }
111 
112  // if no 22,21,33,32,44,43 mode and active -> error
113  // these modes are not in the model
114  for (INT4 ell = 2; ell <= 8; ell++)
115  {
116  for (INT4 mm = -ell; mm < ell + 1; mm++)
117  {
118  if (ell == 2 && mm == 2){
119  continue;
120  }
121  else if (ell == 2 && mm == 1)
122  {
123  continue;
124  }
125  else if (ell == 3 && mm == 3)
126  {
127  continue;
128  }
129  else if (ell == 3 && mm == 2)
130  {
131  continue;
132  }
133  else if (ell == 4 && mm == 4)
134  {
135  continue;
136  }
137  else if (ell == 4 && mm == 3)
138  {
139  continue;
140  }
141 
142  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ell, mm) == 1)
143  {
144  XLAL_ERROR(XLAL_EFUNC, "(%i,%i) mode in ModeArray but model does not include this!\n", ell, mm);
145  }
146  }
147  }
148  return XLAL_SUCCESS;
149 }
150 
151 /**
152  * Precomputes useful quantities and populates the
153  * PhenomPv3HMStorage and sysq (for precession angles) structs.
154  */
155 UNUSED static int init_PhenomPv3HM_Storage(
156  PhenomPv3HMStorage *p, /**< [out] PhenomPv3Storage struct */
157  sysq *pAngles, /**< [out] precession angle pre-computations struct */
158  REAL8 m1_SI, /**< mass of primary in SI (kg) */
159  REAL8 m2_SI, /**< mass of secondary in SI (kg) */
160  REAL8 S1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
161  REAL8 S1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
162  REAL8 S1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
163  REAL8 S2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
164  REAL8 S2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
165  REAL8 S2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
166  const REAL8 distance, /**< distance of source (m) */
167  const REAL8 inclination, /**< inclination of source (rad) */
168  const REAL8 phiRef, /**< reference orbital phase (rad) */
169  const REAL8 deltaF, /**< Sampling frequency (Hz) */
170  const REAL8 f_min, /**< Starting GW frequency (Hz) */
171  const REAL8 f_max, /**< End frequency; 0 defaults to ringdown cutoff freq */
172  const REAL8 f_ref /**< Reference GW frequency (Hz) */
173 )
174 {
175  XLAL_CHECK(0 != p, XLAL_EFAULT, "p is NULL");
176  XLAL_CHECK(0 != pAngles, XLAL_EFAULT, "pAngles is NULL");
177 
178  // We check if the systems is precessing because we skip angle
179  // computation if this is the case.
180  p->PRECESSING = 0;
181  if (S1x == 0. && S1y == 0. && S2x == 0. && S2y == 0.)
182  {
183  p->PRECESSING = 1; // This means the system is not precessing
184  }
185 
186  /* input parameters */
187  p->m1_SI = m1_SI;
188  p->m2_SI = m2_SI;
189  p->chi1x = S1x;
190  p->chi1y = S1y;
191  p->chi1z = S1z;
192  p->chi2x = S2x;
193  p->chi2y = S2y;
194  p->chi2z = S2z;
195  p->distance_SI = distance;
196  p->phiRef = phiRef;
197  p->deltaF = deltaF;
198  p->f_min = f_min;
199  p->f_max = f_max;
200  p->f_ref = f_ref;
201 
202  int retcode = 0;
204  &(p->m1_SI),
205  &(p->m2_SI),
206  &(p->chi1x),
207  &(p->chi1y),
208  &(p->chi1z),
209  &(p->chi2x),
210  &(p->chi2y),
211  &(p->chi2z));
212  XLAL_CHECK(
213  XLAL_SUCCESS == retcode,
214  XLAL_EFUNC,
215  "PhenomInternal_AlignedSpinEnforcePrimaryIsm1 failed");
216 
217  /* derived parameters */
218  p->m1_Msun = m1_SI / LAL_MSUN_SI;
219  p->m2_Msun = m2_SI / LAL_MSUN_SI;
220  p->Mtot_SI = p->m1_SI + p->m2_SI;
221  p->Mtot_Msun = p->m1_Msun + p->m2_Msun;
222 
223  p->eta = p->m1_Msun * p->m2_Msun / (p->Mtot_Msun * p->Mtot_Msun);
224  p->q = p->m1_Msun / p->m2_Msun; /* with m1>=m2 so q>=1 */
225 
226  /* check for rounding errors */
227  if (p->eta > 0.25 || p->q < 1.0)
228  {
229  PhenomInternal_nudge(&(p->eta), 0.25, 1e-6);
230  PhenomInternal_nudge(&(p->q), 1.0, 1e-6);
231  }
232 
233  p->Msec = p->Mtot_Msun * LAL_MTSUN_SI; /* Total mass in seconds */
234 
235  p->amp0 = (p->Mtot_Msun) * LAL_MRSUN_SI * (p->Mtot_Msun) * LAL_MTSUN_SI / (p->distance_SI);
236 
237  /* chi1_l == chi1z, chi2_l == chi2z so we don't need to save them as they are duplicates */
238  REAL8 chi1_l, chi2_l;
239 
240  /* rotate from LAL to PhenomP frame */
242  &chi1_l, &chi2_l, &(p->chip), &(p->thetaJN), &(p->alpha0), &(p->phi_aligned), &(p->zeta_polariz),
243  p->m1_SI, p->m2_SI, p->f_ref, p->phiRef, inclination,
244  p->chi1x, p->chi1y, p->chi1z,
245  p->chi2x, p->chi2y, p->chi2z, IMRPhenomPv3_V);
246  XLAL_CHECK(errcode == XLAL_SUCCESS, XLAL_EFUNC, "XLALSimIMRPhenomPCalculateModelParametersFromSourceFrame failed");
247 
248  p->inclination = p->thetaJN;
249 
250  // printf("(p->zeta_polariz) = %.16f\n", (p->zeta_polariz));
251 
252  /* compute spins in polar coordinates */
253  PhenomInternal_ComputeIMRPhenomPv3CartesianToPolar(&(p->chi1_theta), &(p->chi1_phi), &(p->chi1_mag), p->chi1x, p->chi1y, p->chi1z);
254  PhenomInternal_ComputeIMRPhenomPv3CartesianToPolar(&(p->chi2_theta), &(p->chi2_phi), &(p->chi2_mag), p->chi2x, p->chi2y, p->chi2z);
255 
256  if (p->PRECESSING != 1) // precessing case. compute angles
257  {
258  /* Initialize precession angles */
259  /* evaluating the angles at the reference frequency */
260  p->f_ref_Orb_Hz = 0.5 * p->f_ref; /* factor of 0.5 to go from GW to Orbital frequency */
261  /* precompute everything needed to compute precession angles from LALSimInspiralFDPrecAngles.c */
262  /* note that the reference frequency that you pass into InitializeSystem is the GW frequency */
263 
264  /* ExpansionOrder specifies how many terms in the PN expansion of the precession angles to use.
265  * In PhenomP3 we set this to 5, i.e. all but the highest order terms.
266  * */
267  int ExpansionOrder = 5;
268  errcode = InitializeSystem(pAngles,
269  p->m1_SI, p->m2_SI,
271  cos(p->chi1_theta), p->chi1_phi, p->chi1_mag,
272  cos(p->chi2_theta), p->chi2_phi, p->chi2_mag,
273  p->f_ref, ExpansionOrder);
274  XLAL_CHECK(errcode == XLAL_SUCCESS, XLAL_EFUNC, "InitializeSystem failed");
275  }
276 
277  return XLAL_SUCCESS;
278 }
279 
280 /**
281  * This version doesn't construct precessing hlm modes but instead constructs hplus, hcross directly.
282  */
284  UNUSED COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency-domain waveform h+ */
285  UNUSED COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency-domain waveform hx */
286  UNUSED REAL8Sequence *freqs, /**< frequency sequency in Hz */
287  UNUSED REAL8 m1_SI, /**< mass of companion 1 (kg) */
288  UNUSED REAL8 m2_SI, /**< mass of companion 2 (kg) */
289  UNUSED REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
290  UNUSED REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
291  UNUSED REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
292  UNUSED REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
293  UNUSED REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
294  UNUSED REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
295  UNUSED const REAL8 distance, /**< distance of source (m) */
296  UNUSED const REAL8 inclination, /**< inclination of source (rad) */
297  UNUSED const REAL8 phiRef, /**< reference orbital phase (rad) */
298  UNUSED const REAL8 deltaF, /**< Sampling frequency (Hz). To use arbitrary frequency points set deltaF <= 0. */
299  UNUSED REAL8 f_ref, /**< Reference frequency */
300  UNUSED LALDict *extraParams /**< LALDict struct */
301 )
302 {
303 
304  XLAL_CHECK(f_ref > 0, XLAL_EDOM, "f_ref must be greater than zero.\n");
305 
306  /* Use an auxiliar laldict to not overwrite the input argument */
307  LALDict *extraParams_aux;
308 
309  /* setup mode array */
310  if (extraParams == NULL)
311  {
312  extraParams_aux = XLALCreateDict();
313  }
314  else{
315  extraParams_aux = XLALDictDuplicate(extraParams);
316  }
317  extraParams_aux = IMRPhenomPv3HM_setup_mode_array(extraParams_aux);
318  LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(extraParams_aux);
319  int rcode = IMRPhenomPv3HM_check_mode_array(ModeArray);
320  XLAL_CHECK(XLAL_SUCCESS == rcode, rcode, "IMRPhenomPv3HM_check_mode_array failed");
321 
322  UNUSED const REAL8 Mtot_Msun = (m1_SI + m2_SI) / LAL_MSUN_SI;
323 
324  REAL8 f_min=0.;
325  REAL8 f_max=0.;
326  size_t npts=0;
327  REAL8Sequence *freqs_seq=NULL;
328 
329  LIGOTimeGPS tC = LIGOTIMEGPSZERO; // = {0, 0}
330 
331  if ((freqs->length == 2) && (deltaF > 0.))
332  {
333  // uniform frequencies
334  f_min = freqs->data[0];
335  f_max = freqs->data[1];
336 
337  if (f_max == 0.)
338  {
340  }
341  npts = PhenomInternal_NextPow2(f_max / deltaF) + 1;
342 
343  freqs_seq = XLALCreateREAL8Sequence(npts);
344  for (size_t j = 0; j < npts; j++)
345  {
346  freqs_seq->data[j] = j * deltaF;
347  }
348  /* shift zero frequency to avoid evaluating angles at f=0.*/
349  freqs_seq->data[0] = 1e-13;
350  /* coalesce at t=0 */
351  /* Shift by overall length in time */
352  XLAL_CHECK(
353  XLALGPSAdd(&tC, -1. / deltaF),
354  XLAL_EFUNC,
355  "Failed to shift coalescence time to t=0,\
356 tried to apply shift of -1.0/deltaF with deltaF=%g.",
357  deltaF);
358 
359  }
360  else if ((freqs->length != 2) && (deltaF <= 0.))
361  { /* else if 2. i.e. not uniformly spaced then we don't shift. */
362  // custom frequencies
363  f_min = freqs->data[0];
364  f_max = freqs->data[freqs->length - 1]; /* Last element */
365  npts = freqs->length;
366  freqs_seq = XLALCreateREAL8Sequence(npts);
367  for (size_t j = 0; j < npts; j++)
368  {
369  freqs_seq->data[j] = freqs->data[j];
370  }
371 
372  } else {
373  XLAL_ERROR(XLAL_EFUNC, "cannot interpret frequency bounds!\n");
374  }
375 
376  /* Store useful variables and compute derived and frequency independent variables */
377  PhenomPv3HMStorage *pv3HM;
379 
380  /* Struct that stores the precession angle variables */
381  sysq *pAngles;
382  pAngles = (sysq *)XLALMalloc(sizeof(sysq));
383 
384  int retcode = init_PhenomPv3HM_Storage(pv3HM, pAngles,
385  m1_SI, m2_SI,
386  chi1x, chi1y, chi1z,
387  chi2x, chi2y, chi2z,
388  distance, inclination, phiRef,
389  deltaF, f_min, f_max, f_ref);
390  XLAL_CHECK(XLAL_SUCCESS == retcode, retcode, "init_PhenomPv3HM_Storage failed");
391 
392 
393 
395  *hlmsD = NULL;
396  /* XLALSimIMRPhenomHMGethlmModes currently takes only the positive m modes */
398  hlmsD,
399  freqs,
400  m1_SI,
401  m2_SI,
402  chi1x,
403  chi1y,
404  chi1z,
405  chi2x,
406  chi2y,
407  chi2z,
408  pv3HM->phiRef,
409  deltaF,
410  f_ref,
411  extraParams_aux);
412  XLAL_CHECK(XLAL_SUCCESS == retcode,
413  XLAL_EFUNC, "XLALSimIMRPhenomHMGethlmModes failed");
414 
415  /* Allocate hptilde and hctilde */
416  *hptilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &tC, 0.0, deltaF, &lalStrainUnit, npts);
417  if (!(hptilde))
419  memset((*hptilde)->data->data, 0, npts * sizeof(COMPLEX16));
420  XLALUnitMultiply(&(*hptilde)->sampleUnits, &(*hptilde)->sampleUnits, &lalSecondUnit);
421 
422  *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &tC, 0.0, deltaF, &lalStrainUnit, npts);
423  if (!(hctilde))
425  memset((*hctilde)->data->data, 0, npts * sizeof(COMPLEX16));
426  XLALUnitMultiply(&(*hctilde)->sampleUnits, &(*hctilde)->sampleUnits, &lalSecondUnit);
427 
428  /* Frequency domain amplitude pre-factor */
429  const REAL8 amp0 = XLALSimPhenomUtilsFDamp0(Mtot_Msun, distance);
430 
431  /* logic for looping over modes
432  if 22 mode active:
433  # compute Ylms
434  for i in frequencies:
435  # compute wigner-D
436 
437  if 21 mode active:
438  # compute Ylms
439  for i in frequencies:
440  # compute wigner-D
441 
442  if 33 mode active:
443  # compute Ylms
444  for i in frequencies:
445  # compute wigner-D
446 
447  etc...
448  */
449 
450  UINT4 ell=0;
451  INT4 mprime=0;
452 
453  int ret = 0;
454  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, 2, 2) == 1)
455  {
456  ell=2;
457  mprime=2;
458 
459  ret = IMRPhenomPv3HM_Compute_Mode(hptilde, hctilde, ell, mprime, Mtot_Msun, pv3HM, hlmsD, pAngles, freqs_seq);
460  XLAL_CHECK(XLAL_SUCCESS == ret, ret, "IMRPhenomPv3HM_Compute_Mode failed for the %i, %i mode\n", ell, mprime);
461 
462  }
463  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, 2, 1) == 1)
464  {
465  ell=2;
466  mprime=1;
467 
468  ret = IMRPhenomPv3HM_Compute_Mode(hptilde, hctilde, ell, mprime, Mtot_Msun, pv3HM, hlmsD, pAngles, freqs_seq);
469  XLAL_CHECK(XLAL_SUCCESS == ret, ret, "IMRPhenomPv3HM_Compute_Mode failed for the %i, %i mode\n", ell, mprime);
470  }
471  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, 3, 3) == 1)
472  {
473  ell = 3;
474  mprime = 3;
475 
476  ret = IMRPhenomPv3HM_Compute_Mode(hptilde, hctilde, ell, mprime, Mtot_Msun, pv3HM, hlmsD, pAngles, freqs_seq);
477  XLAL_CHECK(XLAL_SUCCESS == ret, ret, "IMRPhenomPv3HM_Compute_Mode failed for the %i, %i mode\n", ell, mprime);
478  }
479  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, 3, 2) == 1)
480  {
481  ell = 3;
482  mprime = 2;
483 
484  ret = IMRPhenomPv3HM_Compute_Mode(hptilde, hctilde, ell, mprime, Mtot_Msun, pv3HM, hlmsD, pAngles, freqs_seq);
485  XLAL_CHECK(XLAL_SUCCESS == ret, ret, "IMRPhenomPv3HM_Compute_Mode failed for the %i, %i mode\n", ell, mprime);
486  }
487  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, 4, 4) == 1)
488  {
489  ell = 4;
490  mprime = 4;
491 
492  ret = IMRPhenomPv3HM_Compute_Mode(hptilde, hctilde, ell, mprime, Mtot_Msun, pv3HM, hlmsD, pAngles, freqs_seq);
493  XLAL_CHECK(XLAL_SUCCESS == ret, ret, "IMRPhenomPv3HM_Compute_Mode failed for the %i, %i mode\n", ell, mprime);
494  }
495  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, 4, 3) == 1)
496  {
497  ell = 4;
498  mprime = 3;
499 
500  ret = IMRPhenomPv3HM_Compute_Mode(hptilde, hctilde, ell, mprime, Mtot_Msun, pv3HM, hlmsD, pAngles, freqs_seq);
501  XLAL_CHECK(XLAL_SUCCESS == ret, ret, "IMRPhenomPv3HM_Compute_Mode failed for the %i, %i mode\n", ell, mprime);
502  }
503 
504  // LALFree(hlmD);
505  XLALDestroyREAL8Sequence(freqs_seq);
506  XLALDestroyValue(ModeArray);
508  XLALFree(hlmsD);
509 
510  COMPLEX16 PhPpolp, PhPpolc; /* for polarisation application */
511 
512  REAL8 cos_2zeta, sin_2zeta;
513  /* apply polarisation angle - Moved this from LALSimInspiral to indide here for PhenomPv3 */
514  cos_2zeta = cos(2.0 * pv3HM->zeta_polariz);
515  sin_2zeta = sin(2.0 * pv3HM->zeta_polariz);
516 
517  for (UINT4 i = 0; i < (*hptilde)->data->length; i++)
518  { // loop over frequency points in sequence
519 
520  // also applying Frequency Domain distance scale here - amp0
521  PhPpolp = amp0 * (*hptilde)->data->data[i];
522  PhPpolc = amp0 * (*hctilde)->data->data[i];
523  (*hptilde)->data->data[i] = cos_2zeta * PhPpolp + sin_2zeta * PhPpolc;
524  (*hctilde)->data->data[i] = cos_2zeta * PhPpolc - sin_2zeta * PhPpolp;
525  }
526 
527  /* free pointers */
528  LALFree(pv3HM);
529  LALFree(pAngles);
530  XLALDestroyDict(extraParams_aux);
531 
532  return XLAL_SUCCESS;
533 }
534 
535 /**
536  * Driver routine to compute the precessing inspiral-merger-ringdown
537  * phenomenological waveform IMRPhenomPv3 in the frequency domain.
538  *
539  * Reference:
540  * - Hannam et al., arXiv:1308.3271 [gr-qc]
541  * - Bohe et al., PhenomPv2 technical document LIGO-T1500602
542  * - Chatziioannou et al., arXiv 1703.03967 [gr-qc]
543  *
544  * This function can be used for equally-spaced frequency series.
545  * For unequal spacing, use XLALSimIMRPhenomPv3FrequencySequence instead.
546  *
547  * This function calls XLALSimIMRPhenomPv3HMGetHplusHcross with just
548  * the l=m=2 mode
549  *
550  */
552  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency-domain waveform h+ */
553  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency-domain waveform hx */
554  REAL8Sequence *freqs, /**< frequency sequency in Hz */
555  REAL8 m1_SI, /**< mass of companion 1 (kg) */
556  REAL8 m2_SI, /**< mass of companion 2 (kg) */
557  REAL8 S1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
558  REAL8 S1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
559  REAL8 S1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
560  REAL8 S2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
561  REAL8 S2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
562  REAL8 S2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
563  const REAL8 distance, /**< distance of source (m) */
564  const REAL8 inclination, /**< inclination of source (rad) */
565  const REAL8 phiRef, /**< reference orbital phase (rad) */
566  const REAL8 deltaF, /**< Sampling frequency (Hz) */
567  const REAL8 f_ref, /**< Reference frequency */
568  LALDict *extraParams) /**<linked list containing the extra testing GR parameters */
569 {
570 
571  /* Use an auxiliar laldict to not overwrite the input argument */
572  LALDict *extraParams_aux;
573 
574  /* setup mode array */
575  if (extraParams == NULL)
576  {
577  extraParams_aux = XLALCreateDict();
578  }
579  else{
580  extraParams_aux = XLALDictDuplicate(extraParams);
581  }
582  LALValue *ModeArray = XLALSimInspiralCreateModeArray();
583  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 2);
584  XLALSimInspiralWaveformParamsInsertModeArray(extraParams_aux, ModeArray);
585  XLALDestroyValue(ModeArray);
586 
588  hptilde,
589  hctilde,
590  freqs,
591  m1_SI,
592  m2_SI,
593  S1x,
594  S1y,
595  S1z,
596  S2x,
597  S2y,
598  S2z,
599  distance,
600  inclination,
601  phiRef,
602  deltaF,
603  f_ref,
604  extraParams_aux);
605  XLAL_CHECK(
606  XLAL_SUCCESS == ret,
607  XLAL_EFUNC,
608  "XLALSimIMRPhenomPv3HMGetHplusHcross failed in IMRPhenomPv3");
609 
610  XLALDestroyDict(extraParams_aux);
611 
612  return XLAL_SUCCESS;
613 }
614 
615 /**
616  * This is an internal function that returns the precession angles
617  * at a single frequency
618  */
619 static int IMRPhenomPv3HM_Compute_a_b_e(REAL8 *alpha, REAL8 *beta, REAL8 *mprime_epsilon, REAL8 fHz, INT4 mprime, const REAL8 twopi_Msec, PhenomPv3HMStorage *pv3HM, sysq *pAngles)
620 {
621 
622  vector angles;
623  REAL8 f_mprime = 0.;
624  REAL8 xi = 0.;
625 
626  f_mprime = fHz / mprime;
627  xi = pow(f_mprime * twopi_Msec, pAngles->onethird);
628  angles = compute_phiz_zeta_costhetaL3PN(xi, pAngles);
629 
630  *alpha = angles.x + pv3HM->alpha0;
631  REAL8 epsilon = angles.y;
632 
633  *mprime_epsilon = mprime * epsilon;
634 
635  /* angles.z can sometimes nan just above 1.
636  * The following nudge seems to be a good fix.
637  */
638  PhenomInternal_nudge(&(angles.z), 1.0, 1e-6); //This could even go from 1e-6 to 1e-15 - then would have to also change in Pv3 code. Should just fix the problem in the angles code.
639  *beta = acos(angles.z);
640 
641  return XLAL_SUCCESS;
642 }
643 
644 /**
645  * This is an internal function computes terms
646  * required to compute hptilde and hctilde
647  */
649 {
650  INT4 minus1l=0; /* (-1)^ell */
651  if (ell % 2)
652  minus1l = -1;
653  else
654  minus1l = 1;
655 
656  COMPLEX16 Term1_sum = 0.;
657  COMPLEX16 Term2_sum = 0.;
658 
659  if (ell == 2 && mprime == 2)
660  {
661  for (int mm = -ell; mm <= ell; mm++)
662  {
663  COMPLEX16 WigD = als->alpha2[mm + ell] * wigs->d22[0][mm + ell];
664  COMPLEX16 WigDmConj = als->alpha2[-mm + ell] * wigs->d22[1][mm + ell];
665 
666  Term1_sum += ylms->Ylm2[0][mm + ell] * WigD;
667  Term2_sum += minus1l * ylms->Ylm2[1][mm + ell] * WigDmConj;
668  }
669  }
670  else if (ell == 2 && mprime == 1)
671  {
672  for (int mm = -ell; mm <= ell; mm++)
673  {
674  COMPLEX16 WigD = als->alpha2[mm + ell] * wigs->d21[0][mm + ell];
675  COMPLEX16 WigDmConj = als->alpha2[-mm + ell] * wigs->d21[1][mm + ell];
676 
677  Term1_sum += ylms->Ylm2[0][mm + ell] * WigD;
678  Term2_sum += minus1l * ylms->Ylm2[1][mm + ell] * WigDmConj;
679  }
680  }
681  else if (ell == 3 && mprime == 3)
682  {
683  for (int mm = -ell; mm <= ell; mm++)
684  {
685  COMPLEX16 WigD = als->alpha3[mm + ell] * wigs->d33[0][mm + ell];
686  COMPLEX16 WigDmConj = als->alpha3[-mm + ell] * wigs->d33[1][mm + ell];
687 
688  Term1_sum += ylms->Ylm3[0][mm + ell] * WigD;
689  Term2_sum += minus1l * ylms->Ylm3[1][mm + ell] * WigDmConj;
690  }
691  }
692  else if (ell == 3 && mprime == 2)
693  {
694  for (int mm = -ell; mm <= ell; mm++)
695  {
696  COMPLEX16 WigD = als->alpha3[mm + ell] * wigs->d32[0][mm + ell];
697  COMPLEX16 WigDmConj = als->alpha3[-mm + ell] * wigs->d32[1][mm + ell];
698 
699  Term1_sum += ylms->Ylm3[0][mm + ell] * WigD;
700  Term2_sum += minus1l * ylms->Ylm3[1][mm + ell] * WigDmConj;
701  }
702  }
703  else if (ell == 4 && mprime == 4)
704  {
705  for (int mm = -ell; mm <= ell; mm++)
706  {
707  COMPLEX16 WigD = als->alpha4[mm + ell] * wigs->d44[0][mm + ell];
708  COMPLEX16 WigDmConj = als->alpha4[-mm + ell] * wigs->d44[1][mm + ell];
709 
710  Term1_sum += ylms->Ylm4[0][mm + ell] * WigD;
711  Term2_sum += minus1l * ylms->Ylm4[1][mm + ell] * WigDmConj;
712  }
713  }
714  else if (ell == 4 && mprime == 3)
715  {
716  for (int mm = -ell; mm <= ell; mm++)
717  {
718  COMPLEX16 WigD = als->alpha4[mm + ell] * wigs->d43[0][mm + ell];
719  COMPLEX16 WigDmConj = als->alpha4[-mm + ell] * wigs->d43[1][mm + ell];
720 
721  Term1_sum += ylms->Ylm4[0][mm + ell] * WigD;
722  Term2_sum += minus1l * ylms->Ylm4[1][mm + ell] * WigDmConj;
723  }
724  }
725  else
726  {
727  XLAL_ERROR(XLAL_EFUNC, "mode %i, %i not available.\n", ell, mprime);
728  }
729 
730  *Term1 = Term1_sum;
731  *Term2 = Term2_sum;
732 
733  return XLAL_SUCCESS;
734 }
735 
736 /**
737  * This is an internal function that returns hptilde and hctilde
738  * for a single mode in the inertial frame
739  */
741  COMPLEX16FrequencySeries **hptilde,
742  COMPLEX16FrequencySeries **hctilde,
743  UINT4 ell,
744  INT4 mprime,
745  const REAL8 Mtot_Msun,
746  PhenomPv3HMStorage *pv3HM,
747  SphHarmFrequencySeries **hlmsD,
748  sysq *pAngles,
749  REAL8Sequence *freqs_seq)
750 {
751 
752  if (pv3HM->PRECESSING == 1) // non-precessing case. skip angles
753  {
754  INT4 sym; /* sym will decide whether to add the -m mode (when equatorial symmetry is present) */
756  if (!(hlm))
758 
759  /* We test for hypothetical m=0 modes */
760  if (mprime == 0)
761  {
762  sym = 0;
763  }
764  else
765  {
766  sym = 1;
767  }
768  PhenomInternal_IMRPhenomHMFDAddMode(*hptilde, *hctilde, hlm, pv3HM->inclination, 0., ell, mprime, sym);
769  } else { // precessing case. compute angles and do the twist
770 
771  const REAL8 Msec = Mtot_Msun * LAL_MTSUN_SI; /* Total mass in seconds */
772  const REAL8 twopi_Msec = LAL_TWOPI * Msec;
773  REAL8 fHz = 0.;
774 
775  COMPLEX16 half_amp_eps;
776 
777  COMPLEX16 Term1_sum = 0;
778  COMPLEX16 Term2_sum = 0;
779 
780  UNUSED INT4 minus1l = 0; /* (-1)^ell */
781  int ret_abe;
782  int retloop;
783 
784  REAL8 alpha = 0.;
785  REAL8 beta = 0.;
786  REAL8 mprime_epsilon = 0.;
787 
788  // compute Ylms
790  // get non-prec mode
791  COMPLEX16FrequencySeries *hlmD = XLALSphHarmFrequencySeriesGetMode(*hlmsD, ell, mprime);
792  if (!(hlmD))
794 
796  memset(als, 0, sizeof(IMRPhenomPv3HMAlphaStruct));
797 
799  memset(wigs, 0, sizeof(IMRPhenomPv3HMWignderStruct));
800 
801  int ret_als;
802  int ret_wigs;
803 
804  // frequency loop
805  for (size_t j = 0; j < freqs_seq->length; j++)
806  {
807  fHz = freqs_seq->data[j]; //for the angles
808  // compute alpha, beta and mprime*epsilon
809  ret_abe = IMRPhenomPv3HM_Compute_a_b_e(&alpha, &beta, &mprime_epsilon, fHz, mprime, twopi_Msec, pv3HM, pAngles);
810  XLAL_CHECK(
811  XLAL_SUCCESS == ret_abe,
812  XLAL_EFUNC,
813  "IMRPhenomPv3HM_Compute_a_b_e failed");
814  /* Precompute wigner-d elements */
815  ret_wigs = XLALSimIMRPhenomPv3HMComputeWignerdElements(&wigs, ell, mprime, -beta);
816  XLAL_CHECK(
817  XLAL_SUCCESS == ret_wigs,
818  XLAL_EFUNC,
819  "XLALSimIMRPhenomPv3HMComputeWignerdElements failed");
820  /* Precompute powers of e^{i m alpha} */
821  ret_als = XLALSimIMRPhenomPv3HMComputeAlphaElements(&als, ell, alpha);
822  XLAL_CHECK(
823  XLAL_SUCCESS == ret_als,
824  XLAL_EFUNC,
825  "XLALSimIMRPhenomPv3HMComputeAlphaElements failed");
826  retloop = IMRPhenomPv3HM_wigner_loop(&Term1_sum, &Term2_sum, ell, mprime, ylms, als, wigs);
827  XLAL_CHECK(
828  XLAL_SUCCESS == retloop,
829  XLAL_EFUNC,
830  "IMRPhenomPv3HM_wigner_loop failed");
831 
832  COMPLEX16 hlmD_j = (hlmD->data->data[j]);
833  half_amp_eps = 0.5 * hlmD_j * cexp(-I * mprime_epsilon);
834  (*hptilde)->data->data[j] += half_amp_eps * (Term1_sum + Term2_sum);
835  (*hctilde)->data->data[j] += -I * half_amp_eps * (Term1_sum - Term2_sum);
836  }
837 
838  XLALFree(wigs);
839  XLALFree(als);
840  XLALFree(ylms); // allocated in XLALSimIMRPhenomPv3HMComputeYlmElements
841  }
842 
843  return XLAL_SUCCESS;
844 }
845 
846 /**
847  * Returns frequency domain hlm's in inertial frame
848  *
849  */
851  SphHarmFrequencySeries **hlms, /**< [out] SphHarmFrequencySeries struct containing inertial frame hlm modes */
852  REAL8Sequence *freqs, /**< frequency sequency in Hz */
853  REAL8 m1_SI, /**< mass of companion 1 (kg) */
854  REAL8 m2_SI, /**< mass of companion 2 (kg) */
855  REAL8 chi1x, /**< x-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
856  REAL8 chi1y, /**< y-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
857  REAL8 chi1z, /**< z-component of the dimensionless spin of object 1 w.r.t. Lhat = (0,0,1) */
858  REAL8 chi2x, /**< x-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
859  REAL8 chi2y, /**< y-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
860  REAL8 chi2z, /**< z-component of the dimensionless spin of object 2 w.r.t. Lhat = (0,0,1) */
861  const REAL8 phiRef, /**< reference orbital phase (rad) */
862  const REAL8 deltaF, /**< Sampling frequency (Hz) */
863  const REAL8 f_ref, /**< Reference frequency */
864  LALDict *extraParams) /**<linked list containing the extra testing GR parameters */
865 {
866 
867  XLAL_ERROR(XLAL_EFUNC, "Function (XLALSimIMRPhenomPv3HMModes) not implemented!\n");
868 
869  XLAL_CHECK(f_ref > 0, XLAL_EDOM, "f_ref must be greater than zero.\n");
870 
871  /* Use an auxiliar laldict to not overwrite the input argument */
872  LALDict *extraParams_aux;
873 
874  /* setup mode array */
875  if (extraParams == NULL)
876  {
877  extraParams_aux = XLALCreateDict();
878  }
879  else{
880  extraParams_aux = XLALDictDuplicate(extraParams);
881  }
882  extraParams_aux = IMRPhenomPv3HM_setup_mode_array(extraParams_aux);
883  LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(extraParams_aux);
884  int rcode = IMRPhenomPv3HM_check_mode_array(ModeArray);
885  XLAL_CHECK(XLAL_SUCCESS == rcode, rcode, "IMRPhenomPv3HM_check_mode_array failed");
886 
887  UNUSED const REAL8 Mtot_Msun = (m1_SI + m2_SI) / LAL_MSUN_SI;
888 
889  REAL8 f_min = 0.;
890  REAL8 f_max = 0.;
891  size_t npts = 0;
892  REAL8Sequence *freqs_seq = NULL;
893 
894  LIGOTimeGPS tC = LIGOTIMEGPSZERO; // = {0, 0}
895 
896  if ((freqs->length == 2) && (deltaF > 0.))
897  {
898  // uniform frequencies
899  f_min = freqs->data[0];
900  f_max = freqs->data[1];
901 
902  if (f_max == 0.)
903  {
904  f_max = XLALSimPhenomUtilsMftoHz(0.5, Mtot_Msun);
905  }
906  npts = PhenomInternal_NextPow2(f_max / deltaF) + 1;
907 
908  freqs_seq = XLALCreateREAL8Sequence(npts);
909  for (size_t j = 0; j < npts; j++)
910  {
911  freqs_seq->data[j] = j * deltaF;
912  }
913  /* coalesce at t=0 */
914  /* Shift by overall length in time */
915  XLAL_CHECK(
916  XLALGPSAdd(&tC, -1. / deltaF),
917  XLAL_EFUNC,
918  "Failed to shift coalescence time to t=0,\
919 tried to apply shift of -1.0/deltaF with deltaF=%g.",
920  deltaF);
921  }
922  else if ((freqs->length != 2) && (deltaF <= 0.))
923  { /* else if 2. i.e. not uniformly spaced then we don't shift. */
924  // custom frequencies
925  f_min = freqs->data[0];
926  f_max = freqs->data[freqs->length - 1]; /* Last element */
927  npts = freqs->length;
928  freqs_seq = XLALCreateREAL8Sequence(npts);
929  for (size_t j = 0; j < npts; j++)
930  {
931  freqs_seq->data[j] = freqs->data[j];
932  }
933  }
934  else
935  {
936  XLAL_ERROR(XLAL_EDOM, "Input frequencies not as expected. Aborting.\n");
937  }
938 
939  /* Store useful variables and compute derived and frequency independent variables */
940  PhenomPv3HMStorage *pv3HM;
942 
943  /* Struct that stores the precession angle variables */
944  sysq *pAngles;
945  pAngles = (sysq *)XLALMalloc(sizeof(sysq));
946 
947  // distance and inclination are not used in the modes
948  // so we just parse dummy variables
949  REAL8 distance = 1.;
950  REAL8 inclination = 0.;
951 
952  int retcode = init_PhenomPv3HM_Storage(pv3HM, pAngles,
953  m1_SI, m2_SI,
954  chi1x, chi1y, chi1z,
955  chi2x, chi2y, chi2z,
956  distance, inclination, phiRef,
957  deltaF, f_min, f_max, f_ref);
958  XLAL_CHECK(XLAL_SUCCESS == retcode, retcode, "init_PhenomPv3HM_Storage failed");
959 
960  // hlmsD = co-precessing frame modes
962  *hlmsD = NULL;
963  /* XLALSimIMRPhenomHMGethlmModes currently takes only the positive m modes */
965  hlmsD,
966  freqs,
967  m1_SI,
968  m2_SI,
969  chi1x,
970  chi1y,
971  chi1z,
972  chi2x,
973  chi2y,
974  chi2z,
975  pv3HM->phiRef,
976  deltaF,
977  f_ref,
978  extraParams_aux);
979  XLAL_CHECK(XLAL_SUCCESS == retcode,
980  XLAL_EFUNC, "XLALSimIMRPhenomHMGethlmModes failed");
981 
982  const REAL8 Msec = Mtot_Msun * LAL_MTSUN_SI; /* Total mass in seconds */
983  const REAL8 twopi_Msec = LAL_TWOPI * Msec;
984  REAL8 fHz = 0.;
985 
986  int ret_abe;
987  int ret_wig_element;
988 
989  REAL8 alpha = 0.;
990  REAL8 beta = 0.;
991  REAL8 mprime_epsilon = 0.;
992  REAL8 wig_d = 0.;
993 
994  /* loop over modes */
995  /* at this point ModeArray should contain the list of modes
996  * and therefore if NULL then something is wrong and abort.
997  */
998  if (ModeArray == NULL)
999  {
1000  XLAL_ERROR(XLAL_EDOM, "ModeArray is NULL when it shouldn't be. Aborting.\n");
1001  }
1002  for (UINT4 ell = 2; ell < L_MAX_PLUS_1; ell++)
1003  { // inertial frame ell modes
1004  for (INT4 mm = -ell; mm < (INT4)ell + 1; mm++)
1005  { // inertial frame mm modes
1006  // inertial frame mode FrequencySeries
1007  COMPLEX16FrequencySeries *hlm = XLALCreateCOMPLEX16FrequencySeries("hlm: FD waveform", &tC, 0.0, deltaF, &lalStrainUnit, npts);
1008  memset(hlm->data->data, 0, npts * sizeof(COMPLEX16));
1009 
1010  for (INT4 mprime = 1; mprime < (INT4)ell + 1; mprime++)
1011  {
1012  /* The negative m modes are automatically added. */
1013  /* first check if (l,m) mode is 'activated' in the ModeArray */
1014  /* if activated then generate the mode, else skip this mode. */
1015  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ell, mprime) != 1)
1016  { /* skip mode */
1017  XLAL_PRINT_INFO("SKIPPING ell = %i mprime = %i\n", ell, mprime);
1018  continue;
1019  } /* else: generate mode */
1020  XLAL_PRINT_INFO("generateing ell = %i mprime = %i\n", ell, mprime);
1021 
1022  COMPLEX16FrequencySeries *hlmD = XLALSphHarmFrequencySeriesGetMode(*hlmsD, ell, mprime);
1023  if (!(hlmD))
1024  XLAL_ERROR(XLAL_EFUNC, "XLALSphHarmFrequencySeriesGetMode failed for (%i,%i) mode\n", ell, mprime);
1025 
1026  // frequency loop
1027  for (size_t j = 0; j < freqs_seq->length; j++)
1028  {
1029 
1030  fHz = freqs_seq->data[j]; //for the angles
1031  // compute alpha, beta and mprime*epsilon
1032  ret_abe = IMRPhenomPv3HM_Compute_a_b_e(&alpha, &beta, &mprime_epsilon, fHz, mprime, twopi_Msec, pv3HM, pAngles);
1033  XLAL_CHECK(
1034  XLAL_SUCCESS == ret_abe,
1035  XLAL_EFUNC,
1036  "IMRPhenomPv3HM_Compute_a_b_e failed");
1037 
1038  ret_wig_element = XLALSimPhenomUtilsPhenomPv3HMWignerdElement(&wig_d, ell, mprime, mm, -beta);
1039  XLAL_CHECK(
1040  XLAL_SUCCESS == ret_wig_element,
1041  XLAL_EFUNC,
1042  "XLALSimPhenomUtilsPhenomPv3HMWignerdElement failed");
1043 
1044  COMPLEX16 hlmD_j = hlmD->data->data[j];
1045  hlm->data->data[j] += hlmD_j * cexp(-I * mprime_epsilon) * cexp(I * mm * alpha) * wig_d;
1046  }
1047  }
1048 
1049  *hlms = XLALSphHarmFrequencySeriesAddMode(*hlms, hlm, ell, mm);
1050 
1052  }
1053  }
1054 
1055  // LALFree(hlmD);
1056  XLALDestroyREAL8Sequence(freqs_seq);
1057  XLALDestroyValue(ModeArray);
1059  XLALFree(hlmsD);
1060 
1061 
1062  /* free pointers */
1063  LALFree(pv3HM);
1064  LALFree(pAngles);
1065  XLALDestroyDict(extraParams_aux);
1066 
1067  return XLAL_SUCCESS;
1068 }
void XLALDestroyDict(LALDict *dict)
LALDict * XLALDictDuplicate(LALDict *old)
LALDict * XLALCreateDict(void)
#define LALFree(p)
int XLALSimIMRPhenomHMGethlmModes(SphHarmFrequencySeries **hlms, REAL8Sequence *freqs, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, const REAL8 phiRef, const REAL8 deltaF, REAL8 f_ref, LALDict *extraParams)
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.
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.
size_t PhenomInternal_NextPow2(const size_t n)
Return the closest higher power of 2.
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.
int XLALSimIMRPhenomPv3HMModes(SphHarmFrequencySeries **hlms, REAL8Sequence *freqs, REAL8 m1_SI, REAL8 m2_SI, REAL8 chi1x, REAL8 chi1y, REAL8 chi1z, REAL8 chi2x, REAL8 chi2y, REAL8 chi2z, const REAL8 phiRef, const REAL8 deltaF, const REAL8 f_ref, LALDict *extraParams)
Returns frequency domain hlm's in inertial frame.
int XLALSimIMRPhenomPv3HMGetHplusHcross(UNUSED COMPLEX16FrequencySeries **hptilde, UNUSED COMPLEX16FrequencySeries **hctilde, UNUSED REAL8Sequence *freqs, UNUSED REAL8 m1_SI, UNUSED REAL8 m2_SI, UNUSED REAL8 chi1x, UNUSED REAL8 chi1y, UNUSED REAL8 chi1z, UNUSED REAL8 chi2x, UNUSED REAL8 chi2y, UNUSED REAL8 chi2z, UNUSED const REAL8 distance, UNUSED const REAL8 inclination, UNUSED const REAL8 phiRef, UNUSED const REAL8 deltaF, UNUSED REAL8 f_ref, UNUSED LALDict *extraParams)
This version doesn't construct precessing hlm modes but instead constructs hplus, hcross directly.
static int IMRPhenomPv3HM_check_mode_array(LALValue *ModeArray)
Reads in a ModeArray and checks that it is valid.
#define L_MAX_PLUS_1
int XLALSimIMRPhenomPv3(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, REAL8Sequence *freqs, REAL8 m1_SI, REAL8 m2_SI, REAL8 S1x, REAL8 S1y, REAL8 S1z, REAL8 S2x, REAL8 S2y, REAL8 S2z, const REAL8 distance, const REAL8 inclination, const REAL8 phiRef, const REAL8 deltaF, const REAL8 f_ref, LALDict *extraParams)
Driver routine to compute the precessing inspiral-merger-ringdown phenomenological waveform IMRPhenom...
static UNUSED int init_PhenomPv3HM_Storage(PhenomPv3HMStorage *p, sysq *pAngles, REAL8 m1_SI, REAL8 m2_SI, REAL8 S1x, REAL8 S1y, REAL8 S1z, REAL8 S2x, REAL8 S2y, REAL8 S2z, const REAL8 distance, const REAL8 inclination, const REAL8 phiRef, const REAL8 deltaF, const REAL8 f_min, const REAL8 f_max, const REAL8 f_ref)
Precomputes useful quantities and populates the PhenomPv3HMStorage and sysq (for precession angles) s...
static int IMRPhenomPv3HM_wigner_loop(COMPLEX16 *Term1, COMPLEX16 *Term2, INT4 ell, INT4 mprime, IMRPhenomPv3HMYlmStruct *ylms, IMRPhenomPv3HMAlphaStruct *als, IMRPhenomPv3HMWignderStruct *wigs)
This is an internal function computes terms required to compute hptilde and hctilde.
#define PHENOM_DEFAULT_MAXF
static int IMRPhenomPv3HM_Compute_a_b_e(REAL8 *alpha, REAL8 *beta, REAL8 *mprime_epsilon, REAL8 fHz, INT4 mprime, const REAL8 twopi_Msec, PhenomPv3HMStorage *pv3HM, sysq *pAngles)
This is an internal function that returns the precession angles at a single frequency.
static int IMRPhenomPv3HM_Compute_Mode(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, UINT4 ell, INT4 mprime, const REAL8 Mtot_Msun, PhenomPv3HMStorage *pv3HM, SphHarmFrequencySeries **hlmsD, sysq *pAngles, REAL8Sequence *freqs_seq)
This is an internal function that returns hptilde and hctilde for a single mode in the inertial frame...
static LALDict * IMRPhenomPv3HM_setup_mode_array(LALDict *extraParams)
read in a LALDict.
#define LHAT_PHI
#define LHAT_COS_THETA
double XLALSimPhenomUtilsFDamp0(REAL8 Mtot_Msun, REAL8 distance)
compute the frequency domain amplitude pre-factor
int XLALSimIMRPhenomPv3HMComputeAlphaElements(IMRPhenomPv3HMAlphaStruct **p, UINT4 ell, REAL8 alpha)
int XLALSimIMRPhenomPv3HMComputeWignerdElements(IMRPhenomPv3HMWignderStruct **p, UNUSED UINT4 ell, UNUSED INT4 mprime, UNUSED REAL8 b)
computes the wigner-d elements for -beta in batches.
double XLALSimPhenomUtilsMftoHz(REAL8 Mf, REAL8 Mtot_Msun)
Convert from geometric frequency to frequency in Hz.
IMRPhenomPv3HMYlmStruct * XLALSimIMRPhenomPv3HMComputeYlmElements(REAL8 theta, REAL8 phi, INT4 ell)
int XLALSimPhenomUtilsPhenomPv3HMWignerdElement(REAL8 *wig_d, UINT4 ell, INT4 mprime, INT4 mm, REAL8 b)
Hard coded expressions for relevant Wignerd matrix elements.
static UNUSED int InitializeSystem(sysq *system, const double m1, const double m2, const double mul, const double phl, const double mu1, const double ph1, const double ch1, const double mu2, const double ph2, const double ch2, const double f_0, const int ExpansionOrder)
InitializeSystem computes all the prefactors needed to generate precession angles from Chatziioannou ...
static UNUSED vector compute_phiz_zeta_costhetaL3PN(const double xi, const sysq *system)
Internal function that computes phiz, zeta, and costhetaL at 3PN.
static double beta(const double a, const double b, const sysq *system)
Internal function that computes the spin-orbit couplings.
int XLALSimInspiralWaveformParamsInsertModeArray(LALDict *params, LALValue *value)
LALValue * XLALSimInspiralWaveformParamsLookupModeArray(LALDict *params)
void XLALDestroyValue(LALValue *value)
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
#define LAL_MSUN_SI
#define LAL_TWOPI
#define LAL_MTSUN_SI
#define LAL_MRSUN_SI
#define LIGOTIMEGPSZERO
double complex COMPLEX16
double REAL8
uint32_t UINT4
int32_t INT4
void * XLALMalloc(size_t n)
void XLALFree(void *p)
@ IMRPhenomPv3_V
version 3: based on IMRPhenomD and the precession angles from Katerina Chatziioannou PhysRevD....
Definition: LALSimIMR.h:77
int XLALSimIMRPhenomPCalculateModelParametersFromSourceFrame(REAL8 *chi1_l, REAL8 *chi2_l, REAL8 *chip, REAL8 *thetaJN, REAL8 *alpha0, REAL8 *phi_aligned, REAL8 *zeta_polariz, const REAL8 m1_SI, const REAL8 m2_SI, const REAL8 f_ref, const REAL8 phiRef, const REAL8 incl, const REAL8 s1x, const REAL8 s1y, const REAL8 s1z, const REAL8 s2x, const REAL8 s2y, const REAL8 s2z, IMRPhenomP_version_type IMRPhenomP_version)
Function to map LAL parameters (masses, 6 spin components, phiRef and inclination at f_ref) (assumed ...
LALValue * XLALSimInspiralCreateModeArray(void)
Create a LALValue pointer to store the mode array.
int XLALSimInspiralModeArrayIsModeActive(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralModeArrayActivateMode(LALValue *modes, unsigned l, int m)
COMPLEX16FrequencySeries * XLALSphHarmFrequencySeriesGetMode(SphHarmFrequencySeries *ts, UINT4 l, INT4 m)
Get the time series of a waveform's (l,m) spherical harmonic mode from a SphHarmFrequencySeries linke...
SphHarmFrequencySeries * XLALSphHarmFrequencySeriesAddMode(SphHarmFrequencySeries *appended, const COMPLEX16FrequencySeries *inmode, UINT4 l, INT4 m)
Prepend a node to a linked list of SphHarmFrequencySeries, or create a new head.
void XLALDestroySphHarmFrequencySeries(SphHarmFrequencySeries *ts)
Delete list from current pointer to the end of the list.
void 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,...)
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EDOM
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
list p
double alpha
Definition: sgwb.c:106
COMPLEX16Sequence * data
COMPLEX16 * data
a strcut to keep the complex exponential terms of the alpha precession angle
COMPLEX16 alpha4[9]
optimised elements for complex exponential of the alpha angle for ell = 4
COMPLEX16 alpha3[7]
optimised elements for complex exponential of the alpha angle for ell = 3
COMPLEX16 alpha2[5]
optimised elements for complex exponential of the alpha angle for ell = 2
a strcut to keep the wigner-d matrix elements
REAL8 d33[2][7]
wigner-d matrix elements for ell=3, mprime=+/-3 and positive[0] and negative[1] mprime
REAL8 d43[2][9]
wigner-d matrix elements for ell=4, mprime=+/-3 and positive[0] and negative[1] mprime
REAL8 d21[2][5]
wigner-d matrix elements for ell=2, mprime=+/-1 and positive[0] and negative[1] mprime
REAL8 d44[2][9]
wigner-d matrix elements for ell=4, mprime=+/-4 and positive[0] and negative[1] mprime
REAL8 d32[2][7]
wigner-d matrix elements for ell=3, mprime=+/-2 and positive[0] and negative[1] mprime
REAL8 d22[2][5]
wigner-d matrix elements for ell=2, mprime=+/-2 and positive[0] and negative[1] mprime
a strcut to keep the spherical harmonic terms
COMPLEX16 Ylm2[2][5]
optimised elements Ylms for ell = 2.
COMPLEX16 Ylm4[2][9]
optimised elements Ylms for ell = 4.
COMPLEX16 Ylm3[2][7]
optimised elements Ylms for ell = 3.
Structure storing initial and derived variables for IMRPhenomPv3HM.
REAL8 inclination
inclination - used to compute the angle thetaJN (rad)
REAL8 alpha0
Initial value of alpha angle (azimuthal precession angle)
REAL8 zeta_polariz
Angle to rotate the polarizations.
INT4 PRECESSING
integer to signify if system is precessing, 1 for false (not precessing), 0 for true (precessing)
REAL8 * data
double f_min
Definition: unicorn.c:22
double f_max
Definition: unicorn.c:23