Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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
56static 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
58static 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);
59static 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
61static REAL8 EstimateSafeFMinForTD(const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 deltaT);
62static REAL8 EstimateSafeFMaxForTD(const REAL8 f_max, const REAL8 dt);
63static REAL8 ComputeTau0(const REAL8 m1, const REAL8 m2, const REAL8 f_min);
64static size_t EstimateIMRLength(const REAL8 m1, const REAL8 m2, const REAL8 f_min, const REAL8 deltaT);
65
66static 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);
67static size_t find_instant_freq(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 target, const size_t start);
68static size_t find_peak_amp(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc);
69static int apply_phase_shift(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 shift);
70static int apply_inclination(const REAL8TimeSeries *hp, const REAL8TimeSeries *hc, const REAL8 inclination);
71
72static 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
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)) {
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 */
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,
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 */
545static 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 */
555static 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 */
564static 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 */
593static 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) {
633 *signalTD = NULL;
635 }
636 XLALREAL8FreqTimeFFT(*signalTD, signalFD, 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 */
652static 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 */
679static 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
696static 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
711static 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
727static 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 * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
COMPLEX16FrequencySeries * XLALResizeCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series, int first, size_t length)
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)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
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)
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 * XLALGPSSetREAL8(LIGOTimeGPS *epoch, REAL8 t)
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
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