LALSimulation  5.4.0.1-89842e6
LALSimIMRPhenomC.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012 Prayush Kumar, Frank Ohme, 2015 Michael Puerrer
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 /* The paper refered to here as the Main paper, is Phys. Rev. D 82, 064016 (2010)
22  * */
23 
24 #ifdef __GNUC__
25 #define UNUSED __attribute__ ((unused))
26 #else
27 #define UNUSED
28 #endif
29 
30 #include <math.h>
31 #include <complex.h>
32 #include <gsl/gsl_errno.h>
33 #include <gsl/gsl_spline.h>
34 
35 #include <lal/LALStdlib.h>
36 #include <lal/LALSimIMR.h>
37 #include <lal/LALConstants.h>
38 #include <lal/Date.h>
39 #include <lal/FrequencySeries.h>
40 #include <lal/StringInput.h>
41 #include <lal/TimeSeries.h>
42 #include <lal/TimeFreqFFT.h>
43 #include <lal/Units.h>
44 
46 
47 #ifndef _OPENMP
48 #define omp ignore
49 #endif
50 
51 /*
52  * private function prototypes; all internal functions use solar masses.
53  *
54  */
55 
56 static int IMRPhenomCGenerateFD(COMPLEX16FrequencySeries **htilde, const REAL8 phi0, const REAL8 deltaF, const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 f_max, const REAL8 distance, const BBHPhenomCParams *params);
57 
58 static int IMRPhenomCGenerateFDForTD(COMPLEX16FrequencySeries **htilde, const REAL8 t0, const REAL8 phi0, const REAL8 deltaF, const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 f_max, const REAL8 distance, const BBHPhenomCParams *params, const size_t nf);
59 static int IMRPhenomCGenerateTD(REAL8TimeSeries **h, const REAL8 phiPeak, size_t *ind_t0, const REAL8 deltaT, const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 f_max, const REAL8 distance, const BBHPhenomCParams *params);
60 
61 static REAL8 EstimateSafeFMinForTD(const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 deltaT);
62 static REAL8 EstimateSafeFMaxForTD(const REAL8 f_max, const REAL8 dt);
63 static REAL8 ComputeTau0(const REAL8 m1, const REAL8 m2, const REAL8 f_min);
64 static size_t EstimateIMRLength(const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 deltaT);
65 
66 static int FDToTD(REAL8TimeSeries **signalTD, const COMPLEX16FrequencySeries *signalFD, const REAL8 totalMass, const REAL8 deltaT, const REAL8 f_min, const REAL8 f_max, const REAL8 f_min_wide, const REAL8 f_max_wide);
67 static size_t find_instant_freq(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 target, const size_t start);
68 static size_t find_peak_amp(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc);
69 static int apply_phase_shift(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 shift);
70 static int apply_inclination(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 inclination);
71 
72 static REAL8 PlanckTaper(const REAL8 t, const REAL8 t1, const REAL8 t2);
73 
74 /**
75  * @addtogroup LALSimIMRPhenom_c
76  * @{
77  * @name Routines for IMR Phenomenological Model "C"
78  * @{
79  */
80 
81 /**
82  * Driver routine to compute the spin-aligned, inspiral-merger-ringdown
83  * phenomenological waveform IMRPhenomC in the frequency domain.
84  *
85  * Reference: http://arxiv.org/pdf/1005.3306v3.pdf
86  * - Waveform: Eq.(5.3)-(5.13)
87  * - Coefficients: Eq.(5.14) and Table II
88  *
89  * All input parameters should be in SI units. Angles should be in radians.
90  */
92  COMPLEX16FrequencySeries **htilde, /**< FD waveform */
93  const REAL8 phi0, /**< orbital phase at peak (rad) */
94  const REAL8 deltaF, /**< sampling interval (Hz) */
95  const REAL8 m1_SI, /**< mass of companion 1 (kg) */
96  const REAL8 m2_SI, /**< mass of companion 2 (kg) */
97  const REAL8 chi, /**< mass-weighted aligned-spin parameter */
98  const REAL8 f_min, /**< starting GW frequency (Hz) */
99  const REAL8 f_max, /**< end frequency; 0 defaults to ringdown cutoff freq */
100  const REAL8 distance, /**< distance of source (m) */
101  LALDict *extraParams /**< linked list containing the extra testing GR parameters */
102 ) {
104  int status;
105  REAL8 f_max_prime;
106 
107  /* external: SI; internal: solar masses */
108  const REAL8 m1 = m1_SI / LAL_MSUN_SI;
109  const REAL8 m2 = m2_SI / LAL_MSUN_SI;
110 
111  /* check inputs for sanity */
112  if (*htilde) XLAL_ERROR(XLAL_EFAULT);
113  if (deltaF <= 0) XLAL_ERROR(XLAL_EDOM);
114  if (m1 <= 0) XLAL_ERROR(XLAL_EDOM);
115  if (m2 <= 0) XLAL_ERROR(XLAL_EDOM);
116  if (fabs(chi) > 1) XLAL_ERROR(XLAL_EDOM);
117  if (f_min <= 0) XLAL_ERROR(XLAL_EDOM);
118  if (f_max < 0) XLAL_ERROR(XLAL_EDOM);
119  if (distance <= 0) XLAL_ERROR(XLAL_EDOM);
120 
121  /* If spins are above 0.9 or below -0.9, throw an error */
122  if (chi > 0.9 || chi < -0.9)
123  XLAL_ERROR(XLAL_EDOM, "Spins outside the range [-0.9,0.9] are not supported\n");
124 
125  /* If mass ratio is above 4 and below 20, give a warning, and if it is above
126  * 20, throw an error */
127  REAL8 q = (m1 > m2) ? (m1 / m2) : (m2 / m1);
128 
129  if (q > 20.0)
130  XLAL_ERROR(XLAL_EDOM, "Mass ratio is way outside the calibration range. m1/m2 should be <= 20.\n");
131  else if (q > 4.0)
132  XLAL_PRINT_WARNING("Warning: The model is only calibrated for m1/m2 <= 4.\n");
133 
134  /* phenomenological parameters*/
135  params = ComputeIMRPhenomCParams(m1, m2, chi, extraParams);
137  if (params->fCut <= f_min)
138  XLAL_ERROR(XLAL_EDOM, "(fCut = 0.15M) <= f_min\n");
139 
140  /* default f_max to params->fCut */
141  f_max_prime = f_max ? f_max : params->fCut;
142  f_max_prime = (f_max_prime > params->fCut) ? params->fCut : f_max_prime;
143  if (f_max_prime <= f_min)
144  XLAL_ERROR(XLAL_EDOM, "f_max <= f_min\n");
145 
146  status = IMRPhenomCGenerateFD(htilde, phi0, deltaF, m1, m2, f_min, f_max_prime, distance, params);
147 
148  if (f_max_prime < f_max) {
149  // The user has requested a higher f_max than Mf=params->fCut.
150  // Resize the frequency series to fill with zeros to fill with zeros beyond the cutoff frequency.
151  size_t n_full = NextPow2_PC(f_max / deltaF) + 1; // we actually want to have the length be a power of 2 + 1 power of 2 + 1
152  *htilde = XLALResizeCOMPLEX16FrequencySeries(*htilde, 0, n_full);
153  }
154 
155  LALFree(params);
156  return status;
157 }
158 
159 /**
160  * Convenience function to quickly find the default final
161  * frequency.
162  */
164  const REAL8 m1,
165  const REAL8 m2,
166  const REAL8 chi
167 ) {
168  BBHPhenomCParams *phenomParams;
169  LALDict *extraParams = NULL;
170  phenomParams = ComputeIMRPhenomCParams(m1, m2, chi, extraParams);
171  return phenomParams->fCut;
172 }
173 
174 /**
175  * Driver routine to compute the spin-aligned, inspiral-merger-ringdown
176  * phenomenological waveform IMRPhenomC in the time domain.
177  * (Note that this approximant was constructed as a smooth function in
178  * the frequency domain, so there might be spurious effects after
179  * transforming into the time domain. One example are small amplitude
180  * oscillations just before merger.)
181  *
182  * Reference: http://arxiv.org/pdf/1005.3306v3.pdf
183  * - Waveform: Eq.(5.3)-(5.13)
184  * - Coefficients: Eq.(5.14) and Table II
185  *
186  * All input parameters should be in SI units. Angles should be in radians.
187  */
189  REAL8TimeSeries **hplus, /**< +-polarization waveform */
190  REAL8TimeSeries **hcross, /**< x-polarization waveform */
191  const REAL8 phiPeak, /**< orbital phase at peak (rad) */
192  const REAL8 deltaT, /**< sampling interval (s) */
193  const REAL8 m1_SI, /**< mass of companion 1 (kg) */
194  const REAL8 m2_SI, /**< mass of companion 2 (kg) */
195  const REAL8 chi, /**< mass-weighted aligned-spin parameter */
196  const REAL8 f_min, /**< starting GW frequency (Hz) */
197  const REAL8 f_max, /**< end GW frequency; 0 defaults to ringdown cutoff freq */
198  const REAL8 distance, /**< distance of source (m) */
199  const REAL8 inclination, /**< inclination of source (rad) */
200  LALDict *extraParams /**< linked list containing the extra testing GR parameters */
201 ) {
203  size_t cut_ind, peak_ind, ind_t0;
204  REAL8 peak_phase; /* measured, not intended */
205  REAL8 f_max_prime;
206 
207  /* external: SI; internal: solar masses */
208  const REAL8 m1 = m1_SI / LAL_MSUN_SI;
209  const REAL8 m2 = m2_SI / LAL_MSUN_SI;
210  const REAL8 fISCO = 0.022 / ((m1 + m2) * LAL_MTSUN_SI);
211 
212  /* check inputs for sanity */
213  if (*hplus) XLAL_ERROR(XLAL_EFAULT);
214  if (*hcross) XLAL_ERROR(XLAL_EFAULT);
215  if (deltaT <= 0) XLAL_ERROR(XLAL_EDOM);
216  if (m1 < 0) XLAL_ERROR(XLAL_EDOM);
217  if (m2 < 0) XLAL_ERROR(XLAL_EDOM);
218  if (fabs(chi) > 1) XLAL_ERROR(XLAL_EDOM);
219  if (f_min <= 0) XLAL_ERROR(XLAL_EDOM);
220  if (f_max < 0) XLAL_ERROR(XLAL_EDOM);
221  if (distance <= 0) XLAL_ERROR(XLAL_EDOM);
222 
223  /* If spins are above 0.9 or below -0.9, throw an error */
224  if (chi > 0.9 || chi < -0.9)
225  XLAL_ERROR(XLAL_EDOM, "Spins outside the range [-0.9,0.9] are not supported\n");
226 
227  /* If mass ratio is above 4 and below 20, give a warning, and if it is above
228  * 20, throw an error */
229  REAL8 q = (m1 > m2) ? (m1 / m2) : (m2 / m1);
230 
231  if (q > 20.0)
232  XLAL_ERROR(XLAL_EDOM, "Mass ratio is way outside the calibration range. m1/m2 should be <= 20.\n");
233  else if (q > 4.0)
234  XLAL_PRINT_WARNING("Warning: The model is only calibrated for m1/m2 <= 4.\n");
235 
236  /* phenomenological parameters*/
237  params = ComputeIMRPhenomCParams(m1, m2, chi, extraParams);
239  if (params->fCut <= f_min)
240  XLAL_ERROR(XLAL_EDOM, "(fCut = 0.15M) <= f_min\n");
241 
242  /* default f_max to params->fCut */
243  f_max_prime = f_max ? f_max : params->fCut;
244  f_max_prime = (f_max_prime > params->fCut) ? params->fCut : f_max_prime;
245  if (f_max_prime <= f_min)
246  XLAL_ERROR(XLAL_EDOM, "f_max <= f_min\n");
247 
248  /* generate plus */
249 
250  IMRPhenomCGenerateTD(hplus, 0., &ind_t0, deltaT, m1, m2, f_min, f_max_prime, distance, params);
251  if (!(*hplus)) {
252  XLALFree(params);
254  }
255 
256  /* generate hcross, which is hplus w/ GW phase shifted by -pi/2
257  * <==> orb. phase shifted by -pi/4 */
258  IMRPhenomCGenerateTD(hcross, -LAL_PI_4, &ind_t0, deltaT, m1, m2, f_min, f_max_prime, distance, params);
259  if (!(*hcross)) {
261  *hplus = NULL;
263  }
264 
265  /* clip the parts below f_min */
266  //const size_t start_ind = ((*hplus)->data->length + EstimateIMRLength(m1, m2, f_max_prime, deltaT)) > EstimateIMRLength(m1, m2, f_min, deltaT) ? ((*hplus)->data->length + EstimateIMRLength(m1, m2, f_max_prime, deltaT) - EstimateIMRLength(m1, m2, f_min, deltaT)) : 0;
267 
268  //const size_t start_ind = ((*hplus)->data->length
269  // - EstimateIMRLength(m1, m2, 0.95 * f_min + 0.05 * f_max_prime, deltaT));
270 
271 
272  peak_ind = find_peak_amp(*hplus, *hcross);
273 
274  cut_ind =find_instant_freq(*hplus, *hcross, f_min < fISCO/2. ? f_min : fISCO/2., peak_ind);
275  *hplus = XLALResizeREAL8TimeSeries(*hplus, cut_ind, (*hplus)->data->length - cut_ind);
276  *hcross = XLALResizeREAL8TimeSeries(*hcross, cut_ind, (*hcross)->data->length - cut_ind);
277 
278  if (!(*hplus) || !(*hcross))
280 
281  /* set phase and time at peak */
282  peak_ind = find_peak_amp(*hplus, *hcross);
283  peak_phase = atan2((*hcross)->data->data[peak_ind], (*hplus)->data->data[peak_ind]);
284  // NB: factor of 2 b/c phiPeak is *orbital* phase, and we're shifting GW phase
285  apply_phase_shift(*hplus, *hcross, 2.*phiPeak - peak_phase);
286  XLALGPSSetREAL8(&((*hplus)->epoch), -(peak_ind * deltaT));
287  XLALGPSSetREAL8(&((*hcross)->epoch), -(peak_ind * deltaT));
288 
289  /* apply inclination */
290  XLALFree(params);
291  return apply_inclination(*hplus, *hcross, inclination);
292 }
293 
294 /** @} */
295 /** @} */
296 
297 
298 /* *********************************************************************************/
299 /* The following private function generates IMRPhenomC frequency-domain waveforms */
300 /* given coefficients */
301 /* *********************************************************************************/
302 
304  COMPLEX16FrequencySeries **htilde, /**< FD waveform */
305  const REAL8 phi0, /**< phase at peak */
306  const REAL8 deltaF, /**< frequency resolution */
307  const REAL8 m1, /**< mass of companion 1 [solar masses] */
308  const REAL8 m2, /**< mass of companion 2 [solar masses] */
309  //const REAL8 chi, /**< mass-weighted aligned-spin parameter */
310  const REAL8 f_min, /**< start frequency */
311  const REAL8 f_max, /**< end frequency */
312  const REAL8 distance, /**< distance to source (m) */
313  const BBHPhenomCParams *params /**< from ComputeIMRPhenomCParams */
314 ) {
315  LIGOTimeGPS ligotimegps_zero = LIGOTIMEGPSZERO; // = {0, 0}
316 
317  int errcode = XLAL_SUCCESS;
318  /*
319  We can't call XLAL_ERROR() directly with OpenMP on.
320  Keep track of return codes for each thread and in addition use flush to get out of
321  the parallel for loop as soon as possible if something went wrong in any thread.
322  */
323 
324  const REAL8 M = m1 + m2;
325  const REAL8 eta = m1 * m2 / (M * M);
326 
327  /* Memory to temporarily store components of amplitude and phase */
328  /*
329  REAL8 phSPA, phPM, phRD, aPM, aRD;
330  REAL8 wPlusf1, wPlusf2, wMinusf1, wMinusf2, wPlusf0, wMinusf0;
331  */
332 
333  /* compute the amplitude pre-factor */
334  REAL8 amp0 = 2. * sqrt(5. / (64.*LAL_PI)) * M * LAL_MRSUN_SI * M * LAL_MTSUN_SI / distance;
335 
336  /* allocate htilde */
337  size_t n = NextPow2_PC(f_max / deltaF) + 1;
338  /* coalesce at t=0 */
339  XLALGPSAdd(&ligotimegps_zero, -1. / deltaF); // shift by overall length in time
340  *htilde = XLALCreateCOMPLEX16FrequencySeries("htilde: FD waveform", &ligotimegps_zero, 0.0,
341  deltaF, &lalStrainUnit, n);
342  memset((*htilde)->data->data, 0, n * sizeof(COMPLEX16));
343  XLALUnitMultiply(&((*htilde)->sampleUnits), &((*htilde)->sampleUnits), &lalSecondUnit);
344  if (!(*htilde)) XLAL_ERROR(XLAL_EFUNC);
345 
346  size_t ind_min = (size_t) (f_min / deltaF);
347  size_t ind_max = (size_t) (f_max / deltaF);
348 
349  /* Set up spline for phase */
350  gsl_interp_accel *acc = gsl_interp_accel_alloc();
351  size_t L = ind_max - ind_min;
352  gsl_spline *phiI = gsl_spline_alloc(gsl_interp_cspline, L);
353  REAL8 *freqs = XLALMalloc(L*sizeof(REAL8));
354  REAL8 *phis = XLALMalloc(L*sizeof(REAL8));
355 
356  /* now generate the waveform */
357  #pragma omp parallel for
358  for (size_t i = ind_min; i < ind_max; i++)
359  {
360 
361  REAL8 phPhenomC = 0.0;
362  REAL8 aPhenomC = 0.0;
363  REAL8 f = i * deltaF;
364 
365  int per_thread_errcode;
366  #pragma omp flush(errcode)
367  if (errcode != XLAL_SUCCESS)
368  goto skip;
369 
370  per_thread_errcode = IMRPhenomCGenerateAmpPhase( &aPhenomC, &phPhenomC, f, eta, params );
371  if (per_thread_errcode != XLAL_SUCCESS) {
372  errcode = per_thread_errcode;
373  #pragma omp flush(errcode)
374  }
375 
376  phPhenomC -= 2.*phi0; // factor of 2 b/c phi0 is orbital phase
377 
378  freqs[i-ind_min] = f;
379  phis[i-ind_min] = -phPhenomC; // PhenomP uses cexp(-I*phPhenomC); want to use same phase adjustment code, so we will flip the sign of the phase
380 
381  /* generate the waveform */
382  ((*htilde)->data->data)[i] = amp0 * aPhenomC * cos(phPhenomC);
383  ((*htilde)->data->data)[i] += -I * amp0 * aPhenomC * sin(phPhenomC);
384 
385  skip: /* this statement intentionally left blank */;
386 
387  }
388 
389  if( errcode != XLAL_SUCCESS )
390  XLAL_ERROR(errcode);
391 
392  /* Correct phasing so we coalesce at t=0 (with the definition of the epoch=-1/deltaF above) */
393  gsl_spline_init(phiI, freqs, phis, L);
394 
395  REAL8 f_final = params->fRingDown;
396  XLAL_PRINT_INFO("f_ringdown = %g\n", f_final);
397 
398  // Prevent gsl interpolation errors
399  if (f_final > freqs[L-1])
400  f_final = freqs[L-1];
401  if (f_final < freqs[0])
402  XLAL_ERROR(XLAL_EDOM, "f_ringdown <= f_min\n");
403 
404  /* Time correction is t(f_final) = 1/(2pi) dphi/df (f_final) */
405  REAL8 t_corr = gsl_spline_eval_deriv(phiI, f_final, acc) / (2*LAL_PI);
406  XLAL_PRINT_INFO("t_corr = %g\n", t_corr);
407  /* Now correct phase */
408  for (size_t i = ind_min; i < ind_max; i++) {
409  REAL8 f = i * deltaF;
410  ((*htilde)->data->data)[i] *= cexp(-2*LAL_PI * I * f * t_corr);
411  }
412 
413  gsl_spline_free(phiI);
414  gsl_interp_accel_free(acc);
415  XLALFree(freqs);
416  XLALFree(phis);
417 
418  return XLAL_SUCCESS;
419 }
420 
422  COMPLEX16FrequencySeries **htilde, /**< FD waveform */
423  const REAL8 t0, /**< time of coalescence */
424  const REAL8 phi0, /**< phase at peak */
425  const REAL8 deltaF, /**< frequency resolution */
426  const REAL8 m1, /**< mass of companion 1 [solar masses] */
427  const REAL8 m2, /**< mass of companion 2 [solar masses] */
428  //const REAL8 chi, /**< mass-weighted aligned-spin parameter */
429  const REAL8 f_min, /**< start frequency */
430  const REAL8 f_max, /**< end frequency */
431  const REAL8 distance, /**< distance to source (m) */
432  const BBHPhenomCParams *params, /**< from ComputeIMRPhenomCParams */
433  const size_t nf /**< Length of frequency vector required */
434 ) {
435  static LIGOTimeGPS ligotimegps_zero = {0, 0};
436  size_t i;
437  INT4 errcode;
438 
439  const REAL8 M = m1 + m2;
440  const REAL8 eta = m1 * m2 / (M * M);
441 
442  /* Memory to temporarily store components of amplitude and phase */
443  /*
444  REAL8 phSPA, phPM, phRD, aPM, aRD;
445  REAL8 wPlusf1, wPlusf2, wMinusf1, wMinusf2, wPlusf0, wMinusf0;
446  */
447  REAL8 phPhenomC = 0.0;
448  REAL8 aPhenomC = 0.0;
449 
450  /* compute the amplitude pre-factor */
451  REAL8 amp0 = 2. * sqrt(5. / (64.*LAL_PI)) * M * LAL_MRSUN_SI * M * LAL_MTSUN_SI / distance;
452 
453  /* allocate htilde */
454  size_t n = NextPow2_PC(f_max / deltaF) + 1;
455  if ( n > nf )
456  XLAL_ERROR(XLAL_EDOM, "The required length passed as input wont fit the FD waveform\n");
457  else
458  n = nf;
459  *htilde = XLALCreateCOMPLEX16FrequencySeries("htilde: FD waveform", &ligotimegps_zero, 0.0,
460  deltaF, &lalStrainUnit, n);
461  memset((*htilde)->data->data, 0, n * sizeof(COMPLEX16));
462  XLALUnitMultiply(&((*htilde)->sampleUnits), &((*htilde)->sampleUnits), &lalSecondUnit);
463  if (!(*htilde)) XLAL_ERROR(XLAL_EFUNC);
464 
465  /* now generate the waveform */
466  size_t ind_max = (size_t) (f_max / deltaF);
467  for (i = (size_t) (f_min / deltaF); i < ind_max; i++)
468  {
469  REAL8 f = i * deltaF;
470 
471  errcode = IMRPhenomCGenerateAmpPhase( &aPhenomC, &phPhenomC, f, eta, params );
472  if( errcode != XLAL_SUCCESS )
474  phPhenomC -= 2.*phi0; // factor of 2 b/c phi0 is orbital phase
475  phPhenomC += 2.*LAL_PI*f*t0; //shifting the coalescence to t=t0
476 
477  /* generate the waveform */
478  ((*htilde)->data->data)[i] = amp0 * aPhenomC * cos(phPhenomC);
479  ((*htilde)->data->data)[i] += -I * amp0 * aPhenomC * sin(phPhenomC);
480  }
481 
482  return XLAL_SUCCESS;
483 }
484 
485 /*
486  * Private function to generate time-domain waveforms given coefficients
487  */
489  REAL8TimeSeries **h,
490  const REAL8 phi0,
491  size_t *ind_t0,
492  const REAL8 deltaT,
493  const REAL8 m1,
494  const REAL8 m2,
495  //const REAL8 chi,
496  const REAL8 f_min,
497  const REAL8 f_max,
498  const REAL8 distance,
499  const BBHPhenomCParams *params
500 ) {
501  REAL8 deltaF;
502  COMPLEX16FrequencySeries *htilde=NULL;
503  /* We will generate the waveform from a frequency which is lower than the
504  * f_min chosen. Also the cutoff frequency is higher than the f_max. We
505  * will later apply a window function, and truncate the time-domain waveform
506  * below an instantaneous frequency f_min. */
507  REAL8 f_min_wide = EstimateSafeFMinForTD(m1, m2, f_min, deltaT);
508  const REAL8 f_max_wide = params->fCut; //0.5 / deltaT;
509  if (EstimateSafeFMaxForTD(f_max, deltaT) > f_max_wide)
510  XLAL_PRINT_WARNING("Warning: sampling rate (%" LAL_REAL8_FORMAT " Hz) too low for expected spectral content (%" LAL_REAL8_FORMAT " Hz) \n", deltaT, EstimateSafeFMaxForTD(f_max, deltaT));
511 
512  const size_t nt = NextPow2_PC(EstimateIMRLength(m1, m2, f_min_wide, deltaT));
513  const size_t ne = EstimateIMRLength(m1, m2, params->fRingDown, deltaT);
514  *ind_t0 = ((nt - EstimateIMRLength(m1, m2, f_min_wide, deltaT)) > (4 * ne)) ? (nt -(2* ne)) : (nt - (1 * ne));
515  deltaF = 1. / (deltaT * (REAL8) nt);
516 
517 
518  /* Get the length in time for the entire IMR waveform, so the coalesence
519  * can be positioned (in time) accordingly, to avoid wrap-around */
520  REAL8 t0 = deltaT * ((REAL8) *ind_t0);
521 
522 
523  /* generate in frequency domain */
524  if (IMRPhenomCGenerateFDForTD(&htilde, t0, phi0, deltaF, m1, m2, //chi,
525  f_min_wide, f_max_wide, distance, params, nt/2 + 1)) XLAL_ERROR(XLAL_EFUNC);
526 
527 
528  /* convert to time domain */
529  FDToTD(h, htilde, m1 + m2, deltaT, f_min, f_max, f_min_wide, f_max_wide);
530 
532  if (!*h) XLAL_ERROR(XLAL_EFUNC);
533 
534  return XLAL_SUCCESS;
535 }
536 
537 /* *********************************************************************************/
538 /* The following functions are borrowed as-it-is from LALSimIMRPhenom.c, and used */
539 /* to generate the time-domain version of the frequency domain PhenomC approximant */
540 /* *********************************************************************************/
541 
542 /*
543  * Return tau0, the Newtonian chirp length estimate.
544  */
545 static REAL8 ComputeTau0(const REAL8 m1, const REAL8 m2, const REAL8 f_min) {
546  const REAL8 totalMass = m1 + m2;
547  const REAL8 eta = m1 * m2 / (totalMass * totalMass);
548  return 5. * totalMass * LAL_MTSUN_SI / (256. * eta * pow(LAL_PI * totalMass * LAL_MTSUN_SI * f_min, 8./3.));
549 }
550 
551 /*
552  * Estimate the length of a TD vector that can hold the waveform as the Newtonian
553  * chirp time tau0 plus 1000 M.
554  */
555 static size_t EstimateIMRLength(const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 deltaT) {
556  return (size_t) floor((ComputeTau0(m1, m2, f_min) + 1000 * (m1 + m2) * LAL_MTSUN_SI) / deltaT);
557 }
558 
559 /*
560  * Find a lower value for f_min (using the definition of Newtonian chirp
561  * time) such that the waveform has a minimum length of tau0. This is
562  * necessary to avoid FFT artifacts.
563  */
564 static REAL8 EstimateSafeFMinForTD(const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 deltaT) {
565  const REAL8 totalMass = m1 + m2;
566  const REAL8 eta = m1 * m2 / (totalMass * totalMass);
567  const REAL8 fISCO = 0.022 / (totalMass * LAL_MTSUN_SI);
568  REAL8 tau0 = deltaT * NextPow2_PC(1.5 * EstimateIMRLength(m1, m2, f_min, deltaT));
569  REAL8 temp_f_min = pow((tau0 * 256. * eta * pow(totalMass * LAL_MTSUN_SI, 5./3.) / 5.), -3./8.) / LAL_PI;
570  if (temp_f_min > f_min) temp_f_min = f_min;
571  if (temp_f_min < 0.5) temp_f_min = 0.5;
572  if (temp_f_min > fISCO/2.) temp_f_min = fISCO/2.;
573  return temp_f_min;
574 }
575 
576 /*
577  * Find a higher value of f_max so that we can safely apply a window later.
578  */
580  REAL8 temp_f_max = 1.025 * f_max;
581 
582  /* make sure that these frequencies are not too out of range */
583  if (temp_f_max > 2. / deltaT - 100.) temp_f_max = 2. / deltaT - 100.;
584  return temp_f_max;
585 }
586 
587 
588 /*
589  * Window and IFFT a FD waveform to TD, then window in TD.
590  * Requires that the FD waveform be generated outside of f_min and f_max.
591  * FD waveform is modified.
592  */
593 static int FDToTD(REAL8TimeSeries **signalTD, const COMPLEX16FrequencySeries *signalFD, const REAL8 totalMass, const REAL8 deltaT, const REAL8 f_min, const REAL8 f_max, const REAL8 f_min_wide, const REAL8 f_max_wide) {
594  const LIGOTimeGPS gpstime_zero = {0, 0};
595  const size_t nf = signalFD->data->length;
596  const size_t nt = 2 * (nf - 1);
597 
598  /* Calculate the expected lengths of the FD and TD vectors from the
599  * deltaF and deltaT passed in to this function */
600  //const size_t exp_nt = (size_t) 1. / (signalFD->deltaF * deltaT);
601  //const size_t exp_nf = (exp_nt / 2) + 1;
602 
603  const REAL8 windowLength = 20. * totalMass * LAL_MTSUN_SI / deltaT;
604  const REAL8 winFLo = 0.2*f_min_wide + 0.8*f_min;
605  /* frequency used for tapering, slightly less than f_min to minimize FFT artifacts
606  * equivalent to winFLo = f_min_wide + 0.8*(f_min - f_min_wide) */
607  REAL8 winFHi = f_max_wide;
608  COMPLEX16 *FDdata = signalFD->data->data;
609  REAL8FFTPlan *revPlan;
610  REAL8 *TDdata;
611  size_t k;
612 
613  /* check inputs */
614  if (f_min_wide >= f_min) XLAL_ERROR(XLAL_EDOM);
615 
616  /* apply the softening window function */
617  if (winFHi > 0.5 / deltaT) winFHi = 0.5 / deltaT;
618  for (k = nf;k--;) {
619  const REAL8 f = k / (deltaT * nt);
620  REAL8 softWin = PlanckTaper(f, f_min_wide, winFLo) * (1.0 - PlanckTaper(f, f_max, winFHi));
621  FDdata[k] *= softWin;
622  }
623 
624 
625  /* allocate output */
626  *signalTD = XLALCreateREAL8TimeSeries("h", &gpstime_zero, 0.0, deltaT, &lalStrainUnit, nt);
627 
628 
629  /* Inverse Fourier transform */
630  revPlan = XLALCreateReverseREAL8FFTPlan(nt, 1);
631  if (!revPlan) {
632  XLALDestroyREAL8TimeSeries(*signalTD);
633  *signalTD = NULL;
635  }
636  XLALREAL8FreqTimeFFT(*signalTD, signalFD, revPlan);
637  XLALDestroyREAL8FFTPlan(revPlan);
638  if (!(*signalTD)) XLAL_ERROR(XLAL_EFUNC);
639 
640 
641  /* apply a linearly decreasing window at the end
642  * of the waveform in order to avoid edge effects. */
643  if (windowLength > (*signalTD)->data->length) XLAL_ERROR(XLAL_ERANGE);
644  TDdata = (*signalTD)->data->data;
645  for (k = windowLength; k--;)
646  TDdata[nt-k-1] *= k / windowLength;
647 
648  return XLAL_SUCCESS;
649 }
650 
651 /* return the index before the instantaneous frequency rises past target */
652 static size_t find_instant_freq(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 target, const size_t start) {
653  /* const size_t n = hp->data->length - 1; */
654  /* size_t k = (start + 1) > 0 ? (start + 1) : 0; */
655  size_t k = start;
656  size_t target_ind = 0;
657 
658  /* Use second order differencing to find the instantaneous frequency as
659  * h = A e^(2 pi i f t) ==> f = d/dt(h) / (2*pi*h) */
660  for (; k > 0 /*< n*/; k--) {
661  const REAL8 hpDot = (hp->data->data[k+1] - hp->data->data[k-1]) / (2 * hp->deltaT);
662  const REAL8 hcDot = (hc->data->data[k+1] - hc->data->data[k-1]) / (2 * hc->deltaT);
663  REAL8 f = hcDot * hp->data->data[k] - hpDot * hc->data->data[k];
664  f /= LAL_TWOPI;
665  f /= hp->data->data[k] * hp->data->data[k] + hc->data->data[k] * hc->data->data[k];
666  if (f <= target){
667  target_ind = k;
668  break;
669  //return k;// - 1;
670  }
671  }
672 
673 
674  return target_ind;
676 }
677 
678 /* Return the index of the sample at with the peak amplitude */
679 static size_t find_peak_amp(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc) {
680  const REAL8 *hpdata = hp->data->data;
681  const REAL8 *hcdata = hc->data->data;
682  size_t k = hp->data->length;
683  size_t peak_ind = -1;
684  REAL8 peak_amp_sq = 0.;
685 
686  for (;k--;) {
687  const REAL8 amp_sq = hpdata[k] * hpdata[k] + hcdata[k] * hcdata[k];
688  if (amp_sq > peak_amp_sq) {
689  peak_ind = k;
690  peak_amp_sq = amp_sq;
691  }
692  }
693  return peak_ind;
694 }
695 
696 static int apply_phase_shift(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 shift) {
697  REAL8 *hpdata = hp->data->data;
698  REAL8 *hcdata = hc->data->data;
699  size_t k = hp->data->length;
700  const double cs = cos(shift);
701  const double ss = sin(shift);
702 
703  for (;k--;) {
704  const REAL8 temp_hpdata = hpdata[k] * cs - hcdata[k] * ss;
705  hcdata[k] = hpdata[k] * ss + hcdata[k] * cs;
706  hpdata[k] = temp_hpdata;
707  }
708  return 0;
709 }
710 
711 static int apply_inclination(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 inclination) {
712  REAL8 inclFacPlus, inclFacCross;
713  REAL8 *hpdata = hp->data->data;
714  REAL8 *hcdata = hc->data->data;
715  size_t k = hp->data->length;
716 
717  inclFacCross = cos(inclination);
718  inclFacPlus = 0.5 * (1. + inclFacCross * inclFacCross);
719  for (;k--;) {
720  hpdata[k] *= inclFacPlus;
721  hcdata[k] *= inclFacCross;
722  }
723 
724  return XLAL_SUCCESS;
725 }
726 
727 static REAL8 PlanckTaper(const REAL8 t, const REAL8 t1, const REAL8 t2)
728  {
729  REAL8 taper;
730  if (t <= t1) {
731  taper = 0.;
732  }
733  else if (t >= t2) {
734  taper = 1.;
735  }
736  else {
737  taper = 1. / (exp((t2 - t1)/(t - t1) + (t2 - t1)/(t - t2)) + 1.);
738  }
739 
740  return taper;
741  }
#define LALFree(p)
static REAL8 ComputeTau0(const REAL8 m1, const REAL8 m2, const REAL8 f_min)
static REAL8 EstimateSafeFMinForTD(const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 deltaT)
static size_t find_peak_amp(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc)
static int IMRPhenomCGenerateFD(COMPLEX16FrequencySeries **htilde, const REAL8 phi0, const REAL8 deltaF, const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 f_max, const REAL8 distance, const BBHPhenomCParams *params)
static REAL8 PlanckTaper(const REAL8 t, const REAL8 t1, const REAL8 t2)
static int apply_phase_shift(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 shift)
static int IMRPhenomCGenerateFDForTD(COMPLEX16FrequencySeries **htilde, const REAL8 t0, const REAL8 phi0, const REAL8 deltaF, const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 f_max, const REAL8 distance, const BBHPhenomCParams *params, const size_t nf)
static int IMRPhenomCGenerateTD(REAL8TimeSeries **h, const REAL8 phiPeak, size_t *ind_t0, const REAL8 deltaT, const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 f_max, const REAL8 distance, const BBHPhenomCParams *params)
static int FDToTD(REAL8TimeSeries **signalTD, const COMPLEX16FrequencySeries *signalFD, const REAL8 totalMass, const REAL8 deltaT, const REAL8 f_min, const REAL8 f_max, const REAL8 f_min_wide, const REAL8 f_max_wide)
static size_t find_instant_freq(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 target, const size_t start)
static int apply_inclination(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 inclination)
static size_t EstimateIMRLength(const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 deltaT)
static REAL8 EstimateSafeFMaxForTD(const REAL8 f_max, const REAL8 dt)
static size_t NextPow2_PC(const size_t n)
static int IMRPhenomCGenerateAmpPhase(REAL8 *amplitude, REAL8 *phasing, REAL8 f, REAL8 eta, const BBHPhenomCParams *params)
static BBHPhenomCParams * ComputeIMRPhenomCParams(const REAL8 m1, const REAL8 m2, const REAL8 chi, LALDict *extraParams)
REAL8 M
Definition: bh_qnmode.c:133
double dt
Definition: bh_ringdown.c:113
double i
Definition: bh_ringdown.c:118
COMPLEX16FrequencySeries * XLALResizeCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series, int first, size_t length)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
#define LAL_MSUN_SI
#define LAL_PI
#define LAL_TWOPI
#define LAL_MTSUN_SI
#define LAL_PI_4
#define LAL_MRSUN_SI
#define LIGOTIMEGPSZERO
double complex COMPLEX16
double REAL8
int32_t INT4
void * XLALMalloc(size_t n)
void XLALFree(void *p)
int XLALSimIMRPhenomCGenerateTD(REAL8TimeSeries **hplus, REAL8TimeSeries **hcross, const REAL8 phiPeak, const REAL8 deltaT, const REAL8 m1_SI, const REAL8 m2_SI, const REAL8 chi, const REAL8 f_min, const REAL8 f_max, const REAL8 distance, const REAL8 inclination, LALDict *extraParams)
Driver routine to compute the spin-aligned, inspiral-merger-ringdown phenomenological waveform IMRPhe...
int XLALSimIMRPhenomCGenerateFD(COMPLEX16FrequencySeries **htilde, const REAL8 phi0, const REAL8 deltaF, const REAL8 m1_SI, const REAL8 m2_SI, const REAL8 chi, const REAL8 f_min, const REAL8 f_max, const REAL8 distance, LALDict *extraParams)
Driver routine to compute the spin-aligned, inspiral-merger-ringdown phenomenological waveform IMRPhe...
double XLALSimIMRPhenomCGetFinalFreq(const REAL8 m1, const REAL8 m2, const REAL8 chi)
Convenience function to quickly find the default final frequency.
#define LAL_REAL8_FORMAT
static const INT4 q
void XLALDestroyREAL8FFTPlan(REAL8FFTPlan *plan)
REAL8FFTPlan * XLALCreateReverseREAL8FFTPlan(UINT4 size, int measurelvl)
int XLALREAL8FreqTimeFFT(REAL8TimeSeries *tser, const COMPLEX16FrequencySeries *freq, const REAL8FFTPlan *plan)
REAL8TimeSeries * XLALResizeREAL8TimeSeries(REAL8TimeSeries *series, int first, size_t length)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
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_PRINT_WARNING(...)
XLAL_SUCCESS
XLAL_EFAULT
XLAL_ERANGE
XLAL_EFUNC
XLAL_EDOM
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
LIGOTimeGPS * XLALGPSSetREAL8(LIGOTimeGPS *epoch, REAL8 t)
string status
COMPLEX16Sequence * data
COMPLEX16 * data
REAL8Sequence * data
REAL8 * data
Definition: burst.c:245
double f_min
Definition: unicorn.c:22
double deltaT
Definition: unicorn.c:24
double f_max
Definition: unicorn.c:23