Loading [MathJax]/extensions/TeX/AMSmath.js
LALSimulation 6.2.0.1-b246709
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
LALSimIMRSpinPrecEOBv4P.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2017-2019 Sylvain Marsat, Serguei Ossokine, Roberto Cotesta
3 * 2016-2017 Stas Babak, Andrea Taracchini (Precessing EOB)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with with program; see the file COPYING. If not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 * MA 02110-1301 USA
19 */
20#ifndef _LALSIMIMRSPINPRECEOBv4P_C
21#define _LALSIMIMRSPINPRECEOBv4P_C
22/**
23 * @addtogroup LALSimIMRSpinPrecEOBv4P_c
24 *
25 * @author Sylvain Marsat, Serguei Ossokine, Roberto Cotesta, Stas Babak, Andrea
26 * Taracchini
27 *
28 * \brief Functions for producing SEOBNRv4P(HM) waveforms for
29 * precessing binaries of spinning compact objects.
30 */
31
32// clang-format off
33#include <math.h>
34#include <complex.h>
35#include <gsl/gsl_deriv.h>
36
37#include <lal/LALSimInspiral.h>
38#include <lal/LALSimIMR.h>
39#include <lal/Date.h>
40#include <lal/TimeSeries.h>
41#include <lal/LALAdaptiveRungeKuttaIntegrator.h>
42#include <lal/SphericalHarmonics.h>
43#include <gsl/gsl_integration.h>
44#include <gsl/gsl_sf_gamma.h>
45#include <lal/Units.h>
46#include <lal/VectorOps.h>
47#include "LALSimIMREOBNRv2.h"
48#include "LALSimIMRSpinEOB.h"
52
53// clang-format on
54
55/* Include all the static function files we need */
71
72/* Begin OPTv3 */
73//#include "LALSimIMRSpinPrecEOBGSLOptimizedInterpolation.c"
75//#include "LALSimIMRSpinPrecEOBWfGen.c"
76/* End OPTv3 */
77
78#define debugOutput 0
79
80#ifdef __GNUC__
81#ifndef UNUSED
82#define UNUSED __attribute__ ((unused))
83#endif
84#else
85#define UNUSED
86#endif
87
88/* Maximum l allowed in the SEOB model -- input ModeArray will only be scanned
89 * up to this value of l */
90#define _SEOB_MODES_LMAX 5
91
92/* Version flag used in XLALSimIMREOBCalcSpinPrecFacWaveformCoefficients */
93#define v4Pwave 451
94
95/* Structure containing the approximant name and its number in LALSimInspiral.c
96 */
98 const char *name;
100};
101struct approximant v4P = {.name = "SEOBNRv4P", .number = 401};
102struct approximant v4PHM = {.name = "SEOBNRv4PHM", .number = 402};
103
104/* Number of dynamics variables stored by v4P */
105#define v4PdynamicsVariables 26
106
107#define FREE_ALL \
108 if (ICvalues != NULL) \
109 XLALDestroyREAL8Vector(ICvalues); \
110 if (dynamicsAdaS != NULL) \
111 XLALDestroyREAL8Array(dynamicsAdaS); \
112 if (seobdynamicsAdaS != NULL) \
113 SEOBdynamics_Destroy(seobdynamicsAdaS); \
114 if (seobvalues_tstartHiS != NULL) \
115 XLALDestroyREAL8Vector(seobvalues_tstartHiS); \
116 if (ICvaluesHiS != NULL) \
117 XLALDestroyREAL8Vector(ICvaluesHiS); \
118 if (dynamicsHiS != NULL) \
119 XLALDestroyREAL8Array(dynamicsHiS); \
120 if (chi1L_tPeakOmega != NULL) \
121 XLALDestroyREAL8Vector(chi1L_tPeakOmega); \
122 if (chi2L_tPeakOmega != NULL) \
123 XLALDestroyREAL8Vector(chi2L_tPeakOmega); \
124 if (seobdynamicsHiS != NULL) \
125 SEOBdynamics_Destroy(seobdynamicsHiS); \
126 if (seobvalues_tPeakOmega != NULL) \
127 XLALDestroyREAL8Vector(seobvalues_tPeakOmega); \
128 if (Jfinal != NULL) \
129 XLALDestroyREAL8Vector(Jfinal); \
130 if (listhPlm_HiS != NULL) \
131 SphHarmListCAmpPhaseSequence_Destroy(listhPlm_HiS); \
132 if (listhPlm_HiSRDpatch != NULL) \
133 SphHarmListCAmpPhaseSequence_Destroy(listhPlm_HiSRDpatch); \
134 if (listhPlm_AdaS != NULL) \
135 SphHarmListCAmpPhaseSequence_Destroy(listhPlm_AdaS); \
136 if (*tVecPmodes != NULL) \
137 XLALDestroyREAL8Vector(*tVecPmodes); \
138 if (seobdynamicsAdaSHiS != NULL) \
139 SEOBdynamics_Destroy(seobdynamicsAdaSHiS); \
140 if (listhPlm != NULL) \
141 SphHarmListCAmpPhaseSequence_Destroy(listhPlm); \
142 if (*hP22_amp != NULL) \
143 XLALDestroyREAL8Vector(*hP22_amp); \
144 if (*hP22_phase != NULL) \
145 XLALDestroyREAL8Vector(*hP22_phase); \
146 if (*hP21_amp != NULL) \
147 XLALDestroyREAL8Vector(*hP21_amp); \
148 if (*hP21_phase != NULL) \
149 XLALDestroyREAL8Vector(*hP21_phase); \
150 if (*alphaJ2P != NULL) \
151 XLALDestroyREAL8Vector(*alphaJ2P); \
152 if (*betaJ2P != NULL) \
153 XLALDestroyREAL8Vector(*betaJ2P); \
154 if (*gammaJ2P != NULL) \
155 XLALDestroyREAL8Vector(*gammaJ2P); \
156 if (*hJlm != NULL) \
157 XLALDestroySphHarmTimeSeries(*hJlm); \
158 if (*hIlm != NULL) \
159 XLALDestroySphHarmTimeSeries(*hIlm); \
160 if (hplusTS != NULL) \
161 XLALDestroyREAL8TimeSeries(hplusTS); \
162 if (hcrossTS != NULL) \
163 XLALDestroyREAL8TimeSeries(hcrossTS); \
164 if (*mergerParams != NULL) \
165 XLALDestroyREAL8Vector(*mergerParams); \
166 if (*seobdynamicsAdaSVector != NULL) \
167 XLALDestroyREAL8Vector(*seobdynamicsAdaSVector); \
168 if (*seobdynamicsHiSVector != NULL) \
169 XLALDestroyREAL8Vector(*seobdynamicsHiSVector); \
170 if (*seobdynamicsAdaSHiSVector != NULL) \
171 XLALDestroyREAL8Vector(*seobdynamicsAdaSHiSVector);
172#define PRINT_ALL_PARAMS \
173 do { \
174 XLALPrintError( \
175 "--approximant SEOBNRv4P --f-min %.16e --m1 %.16e --m2 %.16e " \
176 "--spin1x %.16e --spin1y %.16e --spin1z %.16e --spin2x %.16e " \
177 "--spin2y %.16e --spin2z %.16e --inclination %.16e --distance %.16e " \
178 "--phiRef %.16e --sample-rate %.16e\n", \
179 fMin, m1SI / LAL_MSUN_SI, m2SI / LAL_MSUN_SI, chi1x, chi1y, chi1z, \
180 chi2x, chi2y, chi2z, inc, r / (1e6 * LAL_PC_SI), phi, 1. / INdeltaT); \
181 } while (0);
182
183/* Compute the highest initial frequency (of the 22 mode):
184 * at which the code will generate a waveform. We choose an initial minimum
185 * separation of 10.5M as a compromise between reliability of initial conditions
186 * and length of the waveform. We use Newtonian Kepler's law. Refuse to
187 * generate waveforms shorter than that.
188 */
190 REAL8 *freqMinRad /**<< OUTPUT, lowest initial 22 mode frequency*/,
191 REAL8 mTotal /**<< Total mass in units of solar masses */) {
192 REAL8 mTScaled = mTotal * LAL_MTSUN_SI;
193 *freqMinRad = pow(10.5, -1.5) / (LAL_PI * mTScaled);
194 return XLAL_SUCCESS;
195}
196
197/* Implements the standard argmax function for an array of reals */
198UNUSED static UINT4 argmax(REAL8Vector *vec) {
199 REAL8 max = vec->data[0];
200 UINT4 idx_max = 0;
201 for (UINT4 i = 0; i < vec->length; i++) {
202 if (vec->data[i] > max) {
203 max = vec->data[i];
204 idx_max = i;
205 }
206 }
207 return idx_max;
208}
209
210/* Return a slice of the given vector, not including the higher index,
211i.e. parroting Python behaviour */
212UNUSED static REAL8Vector *get_slice(REAL8Vector *vec, UINT4 lo, UINT4 hi) {
213 UINT4 size = hi - lo;
214 REAL8Vector *slice = XLALCreateREAL8Vector(size);
215 for (UINT4 jj = 0; jj < size; jj++) {
216 slice->data[jj] = vec->data[lo + jj];
217 }
218 return slice;
219}
220
221/* Function to find robustly the peak of a quantity given as an array of
222samples. The idea is the scan the samples with a window [w_1,w_2] and find the
223local max in each window, where local max has to not lie at the boundaries of
224the window. One then keeps track of all the local maxes and picks the largest
225one. Finally, one compares this value to the global argmax. If there is a clean
226critical point which is also a global max then these 2 values have to agree */
227UNUSED static int XLALEOBFindRobustPeak(REAL8 *tPeakQuant, REAL8Vector *tVec,
228 REAL8Vector *quantVec,
229 UINT4 window_width) {
230 // We begin at the end and go backwards
231 UINT4 vlen = tVec->length;
232 UINT4 local_argmax = 0;
233 UINT4 idx_global = 0;
234 UINT4 lo, hi; // Bounds of local window
235 // Global argmax over the entire array
236 UINT4 global_arg_max = argmax(quantVec);
237 UNUSED REAL8 global_max = quantVec->data[global_arg_max];
238 REAL8Vector *sl = NULL;
239 REAL8 curr_max = 0;
240 for (UINT4 kk = vlen - window_width - 1; kk > window_width; kk--) {
241 lo = kk - window_width;
242 hi = kk + window_width +
243 1; // Slice function does not return the value at the end
244 sl = get_slice(quantVec, lo, hi);
245 local_argmax = argmax(sl);
246 if (sl->data[local_argmax] > sl->data[0] &&
247 sl->data[local_argmax] > sl->data[sl->length - 1]) {
248 // We have *a* local max, figure out it's global index
249 // Is the local argmax the largest local argmax so far?
250 if (sl->data[local_argmax] > curr_max) {
251 curr_max = sl->data[local_argmax];
252 idx_global = lo + local_argmax;
253 }
254 }
256 }
257 *tPeakQuant = 0;
258 // Conditions under which we pick the last point of the dynamics:
259 // i) we found no local maxes at all
260 // ii) the global arg max is larger than the largest of the local maxes
261 // by more than 2 % of the largest maxes value (ideally they should be equal)
262 // iii) the peak is so close to end that we can't interpolate below.
263
264 if (idx_global == 0 ||
265 ((quantVec->data[global_arg_max] - quantVec->data[idx_global]) /
266 quantVec->data[idx_global] >
267 0.1) ||
268 (idx_global > tVec->length - 4)) {
269 XLAL_PRINT_WARNING("Warning no local max found, using last point\n");
270 *tPeakQuant = tVec->data[tVec->length - 1];
271 return XLAL_SUCCESS;
272 }
273 // We have a well-behaved local max. Get the time more accurately.
274 // Now we need to interpolate and then set the derivative of the interpolant
275 // to 0. We solve this via bisection in an interval of 3 points to the left
276 // and right of the argmax.
277 gsl_spline *spline = NULL;
278 gsl_interp_accel *acc = NULL;
279 spline = gsl_spline_alloc(gsl_interp_cspline, quantVec->length);
280 acc = gsl_interp_accel_alloc();
281
282 REAL8 time1 = tVec->data[idx_global - 3];
283 REAL8 time2 = tVec->data[idx_global + 3];
284 REAL8 timePeak = 0;
285 REAL8 omegaDerivMid = 0;
286 gsl_spline_init(spline, tVec->data, quantVec->data, quantVec->length);
287 REAL8 omegaDeriv1 = gsl_spline_eval_deriv(spline, time1, acc);
288 do {
289 timePeak = (time1 + time2) / 2.;
290 omegaDerivMid = gsl_spline_eval_deriv(spline, timePeak, acc);
291
292 if (omegaDerivMid * omegaDeriv1 < 0.0) {
293 time2 = timePeak;
294 } else {
295 omegaDeriv1 = omegaDerivMid;
296 time1 = timePeak;
297 }
298 } while (time2 - time1 > 1.0e-8);
299 *tPeakQuant = timePeak;
300 gsl_spline_free(spline);
301 gsl_interp_accel_free(acc);
302 return XLAL_SUCCESS;
303}
304
305/* The stopping condition used for the high sampling part of SEOBNRv4P
306Will set the termination reason to 1 if terminates normally(i.e. 5 steps
307after peak of omega found). Will set it to -1 if something has become nan.
308*/
309UNUSED static int XLALEOBSpinPrecStopCondition_v4(double UNUSED t,
310 const double values[],
311 double dvalues[],
312 void UNUSED *funcParams) {
313 UINT4 counter;
314 INT4 i;
315 SpinEOBParams UNUSED *params = (SpinEOBParams *)funcParams;
316
317 REAL8 r2 = 0;
318 REAL8 p[3], r[3], pdotVec[3], rdotVec[3];
319 REAL8 omega, omega_xyz[3];
320
321 memcpy(r, values, 3 * sizeof(REAL8));
322 memcpy(p, values + 3, 3 * sizeof(REAL8));
323 memcpy(rdotVec, dvalues, 3 * sizeof(REAL8));
324 memcpy(pdotVec, dvalues + 3, 3 * sizeof(REAL8));
325
326 r2 = inner_product(r, r);
327 cross_product(values, dvalues, omega_xyz);
328 omega = sqrt(inner_product(omega_xyz, omega_xyz)) / r2;
329 counter = params->eobParams->omegaPeaked;
330 if (r2 < 36. && omega < params->eobParams->omega) {
331 params->eobParams->omegaPeaked = counter + 1;
332 }
333
334 if (params->eobParams->omegaPeaked == 5) {
335 return 1;
336 }
337 for (i = 0; i < 12; i++) {
338 if (isnan(dvalues[i]) || isnan(values[i])) {
339 params->termination_reason = -1;
340 return 1;
341 }
342 }
343 params->eobParams->omega = omega;
344 return GSL_SUCCESS;
345}
346
347/**
348 * Stopping conditions for dynamics integration for SEOBNRv4P
349 */
350UNUSED static int
351XLALEOBSpinPrecStopConditionBasedOnPR(double UNUSED t, const double values[],
352 double dvalues[],
353 void UNUSED *funcParams) {
354 int debugPK = 0;
355 int debugPKverbose = 0;
356 INT4 i;
357 SpinEOBParams UNUSED *params = (SpinEOBParams *)funcParams;
358
359 REAL8 r2, pDotr = 0;
360 REAL8 p[3], r[3], pdotVec[3], rdotVec[3];
361 REAL8 omega, omega_xyz[3], L[3], dLdt1[3], dLdt2[3];
362
363 memcpy(r, values, 3 * sizeof(REAL8));
364 memcpy(p, values + 3, 3 * sizeof(REAL8));
365 memcpy(rdotVec, dvalues, 3 * sizeof(REAL8));
366 memcpy(pdotVec, dvalues + 3, 3 * sizeof(REAL8));
367
368 r2 = inner_product(r, r);
369 cross_product(values, dvalues, omega_xyz);
370 omega = sqrt(inner_product(omega_xyz, omega_xyz)) / r2;
371 pDotr = inner_product(p, r) / sqrt(r2);
372 if (debugPK) {
373 XLAL_PRINT_INFO("XLALEOBSpinPrecStopConditionBasedOnPR:: r = %e %e\n",
374 sqrt(r2), omega);
375 }
376 if (debugPK) {
378 "XLALEOBSpinPrecStopConditionBasedOnPR:: values = %e %e %e %e %e %e\n",
379 values[6], values[7], values[8], values[9], values[10], values[11]);
380 }
381 if (debugPK) {
383 "XLALEOBSpinPrecStopConditionBasedOnPR:: dvalues = %e %e %e %e %e %e\n",
384 dvalues[6], dvalues[7], dvalues[8], dvalues[9], dvalues[10],
385 dvalues[11]);
386 }
387 REAL8 rdot;
388 // this is d(r)/dt obtained by differentiating r2 (see above)
389 rdot = inner_product(rdotVec, r) / sqrt(r2);
390 // This is d/dt(pDotr) see pDotr above.
391 double prDot = -inner_product(p, r) * rdot / r2 +
392 inner_product(pdotVec, r) / sqrt(r2) +
393 inner_product(rdotVec, p) / sqrt(r2);
394
395 cross_product(r, pdotVec, dLdt1);
396 cross_product(rdotVec, p, dLdt2);
397 cross_product(r, p, L);
398
399 /* ********************************************************** */
400 /* ******* Different termination conditions Follow ******** */
401 /* ********************************************************** */
402
403 /* Table of termination conditions
404
405 Value Reason
406 -1 Any of the derivatives are Nan
407 0 r < 8 and pDotr >= 0 (outspiraling)
408 1 r < 8 and rdot >= 0 (outspiraling)
409 2 r < 2 and prDot > 0 (dp_r/dt is growing)
410 3 r < 8 and |p_vec| > 10 (the momentum vector is large)
411 4 r < 8 and |p_vec| < 1e-10 (momentum vector is small)
412 5 r < 2 and omega has a another peak
413 6 r < 8 and omega < 0.04 or (r < 2. and omega < 0.14 and
414 omega has a peak) 7 r < 8 and omega > 1 (unphysical
415 omega) 8 r < 5 and any of |dp_i/dt| > 10 9 r < 8 and
416 pphi > 10
417 10 r < 3 and rdot increases
418 */
419
420 /* Terminate if any derivative is Nan */
421 for (i = 0; i < 12; i++) {
422 if (isnan(dvalues[i]) || isnan(values[i])) {
423 if (debugPK) {
424 XLAL_PRINT_INFO("\n isnan reached. r2 = %f\n", r2);
425 fflush(NULL);
426 }
427 XLALPrintError("XLAL Error - %s: nan reached at r2 = %f \n", __func__,
428 r2);
430 params->termination_reason = -1;
431 return 1;
432 }
433 }
434
435 /* ********************************************************** */
436 /* ******* Unphysical orbital conditions ******** */
437 /* ********************************************************** */
438
439 /* Terminate if p_r points outwards */
440 if (r2 < 16 && pDotr >= 0) {
441 if (debugPK) {
443 "\n Integration stopping, p_r pointing outwards -- out-spiraling!\n");
444 fflush(NULL);
445 }
446 params->termination_reason = 0;
447
448 return 1;
449 }
450
451 /* Terminate if rdot is >0 (OUTspiraling) for separation <4M */
452 if (r2 < 16 && rdot >= 0) {
453 if (debugPK) {
454 XLAL_PRINT_INFO("\n Integration stopping, dr/dt>0 -- out-spiraling!\n");
455 fflush(NULL);
456 }
457 params->termination_reason = 1;
458
459 return 1;
460 }
461
462 /* Terminate if dp_R/dt > 0, i.e. radial momentum is increasing for separation
463 * <2M */
464 if (r2 < 4. && prDot > 0.) {
465 if (debugPK) {
466 XLAL_PRINT_INFO("\n Integration stopping as prDot = %lf at r = %lf\n",
467 prDot, sqrt(r2));
468 fflush(NULL);
469 }
470 params->termination_reason = 2;
471
472 return 1;
473 }
474
475 if (r2 < 16. && (sqrt(values[3] * values[3] + values[4] * values[4] +
476 values[5] * values[5]) > 10.)) {
477 if (debugPK)
478 XLAL_PRINT_INFO("\n Integration stopping |pvec|> 10\n");
479 fflush(NULL);
480 params->termination_reason = 3;
481
482 return 1;
483 }
484
485 if (r2 < 16. && (sqrt(values[3] * values[3] + values[4] * values[4] +
486 values[5] * values[5]) < 1.e-10)) {
487 if (debugPK)
488 XLAL_PRINT_INFO("\n Integration stopping |pvec|<1e-10\n");
489 fflush(NULL);
490 params->termination_reason = 4;
491
492 return 1;
493 }
494
495 /* **************************************************************** */
496 /* Omega related */
497 /* **************************************************************** */
498 /* Terminate when omega reaches peak, and separation is < 4M */
499 if (r2 < 16. && omega < params->eobParams->omega)
500 params->eobParams->omegaPeaked = 1;
501
502 /* If omega has gone through a second extremum, break */
503 if (r2 < 4. && params->eobParams->omegaPeaked == 1 &&
504 omega > params->eobParams->omega) {
505 if (debugPK) {
507 "\n Integration stopping, omega reached second extremum\n");
508 fflush(NULL);
509 }
510 params->termination_reason = 5;
511
512 return 1;
513 }
514
515 /* If Momega did not evolve above 0.01 even though r < 4 or omega<0.14 for
516 * r<2, break */
517 if ((r2 < 16. && omega < 0.04) ||
518 (r2 < 4. && omega < 0.14 && params->eobParams->omegaPeaked == 1)) {
519 if (debugPK) {
520 XLAL_PRINT_INFO("\n Integration stopping for omega below threshold, "
521 "omega=%f at r = %f\n",
522 omega, sqrt(r2));
523 fflush(NULL);
524 }
525 params->termination_reason = 6;
526
527 return 1;
528 }
529
530 if (r2 < 16. && omega > 1.) {
531 if (debugPK) {
532 XLAL_PRINT_INFO("\n Integration stopping, omega>1 at r = %f\n", sqrt(r2));
533 fflush(NULL);
534 }
535 params->termination_reason = 7;
536
537 return 1;
538 }
539 params->eobParams->omega = omega;
540
541 /* **************************************************************** */
542 /* related to Numerical values of x/p/derivatives */
543 /* **************************************************************** */
544
545 /* If momentum derivatives are too large numerically, break */
546 if (r2 < 25 && (fabs(dvalues[3]) > 10 || fabs(dvalues[4]) > 10 ||
547 fabs(dvalues[5]) > 10)) {
548 if (debugPK) {
549 XLAL_PRINT_INFO("\n Integration stopping, dpdt > 10 -- too large!\n");
550 fflush(NULL);
551 }
552 params->termination_reason = 8;
553
554 return 1;
555 }
556
557 /* If p_\Phi is too large numerically, break */
558 if (r2 < 16. && values[5] > 10) {
559 if (debugPK) {
560 XLAL_PRINT_INFO("Integration stopping, Pphi > 10 now\n\n");
561 fflush(NULL);
562 }
563 params->termination_reason = 9;
564
565 return 1;
566 }
567 /* If rdot inclreases, break */
568 if (r2 < 9. && rdot > params->prev_dr) {
569 if (debugPK) {
570 XLAL_PRINT_INFO("\n Integration stopping, dr/dt increasing!\n");
571 fflush(NULL);
572 }
573 params->prev_dr = rdot;
574 params->termination_reason = 10;
575
576 return 1;
577 }
578 params->prev_dr = rdot;
579
580 /* **************************************************************** */
581 /* Last resort conditions */
582 /* **************************************************************** */
583
584 /* Very verbose output */
585 if (debugPKverbose && r2 < 16.) {
586 XLAL_PRINT_INFO("%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n", t,
587 values[0], values[1], values[2], values[3], values[4],
588 values[5], values[6], values[7], values[8], values[9],
589 values[10], values[11], values[12], values[13], omega);
590 }
591
592 return GSL_SUCCESS;
593}
594
595/**
596 * Stopping condition for the regular resolution SEOBNRv1/2 orbital evolution
597 * -- stop when reaching max orbital frequency in strong field.
598 * At each test,
599 * if omega starts to decrease, return 1 to stop evolution;
600 * if not, update omega with current value and return GSL_SUCCESS to continue
601 * evolution.
602 */
604 double UNUSED t, /**< UNUSED */
605 const double values[], /**< dynamical variable values */
606 double dvalues[], /**< dynamical variable time derivative values */
607 void *funcParams /**< physical parameters */
608) {
609 int debugPK = 0;
610 REAL8 omega, r;
611 SpinEOBParams *params = (SpinEOBParams *)funcParams;
612
613 r = values[0];
614 omega = dvalues[1];
615 if (debugPK) {
616 XLAL_PRINT_INFO("XLALEOBSpinPrecAlignedStopCondition:: r = %e\n", r);
617 }
618
619 if (r < 6. && omega < params->eobParams->omega) {
620 return 1;
621 }
622
623 params->eobParams->omega = omega;
624 return GSL_SUCCESS;
625}
626
627/**
628 * Stopping condition for the high resolution SEOBNRv4.
629 */
631 double UNUSED t, /**< UNUSED */
632 const double UNUSED values[], /**< dynamical variable values */
633 double dvalues[], /**< dynamical variable time derivative values */
634 void UNUSED *funcParams /**< physical parameters */
635) {
636
637 REAL8 omega, r;
638 UINT4 counter;
639 SpinEOBParams *params = (SpinEOBParams *)funcParams;
640 r = values[0];
641 omega = dvalues[1];
642 counter = params->eobParams->omegaPeaked;
643
644 if (r < 6. && omega < params->eobParams->omega) {
645
646 params->eobParams->omegaPeaked = counter + 1;
647 }
648 if (dvalues[2] >= 0. || params->eobParams->omegaPeaked == 5 ||
649 isnan(dvalues[3]) || isnan(dvalues[2]) || isnan(dvalues[1]) ||
650 isnan(dvalues[0])) {
651
652 return 1;
653 }
654 params->eobParams->omega = omega;
655 return GSL_SUCCESS;
656}
657
658/**
659 * ModeArray is a structure which allows to select the the co-precessing frame
660 * modes to include in the waveform. This function will create a structure with
661 * the default modes for every model
662 */
664 UINT4 PrecEOBversion) {
665
666 /* setup ModeArray */
667
668 if (PrecEOBversion == v4PHM.number) {
669 /* Adding all the modes of SEOBNRv4PHM
670 * i.e. [(2,2),(2,1),(3,3),(4,4),(5,5)]
671 the relative -m modes are added automatically*/
677 }
678 if (PrecEOBversion == v4P.number) {
679 /* Adding all the modes of SEOBNRv4P
680 * i.e. [(2,2),(2,1)]
681 the relative -m modes are added automatically*/
684 }
685
686 return XLAL_SUCCESS;
687}
688
689/**
690 * ModeArray is a structure which allows to select the the co-precessing frame
691 * modes to include in the waveform. This function check if the selected modes
692 * are available for a given model
693 */
694static INT4 XLALCheck_EOB_mode_array_structure(LALValue *ModeArray,
695 UINT4 PrecEOBversion) {
696 INT4 flagTrue = 0;
697 UINT4 modeL;
698 UINT4 modeM;
700 const UINT4 lmModes[5][2] = {{2, 2}, {2, 1}, {3, 3}, {4, 4}, {5, 5}};
701 if (PrecEOBversion == v4PHM.number) {
702 /*If one select SEOBNRv4PHM all the modes above are selected to check
703 */
704 nModes = 5;
705 } else {
706 /*If not only the modes 22 and 21 are selected to check
707 */
708 nModes = 2;
709 }
710 /* First check if the user is entering a mode with negative m */
711 /* This function only takes positive m and then selects automatically +- m */
712 for (UINT4 ELL = 2; ELL <= _SEOB_MODES_LMAX; ELL++) {
713 for (INT4 EMM = -ELL; EMM < 0; EMM++) {
714 if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ELL, EMM) == 1) {
715 XLALPrintError("Mode (%d,%d) has a negative m. \
716 In mode array you should specify (l,|m|). The code will automatically return +- m modes\n",
717 ELL, EMM);
718 return XLAL_FAILURE;
719 }
720 }
721 }
722
723 /*Loop over all the possible modes
724 *we only check +m modes, when one select (l,m) mode is actually
725 *selecting (l,|m|) mode
726 */
727 for (UINT4 ELL = 2; ELL <= _SEOB_MODES_LMAX; ELL++) {
728 for (UINT4 EMM = 0; EMM <= ELL; EMM++) {
729 if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ELL, EMM) == 1) {
730 for (UINT4 k = 0; k < nModes; k++) {
731 modeL = lmModes[k][0];
732 modeM = lmModes[k][1];
733 if ((modeL == ELL) && (modeM == EMM)) {
734 flagTrue = 1;
735 }
736 }
737 /*For each active mode check if is available for the selected model
738 */
739 if (flagTrue != 1) {
740 if (PrecEOBversion == v4PHM.number) {
741 XLALPrintError("Mode (%d,%d) is not available for the model %s.\n",
742 ELL, EMM, v4PHM.name);
743 return XLAL_FAILURE;
744 }
745 if (PrecEOBversion == v4P.number) {
746 XLALPrintError("Mode (%d,%d) is not available for the model %s.\n",
747 ELL, EMM, v4P.name);
748 return XLAL_FAILURE;
749 }
750 }
751 flagTrue = 0;
752 }
753 }
754 }
755
756 return XLAL_SUCCESS;
757}
758
759/**
760 * Standard interface for SEOBNRv4P waveform generator: calls
761 * XLALSimIMRSpinPrecEOBWaveformAll
762 */
764 REAL8TimeSeries **hplus, /**<< OUTPUT, +-polarization waveform */
765 REAL8TimeSeries **hcross, /**<< OUTPUT, x-polarization waveform */
766 const REAL8 phiC, /**<< coalescence orbital phase (rad) */
767 const REAL8 deltaT, /**<< sampling time step */
768 const REAL8 m1SI, /**<< mass-1 in SI unit (kg) */
769 const REAL8 m2SI, /**<< mass-2 in SI unit (kg) 8*/
770 const REAL8 fMin, /**<< starting frequency (Hz) */
771 const REAL8 r, /**<< luminosity distance in SI unit (m) */
772 const REAL8 inc, /**<< inclination angle */
773 const REAL8 INspin1[], /**<< spin1 */
774 const REAL8 INspin2[], /**<< spin2 */
775 UNUSED const UINT4
776 PrecEOBversion, /**<< Precessing EOB waveform generator model */
777 LALDict *LALParams /**<< Dictionary of additional wf parameters */
778) {
779
780 REAL8Vector *dyn_Low = NULL;
781 REAL8Vector *dyn_Hi = NULL;
782 REAL8Vector *dyn_all = NULL;
783 REAL8Vector *t_vec_modes = NULL;
784 REAL8Vector *hP22_amp = NULL;
785 REAL8Vector *hP22_phase = NULL;
786 REAL8Vector *hP21_amp = NULL;
787 REAL8Vector *hP21_phase = NULL;
788 REAL8Vector *hP33_amp = NULL;
789 REAL8Vector *hP33_phase = NULL;
790 REAL8Vector *hP44_amp = NULL;
791 REAL8Vector *hP44_phase = NULL;
792 REAL8Vector *hP55_amp = NULL;
793 REAL8Vector *hP55_phase = NULL;
794 REAL8Vector *alphaJ2P = NULL;
795 REAL8Vector *betaJ2P = NULL;
796 REAL8Vector *gammaJ2P = NULL;
797 REAL8Vector *AttachPars = NULL;
798
799 /** This time series contains harmonics in precessing (P) frame, no RD, for
800 * the end of the signal (high samling part)*/
801 SphHarmTimeSeries *hIlm = NULL;
802 /** This stores harmonics in J-frame, no RD, for the end of the signal (high
803 * sampling part) */
804 SphHarmTimeSeries *hJlm = NULL;
805
806 /* Import the set of modes requested by the user if available, if not
807 load the default modes */
808 LALValue *modearray = XLALSimInspiralWaveformParamsLookupModeArray(LALParams);
809 if (modearray == NULL) {
810 modearray = XLALSimInspiralCreateModeArray();
811 XLALSetup_EOB__std_mode_array_structure(modearray, PrecEOBversion);
812 }
813 /*Check that the modes chosen are available for the given model*/
814 if (XLALCheck_EOB_mode_array_structure(modearray, PrecEOBversion) ==
815 XLAL_FAILURE) {
816 XLALPrintError("Not available mode chosen.\n");
818 }
819
820 /* Set of SEOB flags */
821 LALDict *seobflags = XLALCreateDict();
822 /* Spin-aligned model v4 */
823 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_SpinAlignedEOBversion", 4);
824 /* Generate P-frame modes m<0 with the symmetry hP_l-m ~ (-1)^l hP_lm* */
825 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_SymmetrizehPlminusm", 1);
826 /* Use numerical or analytical derivatives of the Hamiltonian
827 Default is numerical with the flag 1*/
828 INT4 NumericalOrAnalyticalHamiltonianDerivative =
830 /* NumericalOrAnalyticalHamiltonianDerivative can only be 0 (analytical) or 1
831 * (numerical), let's check! */
832 if ((NumericalOrAnalyticalHamiltonianDerivative != 0) &&
833 (NumericalOrAnalyticalHamiltonianDerivative != 1)) {
834 XLALPrintError("XLAL Error - %s: Unknown value for the derivative of the "
835 "Hamiltonian flag. \nAt present only "
836 "1 (numerical derivative) or 0 (analytical derivative) are "
837 "available.\n",
838 __func__);
840 }
841 if (NumericalOrAnalyticalHamiltonianDerivative ==
843 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_HamiltonianDerivative",
845 } else {
846 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_HamiltonianDerivative",
847 NumericalOrAnalyticalHamiltonianDerivative);
848 }
849 /* Extension of Euler angles post-merger: simple precession around final J at
850 * a rate set by QNMs */
851 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_euler_extension",
853 /* Z-axis of the radiation frame L */
854 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_Zframe",
856 /* No debug output */
857 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_debug", 0);
858 // What max ell to use for the Nyquist check.
859 // Note that this has no effect at which waveform modes are actually being generated.
860 // For SEOBNRv4P we do not give the user a choice, the max ell is *always* 2
861 INT4 ellMaxForNyquistCheck = 2;
862 if (PrecEOBversion == v4PHM.number){
863 ellMaxForNyquistCheck =
865 }
866 XLALDictInsertINT4Value(seobflags,"ellMaxForNyquistCheck",ellMaxForNyquistCheck);
867 int ret = XLAL_SUCCESS;
869 hplus, hcross, &hIlm, &hJlm, &dyn_Low, &dyn_Hi, &dyn_all,
870 &t_vec_modes, &hP22_amp, &hP22_phase, &hP21_amp, &hP21_phase,
871 &hP33_amp, &hP33_phase, &hP44_amp, &hP44_phase, &hP55_amp,
872 &hP55_phase, &alphaJ2P, &betaJ2P, &gammaJ2P, &AttachPars, phiC,
873 deltaT, m1SI, m2SI, fMin, r, inc, INspin1[0], INspin1[1],
874 INspin1[2], INspin2[0], INspin2[1], INspin2[2], modearray,
875 seobflags),
876 ret);
877 /*
878 if (ret == XLAL_SUCCESS) {
879 if (*hplus == NULL || *hcross == NULL) {
880 XLALPrintError(
881 "Houston-2, we've got a problem SOS, SOS, SOS, the waveform "
882 "generator returns NULL!!!... m1 = %.18e, m2 = %.18e, fMin = %.18e, "
883 "inclination = %.18e, spin1 = {%.18e, %.18e, %.18e}, spin2 = "
884 "{%.18e, %.18e, %.18e} \n",
885 m1SI / LAL_MSUN_SI, m2SI / LAL_MSUN_SI, (double)fMin, (double)inc,
886 INspin1[0], INspin1[1], INspin1[2], INspin2[0], INspin2[1],
887 INspin2[2]);
888 XLAL_ERROR(XLAL_ENOMEM);
889 }
890 if ((*hplus)->data == NULL || (*hcross)->data == NULL) {
891 XLALPrintError(
892 "Houston-3, we've got a problem SOS, SOS, SOS, the waveform "
893 "generator returns NULL!!!... m1 = %.18e, m2 = %.18e, fMin = %.18e, "
894 "inclination = %.18e, spin1 = {%.18e, %.18e, %.18e}, spin2 = "
895 "{%.18e, %.18e, %.18e} \n",
896 m1SI / LAL_MSUN_SI, m2SI / LAL_MSUN_SI, (double)fMin, (double)inc,
897 INspin1[0], INspin1[1], INspin1[2], INspin2[0], INspin2[1],
898 INspin2[2]);
899 XLAL_ERROR(XLAL_ENOMEM);
900 }
901 }*/
902 if (modearray)
903 XLALDestroyValue(modearray);
904 if (seobflags)
905 XLALDestroyDict(seobflags);
906 if (dyn_Low)
907 XLALDestroyREAL8Vector(dyn_Low);
908 if (dyn_Hi)
910 if (dyn_all)
911 XLALDestroyREAL8Vector(dyn_all);
912
913 if (t_vec_modes)
914 XLALDestroyREAL8Vector(t_vec_modes);
915 if (hP22_amp)
916 XLALDestroyREAL8Vector(hP22_amp);
917 if (hP22_phase)
918 XLALDestroyREAL8Vector(hP22_phase);
919 if (hP21_amp)
920 XLALDestroyREAL8Vector(hP21_amp);
921 if (hP21_phase)
922 XLALDestroyREAL8Vector(hP21_phase);
923 if (hP33_amp)
924 XLALDestroyREAL8Vector(hP33_amp);
925 if (hP33_phase)
926 XLALDestroyREAL8Vector(hP33_phase);
927 if (hP44_amp)
928 XLALDestroyREAL8Vector(hP44_amp);
929 if (hP44_phase)
930 XLALDestroyREAL8Vector(hP44_phase);
931 if (hP55_amp)
932 XLALDestroyREAL8Vector(hP55_amp);
933 if (hP55_phase)
934 XLALDestroyREAL8Vector(hP55_phase);
935
936 if (alphaJ2P)
937 XLALDestroyREAL8Vector(alphaJ2P);
938 if (betaJ2P)
939 XLALDestroyREAL8Vector(betaJ2P);
940 if (gammaJ2P)
941 XLALDestroyREAL8Vector(gammaJ2P);
942 if (AttachPars)
943 XLALDestroyREAL8Vector(AttachPars);
944 if (hIlm)
946 if (hJlm)
948 if (ret != XLAL_SUCCESS) {
949 XLAL_ERROR(ret);
950 }
951 return ret;
952}
953/**
954 * This function returns the maximum ell in the mode array.
955 * Note that m<=0 modes are ignored and a warning given.
956 */
957static int
958SEOBGetLMaxInModeArray(LALValue *modearray, /**<< Input: ModeArray structure */
959 int lmax /**<< Input: maximum value of l to explore --
960 possible modes with l>lmax will be ignored */
961) {
962 /* Populate array */
963 INT4 lmax_array = 0;
964 for (INT4 l = 2; l <= lmax; l++) {
965 for (INT4 m = l; m >= -l; m--) {
966 if (m > 0) {
967 if (XLALSimInspiralModeArrayIsModeActive(modearray, l, m)) {
968 if (lmax_array < l)
969 lmax_array = l;
970 }
971 } else {
973 "XLAL Warning - %s: mode (l,m)=(%d,%d) present in mode array -- "
974 "in our conventions, we only consider m>0. Mode ignored for "
975 "counting.\n",
976 __func__, l, m);
977 }
978 }
979 }
980
981 return lmax_array;
982}
983
984/**
985 * Standard interface for SEOBNRv4P modes generator: calls
986 * XLALSimIMRSpinPrecEOBWaveformAll
987 */
989 const REAL8 deltaT, /**<< sampling time step */
990 const REAL8 m1SI, /**<< mass-1 in SI unit (kg) */
991 const REAL8 m2SI, /**<< mass-2 in SI unit (kg) 8*/
992 const REAL8 fMin, /**<< starting frequency (Hz) */
993 const REAL8 r, /**<< luminosity distance in SI unit (m) */
994 const REAL8 INspin1[], /**<< spin1 */
995 const REAL8 INspin2[], /**<< spin2 */
996 UNUSED const UINT4
997 PrecEOBversion, /**<< Precessing EOB waveform generator model */
998 LALDict *LALParams /**<< Dictionary of additional wf parameters */
999) {
1000
1001 REAL8Vector *dyn_Low = NULL;
1002 REAL8Vector *dyn_Hi = NULL;
1003 REAL8Vector *dyn_all = NULL;
1004 REAL8Vector *t_vec_modes = NULL;
1005 REAL8Vector *hP22_amp = NULL;
1006 REAL8Vector *hP22_phase = NULL;
1007 REAL8Vector *hP21_amp = NULL;
1008 REAL8Vector *hP21_phase = NULL;
1009 REAL8Vector *hP33_amp = NULL;
1010 REAL8Vector *hP33_phase = NULL;
1011 REAL8Vector *hP44_amp = NULL;
1012 REAL8Vector *hP44_phase = NULL;
1013 REAL8Vector *hP55_amp = NULL;
1014 REAL8Vector *hP55_phase = NULL;
1015 REAL8Vector *alphaJ2P = NULL;
1016 REAL8Vector *betaJ2P = NULL;
1017 REAL8Vector *gammaJ2P = NULL;
1018 REAL8Vector *AttachPars = NULL;
1019 REAL8TimeSeries *hplus = NULL;
1020 REAL8TimeSeries *hcross = NULL;
1021
1022 /** This time series contains harmonics in precessing (P) frame, no RD, for
1023 * the end of the signal (high samling part)*/
1024 SphHarmTimeSeries *hIlm = NULL;
1025 /** This stores harmonics in J-frame, no RD, for the end of the signal (high
1026 * sampling part) */
1027 SphHarmTimeSeries *hJlm = NULL;
1028
1029 /* Import the set of modes requested by the user if available, if not
1030 load the default modes */
1031 LALValue *modearray = XLALSimInspiralWaveformParamsLookupModeArray(LALParams);
1032 if (modearray == NULL) {
1033 modearray = XLALSimInspiralCreateModeArray();
1034 XLALSetup_EOB__std_mode_array_structure(modearray, PrecEOBversion);
1035 }
1036 /*Check that the modes chosen are available for the given model*/
1037 if (XLALCheck_EOB_mode_array_structure(modearray, PrecEOBversion) ==
1038 XLAL_FAILURE) {
1039 XLALPrintError("Not available mode chosen.\n");
1041 }
1042
1043 /* Set of SEOB flags */
1044 LALDict *seobflags = XLALCreateDict();
1045 /* Spin-aligned model v4 */
1046 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_SpinAlignedEOBversion", 4);
1047 /* Generate P-frame modes m<0 with the symmetry hP_l-m ~ (-1)^l hP_lm* */
1048 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_SymmetrizehPlminusm", 1);
1049 /* Use numerical or analytical derivatives of the Hamiltonian
1050 Default is numerical with the flag 1*/
1051 INT4 NumericalOrAnalyticalHamiltonianDerivative =
1053 /* NumericalOrAnalyticalHamiltonianDerivative can only be 0 (analytical) or 1
1054 * (numerical), let's check! */
1055 if ((NumericalOrAnalyticalHamiltonianDerivative != 0) &&
1056 (NumericalOrAnalyticalHamiltonianDerivative != 1)) {
1057 XLALPrintError("XLAL Error - %s: Unknown value for the derivative of the "
1058 "Hamiltonian flag. \nAt present only "
1059 "1 (numerical derivative) or 0 (analytical derivative) are "
1060 "available.\n",
1061 __func__);
1063 }
1064 if (NumericalOrAnalyticalHamiltonianDerivative ==
1066 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_HamiltonianDerivative",
1068 } else {
1069 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_HamiltonianDerivative",
1070 NumericalOrAnalyticalHamiltonianDerivative);
1071 }
1072 /* Extension of Euler angles post-merger: simple precession around final J at
1073 * a rate set by QNMs */
1074 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_euler_extension",
1076 /* Z-axis of the radiation frame L */
1077 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_Zframe",
1079 /* No debug output */
1080 XLALDictInsertINT4Value(seobflags, "SEOBNRv4P_debug", 0);
1081
1082 INT4 ellMaxForNyquistCheck = 2;
1083 if (PrecEOBversion == v4PHM.number){
1084 ellMaxForNyquistCheck =
1086 }
1087 XLALDictInsertINT4Value(seobflags,"ellMaxForNyquistCheck",ellMaxForNyquistCheck);
1088 int ret = XLAL_SUCCESS;
1090 &hplus, &hcross, &hIlm, &hJlm, &dyn_Low, &dyn_Hi, &dyn_all,
1091 &t_vec_modes, &hP22_amp, &hP22_phase, &hP21_amp, &hP21_phase,
1092 &hP33_amp, &hP33_phase, &hP44_amp, &hP44_phase, &hP55_amp,
1093 &hP55_phase, &alphaJ2P, &betaJ2P, &gammaJ2P, &AttachPars, 0.,
1094 deltaT, m1SI, m2SI, fMin, r, 0., INspin1[0], INspin1[1],
1095 INspin1[2], INspin2[0], INspin2[1], INspin2[2], modearray,
1096 seobflags),
1097 ret);
1098 if (ret != XLAL_SUCCESS)
1099 {
1100 // We have to clean up right here
1101 if (modearray)
1102 XLALDestroyValue(modearray);
1103 if (seobflags)
1104 XLALDestroyDict(seobflags);
1105 if (dyn_Low)
1106 XLALDestroyREAL8Vector(dyn_Low);
1107 if (dyn_Hi)
1108 XLALDestroyREAL8Vector(dyn_Hi);
1109 if (dyn_all)
1110 XLALDestroyREAL8Vector(dyn_all);
1111
1112 if (t_vec_modes)
1113 XLALDestroyREAL8Vector(t_vec_modes);
1114 if (hP22_amp)
1115 XLALDestroyREAL8Vector(hP22_amp);
1116 if (hP22_phase)
1117 XLALDestroyREAL8Vector(hP22_phase);
1118 if (hP21_amp)
1119 XLALDestroyREAL8Vector(hP21_amp);
1120 if (hP21_phase)
1121 XLALDestroyREAL8Vector(hP21_phase);
1122 if (hP33_amp)
1123 XLALDestroyREAL8Vector(hP33_amp);
1124 if (hP33_phase)
1125 XLALDestroyREAL8Vector(hP33_phase);
1126 if (hP44_amp)
1127 XLALDestroyREAL8Vector(hP44_amp);
1128 if (hP44_phase)
1129 XLALDestroyREAL8Vector(hP44_phase);
1130 if (hP55_amp)
1131 XLALDestroyREAL8Vector(hP55_amp);
1132 if (hP55_phase)
1133 XLALDestroyREAL8Vector(hP55_phase);
1134
1135 if (alphaJ2P)
1136 XLALDestroyREAL8Vector(alphaJ2P);
1137 if (betaJ2P)
1138 XLALDestroyREAL8Vector(betaJ2P);
1139 if (gammaJ2P)
1140 XLALDestroyREAL8Vector(gammaJ2P);
1141 if (AttachPars)
1142 XLALDestroyREAL8Vector(AttachPars);
1143 if (hJlm)
1145 // Fail correctly
1146 XLAL_ERROR_NULL(ret);
1147 }
1148 /* Here we multiply the appropriate factor to the modes to convert them in
1149 * dimensional units */
1150 REAL8 m1 = m1SI / LAL_MSUN_SI;
1151 REAL8 m2 = m2SI / LAL_MSUN_SI;
1152 REAL8 mTotal = m1 + m2;
1153 REAL8 mTScaled = mTotal * LAL_MTSUN_SI;
1154
1155 /* Initialize amplitude factor */
1156 REAL8 amp0 = mTotal * LAL_MRSUN_SI / r;
1157 char mode_string[32];
1158
1159 INT4 modes_lmax = SEOBGetLMaxInModeArray(modearray, _SEOB_MODES_LMAX);
1160 UINT4 retLen = hIlm->tdata->length;
1161 SphHarmTimeSeries *hIlm_dimfull = NULL;
1163 XLALGPSAdd(&tGPS, -mTScaled * AttachPars->data[2]);
1164
1165 for (INT4 l = 2; l <= modes_lmax; l++) {
1166 for (INT4 m = -l; m <= l; m++) {
1167
1168 /* Get dimensionless mode hIlm */
1169 COMPLEX16TimeSeries *mode_dimless =
1171
1172 /* Time series for dimensionful mode */
1174 mode_string, &tGPS, 0., deltaT, &lalStrainUnit, retLen);
1175 memset(mode_dimfull->data->data, 0, retLen * sizeof(COMPLEX16));
1176
1177 for (UINT4 i = 0; i < mode_dimless->data->length; i++) {
1178 /* The factor -1 here is to rotate EOB modes in LAL convention
1179 * see https://dcc.ligo.org/LIGO-G1900275 */
1180 mode_dimfull->data->data[i] =
1181 -1. * (COMPLEX16)amp0 * mode_dimless->data->data[i];
1182 }
1183 hIlm_dimfull =
1184 XLALSphHarmTimeSeriesAddMode(hIlm_dimfull, mode_dimfull, l, m);
1185 XLALDestroyCOMPLEX16TimeSeries(mode_dimless);
1186 XLALDestroyCOMPLEX16TimeSeries(mode_dimfull);
1187 }
1188 }
1189
1190 /*
1191 if (ret == XLAL_SUCCESS) {
1192 if (*hplus == NULL || *hcross == NULL) {
1193 XLALPrintError(
1194 "Houston-2, we've got a problem SOS, SOS, SOS, the waveform "
1195 "generator returns NULL!!!... m1 = %.18e, m2 = %.18e, fMin = %.18e, "
1196 "inclination = %.18e, spin1 = {%.18e, %.18e, %.18e}, spin2 = "
1197 "{%.18e, %.18e, %.18e} \n",
1198 m1SI / LAL_MSUN_SI, m2SI / LAL_MSUN_SI, (double)fMin, (double)inc,
1199 INspin1[0], INspin1[1], INspin1[2], INspin2[0], INspin2[1],
1200 INspin2[2]);
1201 XLAL_ERROR(XLAL_ENOMEM);
1202 }
1203 if ((*hplus)->data == NULL || (*hcross)->data == NULL) {
1204 XLALPrintError(
1205 "Houston-3, we've got a problem SOS, SOS, SOS, the waveform "
1206 "generator returns NULL!!!... m1 = %.18e, m2 = %.18e, fMin = %.18e, "
1207 "inclination = %.18e, spin1 = {%.18e, %.18e, %.18e}, spin2 = "
1208 "{%.18e, %.18e, %.18e} \n",
1209 m1SI / LAL_MSUN_SI, m2SI / LAL_MSUN_SI, (double)fMin, (double)inc,
1210 INspin1[0], INspin1[1], INspin1[2], INspin2[0], INspin2[1],
1211 INspin2[2]);
1212 XLAL_ERROR(XLAL_ENOMEM);
1213 }
1214 }*/
1215
1216 if (modearray)
1217 XLALDestroyValue(modearray);
1218 if (seobflags)
1219 XLALDestroyDict(seobflags);
1220 if (dyn_Low)
1221 XLALDestroyREAL8Vector(dyn_Low);
1222 if (dyn_Hi)
1223 XLALDestroyREAL8Vector(dyn_Hi);
1224 if (dyn_all)
1225 XLALDestroyREAL8Vector(dyn_all);
1226
1227 if (t_vec_modes)
1228 XLALDestroyREAL8Vector(t_vec_modes);
1229 if (hP22_amp)
1230 XLALDestroyREAL8Vector(hP22_amp);
1231 if (hP22_phase)
1232 XLALDestroyREAL8Vector(hP22_phase);
1233 if (hP21_amp)
1234 XLALDestroyREAL8Vector(hP21_amp);
1235 if (hP21_phase)
1236 XLALDestroyREAL8Vector(hP21_phase);
1237 if (hP33_amp)
1238 XLALDestroyREAL8Vector(hP33_amp);
1239 if (hP33_phase)
1240 XLALDestroyREAL8Vector(hP33_phase);
1241 if (hP44_amp)
1242 XLALDestroyREAL8Vector(hP44_amp);
1243 if (hP44_phase)
1244 XLALDestroyREAL8Vector(hP44_phase);
1245 if (hP55_amp)
1246 XLALDestroyREAL8Vector(hP55_amp);
1247 if (hP55_phase)
1248 XLALDestroyREAL8Vector(hP55_phase);
1249
1250 if (alphaJ2P)
1251 XLALDestroyREAL8Vector(alphaJ2P);
1252 if (betaJ2P)
1253 XLALDestroyREAL8Vector(betaJ2P);
1254 if (gammaJ2P)
1255 XLALDestroyREAL8Vector(gammaJ2P);
1256 if (AttachPars)
1257 XLALDestroyREAL8Vector(AttachPars);
1258 if (hJlm)
1260
1261 return hIlm_dimfull;
1262}
1263
1265 CAmpPhaseSequence **campphase, /* Double pointer to campphase sequence */
1266 int size /* Size of data */
1267) {
1268 /* Check input pointer */
1269 if (!campphase) {
1270 XLALPrintError("XLAL Error - %s: input double pointer is NULL.\n",
1271 __func__);
1273 }
1274 if (*campphase) {
1275 XLALPrintError("XLAL Error - %s: input pointer is not NULL.\n", __func__);
1277 }
1278
1279 /* Allocate structure */
1280 *campphase = XLALMalloc(sizeof(CAmpPhaseSequence));
1281
1282 /* Allocate storage for data and initialize to 0 */
1283 if (!((*campphase)->xdata = XLALCreateREAL8Vector(size))) {
1284 XLALPrintError("XLAL Error - %s: failed to create REAL8Vector xdata.\n",
1285 __func__);
1287 }
1288 if (!((*campphase)->camp_real = XLALCreateREAL8Vector(size))) {
1289 XLALPrintError("XLAL Error - %s: failed to create REAL8Vector camp_real.\n",
1290 __func__);
1292 }
1293 if (!((*campphase)->camp_imag = XLALCreateREAL8Vector(size))) {
1294 XLALPrintError("XLAL Error - %s: failed to create REAL8Vector camp_imag.\n",
1295 __func__);
1297 }
1298 if (!((*campphase)->phase = XLALCreateREAL8Vector(size))) {
1299 XLALPrintError("XLAL Error - %s: failed to create REAL8Vector phase.\n",
1300 __func__);
1302 }
1303 memset((*campphase)->xdata->data, 0, size * sizeof(REAL8));
1304 memset((*campphase)->camp_real->data, 0, size * sizeof(REAL8));
1305 memset((*campphase)->camp_imag->data, 0, size * sizeof(REAL8));
1306 memset((*campphase)->phase->data, 0, size * sizeof(REAL8));
1307
1308 return XLAL_SUCCESS;
1309}
1310
1312 CAmpPhaseSequence *campphase /* Pointer to structure to be destroyed */
1313) {
1314 /* Raise an error if NULL pointer */
1315 if (!campphase) {
1316 XLALPrintError("XLAL Error - %s: data is a NULL pointer.\n", __func__);
1318 }
1319 if (campphase->xdata)
1320 XLALDestroyREAL8Vector(campphase->xdata);
1321 if (campphase->camp_real)
1323 if (campphase->camp_imag)
1325 if (campphase->phase)
1326 XLALDestroyREAL8Vector(campphase->phase);
1327 XLALFree(campphase);
1328
1329 return XLAL_SUCCESS;
1330}
1331
1333 SphHarmListEOBNonQCCoeffs *list /* Pointer to list to be destroyed */
1334) {
1336 while ((pop = list)) {
1337 if (pop->nqcCoeffs) { /* Internal EOBNonQCCoeffs is freed */
1338 XLALFree(pop->nqcCoeffs);
1339 }
1340 /* Go to next element */
1341 list = pop->next;
1342 /* Free structure itself */
1343 XLALFree(pop);
1344 }
1345
1346 return XLAL_SUCCESS;
1347}
1348
1349/* Note that we do NOT COPY the input mode */
1350/* The added data is simply passed by pointer */
1351/* The added data can therefore e.g. be destroyed if the list is destroyed */
1354 *list_prepended, /* List structure to prepend to */
1355 EOBNonQCCoeffs *nqcCoeffs, /* Mode data to be added */
1356 UINT4 l, /*< Mode number l */
1357 INT4 m /*< Mode number m */
1358) {
1360 /* Check if the node with this mode already exists */
1361 list = *list_prepended;
1362 while (list) {
1363 if (l == list->l && m == list->m) {
1364 break;
1365 }
1366 list = list->next;
1367 }
1368 if (list) { /* We don't allow for the case where the mode already exists in
1369 the list*/
1370 XLALPrintError("XLAL Error - %s: tried to add an already existing mode to "
1371 "a SphHarmListCAmpPhaseSequence.\n",
1372 __func__);
1374 } else {
1375 list = XLALMalloc(sizeof(SphHarmListEOBNonQCCoeffs));
1376 }
1377 list->l = l;
1378 list->m = m;
1379 if (nqcCoeffs) {
1380 list->nqcCoeffs = nqcCoeffs;
1381 } else {
1382 list->nqcCoeffs = NULL;
1383 }
1384 if (*list_prepended) {
1385 list->next = *list_prepended;
1386 } else {
1387 list->next = NULL;
1388 }
1389 *list_prepended = list;
1390
1391 return XLAL_SUCCESS;
1392}
1393
1396 *list, /* List structure to get a particular mode from */
1397 UINT4 l, /*< Mode number l */
1398 INT4 m /*< Mode number m */
1399) {
1400 if (!list)
1401 return NULL;
1402
1403 SphHarmListEOBNonQCCoeffs *itr = list;
1404 while (itr->l != l || itr->m != m) {
1405 itr = itr->next;
1406 if (!itr)
1407 return NULL;
1408 }
1409 /* Return a pointer to a SphHarmListCAmpPhaseSequence */
1410 return itr;
1411}
1412
1414 SphHarmListCAmpPhaseSequence *list /* Pointer to list to be destroyed */
1415) {
1417 while ((pop = list)) {
1418 if (pop->campphase) { /* Internal CAmpPhaseSequence is freed */
1421 "XLAL Error - %s: failure in CAmpPhaseSequence_Destroy.\n",
1422 __func__);
1424 }
1425 }
1426 /* Go to next element */
1427 list = pop->next;
1428 /* Free structure itself */
1429 XLALFree(pop);
1430 }
1431
1432 return XLAL_SUCCESS;
1433}
1434
1435/* Note that we do NOT COPY the input mode */
1436/* The added data is simply passed by pointer */
1437/* The added data can therefore e.g. be destroyed if the list is destroyed */
1440 *list_prepended, /* List structure to prepend to */
1441 CAmpPhaseSequence *campphase, /* Mode data to be added */
1442 UINT4 l, /*< Mode number l */
1443 INT4 m /*< Mode number m */
1444) {
1446 /* Check if the node with this mode already exists */
1447 list = *list_prepended;
1448 while (list) {
1449 if (l == list->l && m == list->m) {
1450 break;
1451 }
1452 list = list->next;
1453 }
1454 if (list) { /* We don't allow for the case where the mode already exists in
1455 the list*/
1456 XLALPrintError("XLAL Error - %s: tried to add an already existing mode to "
1457 "a SphHarmListCAmpPhaseSequence.\n",
1458 __func__);
1460 } else {
1462 }
1463 list->l = l;
1464 list->m = m;
1465 if (campphase) {
1466 list->campphase = campphase;
1467 } else {
1468 list->campphase = NULL;
1469 }
1470 if (*list_prepended) {
1471 list->next = *list_prepended;
1472 } else {
1473 list->next = NULL;
1474 }
1475 *list_prepended = list;
1476
1477 return XLAL_SUCCESS;
1478}
1479
1482 *list, /* List structure to get a particular mode from */
1483 UINT4 l, /*< Mode number l */
1484 INT4 m /*< Mode number m */
1485) {
1486 if (!list)
1487 return NULL;
1488
1489 SphHarmListCAmpPhaseSequence *itr = list;
1490 while (itr->l != l || itr->m != m) {
1491 itr = itr->next;
1492 if (!itr)
1493 return NULL;
1494 }
1495 /* Return a pointer to a SphHarmListCAmpPhaseSequence */
1496 return itr;
1497}
1498
1499static int SEOBdynamics_Destroy(SEOBdynamics *seobdynamics) {
1500 XLALDestroyREAL8Array(seobdynamics->array);
1501 XLALFree(seobdynamics);
1502
1503 return XLAL_SUCCESS;
1504}
1505
1507 SEOBdynamics **seobdynamics, /**<< Output: pointer to the SOBdynamics */
1508 UINT4 retLen /**<< Input: length of dynamics data to allocate */
1509) {
1510 /* Check that the input double pointer is not NULL */
1511 if (!seobdynamics) {
1512 XLALPrintError("XLAL Error - %s: seobdynamics is a NULL double pointer.\n",
1513 __func__);
1515 }
1516
1517 /* If double pointer points to an existing struct, destroy it first */
1518 if ((*seobdynamics)) {
1519 SEOBdynamics_Destroy(*seobdynamics);
1520 }
1521
1522 /* Allocate struct */
1523 if (!(*seobdynamics = XLALMalloc(sizeof(SEOBdynamics)))) {
1524 XLALPrintError("XLAL Error - %s: failed to allocate struct SEOBdynamics.\n",
1525 __func__);
1527 }
1528
1529 /* Length of data */
1530 (*seobdynamics)->length = retLen;
1531
1532 /* Allocate array for the data */
1533 if (!((*seobdynamics)->array =
1536 "XLAL Error - %s: failed to allocate REAL8Array seobdynamics->array.\n",
1537 __func__);
1539 }
1540
1541 /* Set array pointers corresponding to the data vectors (successive vectors of
1542 * length retLen in the 1D array data) */
1543 (*seobdynamics)->tVec = (*seobdynamics)->array->data;
1544 (*seobdynamics)->posVecx = (*seobdynamics)->array->data + 1 * retLen;
1545 (*seobdynamics)->posVecy = (*seobdynamics)->array->data + 2 * retLen;
1546 (*seobdynamics)->posVecz = (*seobdynamics)->array->data + 3 * retLen;
1547 (*seobdynamics)->momVecx = (*seobdynamics)->array->data + 4 * retLen;
1548 (*seobdynamics)->momVecy = (*seobdynamics)->array->data + 5 * retLen;
1549 (*seobdynamics)->momVecz = (*seobdynamics)->array->data + 6 * retLen;
1550 (*seobdynamics)->s1Vecx = (*seobdynamics)->array->data + 7 * retLen;
1551 (*seobdynamics)->s1Vecy = (*seobdynamics)->array->data + 8 * retLen;
1552 (*seobdynamics)->s1Vecz = (*seobdynamics)->array->data + 9 * retLen;
1553 (*seobdynamics)->s2Vecx = (*seobdynamics)->array->data + 10 * retLen;
1554 (*seobdynamics)->s2Vecy = (*seobdynamics)->array->data + 11 * retLen;
1555 (*seobdynamics)->s2Vecz = (*seobdynamics)->array->data + 12 * retLen;
1556 (*seobdynamics)->phiDMod = (*seobdynamics)->array->data + 13 * retLen;
1557 (*seobdynamics)->phiMod = (*seobdynamics)->array->data + 14 * retLen;
1558 (*seobdynamics)->velVecx = (*seobdynamics)->array->data + 15 * retLen;
1559 (*seobdynamics)->velVecy = (*seobdynamics)->array->data + 16 * retLen;
1560 (*seobdynamics)->velVecz = (*seobdynamics)->array->data + 17 * retLen;
1561 (*seobdynamics)->polarrVec = (*seobdynamics)->array->data + 18 * retLen;
1562 (*seobdynamics)->polarphiVec = (*seobdynamics)->array->data + 19 * retLen;
1563 (*seobdynamics)->polarprVec = (*seobdynamics)->array->data + 20 * retLen;
1564 (*seobdynamics)->polarpphiVec = (*seobdynamics)->array->data + 21 * retLen;
1565 (*seobdynamics)->omegaVec = (*seobdynamics)->array->data + 22 * retLen;
1566 (*seobdynamics)->s1dotZVec = (*seobdynamics)->array->data + 23 * retLen;
1567 (*seobdynamics)->s2dotZVec = (*seobdynamics)->array->data + 24 * retLen;
1568 (*seobdynamics)->hamVec = (*seobdynamics)->array->data + 25 * retLen;
1569
1570 return XLAL_SUCCESS;
1571}
1572
1573/**
1574 * Functions to calculate symmetrized and antisymmetrized combinations
1575 * of the dimensionless spins projected on the radiation frame Z-axis (L or LN)
1576 */
1577static REAL8 SEOBCalculateChiS(REAL8 chi1dotZ, REAL8 chi2dotZ) {
1578 return 0.5 * (chi1dotZ + chi2dotZ);
1579}
1580static REAL8 SEOBCalculateChiA(REAL8 chi1dotZ, REAL8 chi2dotZ) {
1581 return 0.5 * (chi1dotZ - chi2dotZ);
1582}
1583
1584/**
1585 * Function to calculate tplspin
1586 * See discussion below Eq. 4 of PRD 89, 061502(R) [arXiv:1311.2544] (2014)
1587 */
1588static REAL8 SEOBCalculatetplspin(REAL8 m1, REAL8 m2, REAL8 eta, REAL8 chi1dotZ,
1589 REAL8 chi2dotZ, INT4 SpinAlignedEOBversion) {
1590 REAL8 chiS, chiA, tplspin;
1591 chiS = SEOBCalculateChiS(chi1dotZ, chi2dotZ);
1592 chiA = SEOBCalculateChiA(chi1dotZ, chi2dotZ);
1593
1594 switch (SpinAlignedEOBversion) {
1595 case 1:
1596 /* See below Eq. 17 of PRD 86, 024011 (2012) [arXiv:1202.0790] */
1597 tplspin = 0.0;
1598 break;
1599 case 2:
1600 case 4:
1601 /* See below Eq. 4 of PRD 89, 061502(R) (2014) [arXiv:1311.2544] */
1602 tplspin = (1. - 2. * eta) * chiS + (m1 - m2) / (m1 + m2) * chiA;
1603 break;
1604 default:
1605 XLALPrintError("XLAL Error - %s: Unknown SEOBNR version!\nAt present only "
1606 "v1, v2 and v4 are available.\n",
1607 __func__);
1609 break;
1610 }
1611 return tplspin;
1612}
1613
1614/**
1615 * Function to calculate normalized spin of the deformed-Kerr background in
1616 * SEOBNRv1. Eq. 5.2 of Barausse and Buonanno PRD 81, 084024 (2010) [arXiv:0912.3517].
1617 * Identical to XLALSimIMRSpinEOBCalculateSigmaKerr, except that the input spins are in
1618 * units of mTotal^2
1619 */
1621 REAL8Vector *sigmaKerr, /**<< OUTPUT, normalized (to total mass) spin of
1622 deformed-Kerr */
1623 REAL8Vector *s1, /**<< spin vector 1, in units of mTotal^2 */
1624 REAL8Vector *s2 /**<< spin vector 2, in units of mTotal^2 */
1625) {
1626 for (UINT4 i = 0; i < 3; i++) {
1627 sigmaKerr->data[i] = (s1->data[i] + s2->data[i]);
1628 }
1629 return XLAL_SUCCESS;
1630}
1631
1632/**
1633 * Function to calculate normalized spin of the test particle in SEOBNRv1.
1634 * Eq. 5.3 of Barausse and Buonanno PRD 81, 084024 (2010) [arXiv:0912.3517].
1635 * Identical to XLALSimIMRSpinEOBCalculateSigmaStar, except that the input spins
1636 * are in units of mTotal^2
1637 */
1639 REAL8Vector *sigmaStar, /**<< OUTPUT, normalized (to total mass) spin of
1640 test particle */
1641 REAL8 mass1, /**<< mass 1 */
1642 REAL8 mass2, /**<< mass 2 */
1643 REAL8Vector *s1, /**<< spin vector 1, in units of mTotal^2 */
1644 REAL8Vector *s2 /**<< spin vector 2, in units of mTotal^2 */
1645) {
1646 for (UINT4 i = 0; i < 3; i++) {
1647 sigmaStar->data[i] =
1648 (mass2 / mass1 * s1->data[i] + mass1 / mass2 * s2->data[i]);
1649 }
1650 return XLAL_SUCCESS;
1651}
1652
1653/**
1654 * This function computes quantities (polardynamics, omega, s1dotZ, s2dotZ,
1655 * hamiltonian) derived from the dynamics as output by the integrator, and
1656 * returns a SEOBdynamics struct. Two choices for Z: L or LN. Note: this
1657 * function also applies when the spins are almost aligned and v4 is used.
1658 */
1660 SEOBdynamics **seobdynamics, /**<< Output, double pointer to SEOBdynamics
1661 struct. If points to an existing struct, the
1662 latter will be destroyed */
1663 REAL8Array *dynamics, /**<< Input, array containing the dynamics as output
1664 by the integrator */
1665 UINT4 retLen, /**<< Input, length of the dynamics */
1666 SpinEOBParams *seobParams, /**<< SEOB parameters */
1668 flagHamiltonianDerivative, /**<< flag to choose between numerical and
1669 analytical Hamiltonian derivatives */
1671 flagZframe /**<< flag to choose Z direction of the frame, LN or L */
1672) {
1673
1674 /* Create structure SEOBdynamics */
1675 SEOBdynamics_Init(seobdynamics, retLen);
1676 SEOBdynamics *seobdyn = *seobdynamics;
1677
1678 /* Local variables */
1679 REAL8 rvec[3] = {0, 0, 0};
1680 REAL8 pvec[3] = {0, 0, 0};
1681 REAL8 spin1vec[3] = {0, 0, 0}; /* in units of mTotal^2 */
1682 REAL8 spin2vec[3] = {0, 0, 0}; /* in units of mTotal^2 */
1683 REAL8 rdotvec[3] = {0, 0, 0};
1684 REAL8 rcrossrdot[3] = {0, 0, 0};
1685 REAL8 rcrossp[3] = {0, 0, 0};
1686 REAL8 LNhat[3] = {0, 0, 0};
1687 REAL8 Lhat[3] = {0, 0, 0};
1688 REAL8 polarr, polarphi, polarpr, polarpphi, omega, s1dotZ, s2dotZ, ham;
1689
1690 /* Allocate temporary vectors values, dvalues */
1691 REAL8Vector *values = NULL;
1692 REAL8Vector *dvalues = NULL;
1693 if (!(values = XLALCreateREAL8Vector(14)) ||
1694 !(dvalues = XLALCreateREAL8Vector(14))) {
1696 "XLAL Error - %s: failed to allocate REAL8Vector values, dvalues.\n",
1697 __func__);
1699 }
1700 memset(values->data, 0, (values->length) * sizeof(REAL8));
1701 memset(dvalues->data, 0, (dvalues->length) * sizeof(REAL8));
1702
1703 /* Masses and coeffs from SpinEOBParams */
1704 REAL8 m1 = seobParams->eobParams->m1;
1705 REAL8 m2 = seobParams->eobParams->m2;
1706 REAL8 eta = seobParams->eobParams->eta;
1707 SpinEOBHCoeffs *seobCoeffs = seobParams->seobCoeffs;
1708
1709 /* Copying directly the dynamics data in seobdynamics - 15 vectors of length
1710 * retLen */
1711 memcpy(seobdyn->array->data, dynamics->data, 15 * retLen * sizeof(REAL8));
1712
1713 /* We will need a vector structure for sigmaKerr and sigmaStar */
1714 REAL8Vector *sigmaStar = NULL, *sigmaKerr = NULL;
1715 sigmaStar = XLALCreateREAL8Vector(3);
1716 sigmaKerr = XLALCreateREAL8Vector(3);
1717 memset(sigmaStar->data, 0, 3 * sizeof(REAL8));
1718 memset(sigmaKerr->data, 0, 3 * sizeof(REAL8));
1719
1720 /* Loop to compute the derived quantities from the dynamics */
1721 UINT4 i, j;
1722 for (i = 0; i < retLen; i++) {
1723
1724 /* Copy dynamics values in the temporary vector values -- time excepted */
1725 for (j = 0; j < 14; j++) {
1726 values->data[j] = dynamics->data[i + (j + 1) * retLen];
1727 }
1728
1729 /* Computing velocity from Hamiltonian derivatives */
1730 if (flagHamiltonianDerivative ==
1732 if (XLALSpinPrecHcapRvecDerivative_exact(0, values->data, dvalues->data,
1733 (void *)seobParams) ==
1734 XLAL_FAILURE) {
1735 XLALPrintError("XLAL Error - %s: failure in "
1736 "XLALSpinPrecHcapRvecDerivative_exact.\n",
1737 __func__);
1739 }
1740 } else if (flagHamiltonianDerivative ==
1742 if (XLALSpinPrecHcapRvecDerivative(0, values->data, dvalues->data,
1743 (void *)seobParams) == XLAL_FAILURE) {
1745 "XLAL Error - %s: failure in XLALSpinPrecHcapRvecDerivative.\n",
1746 __func__);
1748 }
1749 } else {
1751 "XLAL Error - %s: flagHamiltonianDerivative not recognized.\n",
1752 __func__);
1754 }
1755
1756 /* Compute omega and LNhat */
1757 for (j = 0; j < 3; j++) {
1758 rvec[j] = values->data[j];
1759 pvec[j] = values->data[3 + j];
1760 spin1vec[j] = values->data[6 + j];
1761 spin2vec[j] = values->data[9 + j];
1762 rdotvec[j] = dvalues->data[j];
1763 }
1764 cross_product(rvec, pvec, rcrossp);
1765 cross_product(rvec, rdotvec, rcrossrdot);
1766 REAL8 rcrossrdotNorm = sqrt(inner_product(rcrossrdot, rcrossrdot));
1767 for (j = 0; j < 3; j++) {
1768 LNhat[j] = rcrossrdot[j] / rcrossrdotNorm;
1769 }
1770
1771 /* Polar dynamics */
1772 polarr = sqrt(inner_product(rvec, rvec));
1773 polarpr = inner_product(rvec, pvec) / polarr;
1774 polarphi = values->data[12] + values->data[13];
1775 REAL8 magL = sqrt(inner_product(rcrossp, rcrossp));
1776 for (j = 0; j < 3; j++) {
1777 Lhat[j] = rcrossp[j] / magL;
1778 }
1779 polarpphi = magL;
1780
1781 /* Computing omega */
1782 omega = rcrossrdotNorm / (polarr * polarr);
1783
1784 /* Projections of the spin vectors onto the Z-axis of the precessing frame,
1785 * L or LN */
1786 if (flagZframe == FLAG_SEOBNRv4P_ZFRAME_L) {
1787 s1dotZ = inner_product(spin1vec, Lhat);
1788 s2dotZ = inner_product(spin2vec, Lhat);
1789 } else if (flagZframe == FLAG_SEOBNRv4P_ZFRAME_LN) {
1790 s1dotZ = inner_product(spin1vec, LNhat);
1791 s2dotZ = inner_product(spin2vec, LNhat);
1792 } else {
1793 XLALPrintError("XLAL Error - %s: flagZframe not recognized.\n", __func__);
1795 }
1796
1797 /* Compute Hamiltonian */
1798 UINT4 SpinAlignedEOBversion = seobParams->seobCoeffs->SpinAlignedEOBversion;
1799 REAL8Vector cartPosVec, cartMomVec, s1Vec, s2Vec;
1800 cartPosVec.length = cartMomVec.length = s1Vec.length = s2Vec.length = 3;
1801 cartPosVec.data = rvec;
1802 cartMomVec.data = pvec;
1803 s1Vec.data = spin1vec; /* in units of mTotal^2 */
1804 s2Vec.data = spin2vec; /* in units of mTotal^2 */
1805 SEOBCalculateSigmaStar(sigmaStar, m1, m2, &s1Vec, &s2Vec);
1806 SEOBCalculateSigmaKerr(sigmaKerr, &s1Vec, &s2Vec);
1807
1808 // Compute the augmented spin used in the Hamiltonian calibration
1809 // coefficients. See LIGO-T1900601-v1.
1810
1811 REAL8 tempS1_p = inner_product(s1Vec.data, Lhat);
1812 REAL8 tempS2_p = inner_product(s2Vec.data, Lhat);
1813 REAL8 S1_perp[3] = {0, 0, 0};
1814 REAL8 S2_perp[3] = {0, 0, 0};
1815 for (UINT4 jj = 0; jj < 3; jj++) {
1816 S1_perp[jj] = spin1vec[jj] - tempS1_p * Lhat[jj];
1817 S2_perp[jj] = spin2vec[jj] - tempS2_p * Lhat[jj];
1818 }
1819 UNUSED REAL8 sKerr_norm =
1820 sqrt(inner_product(sigmaKerr->data, sigmaKerr->data));
1821 REAL8 S_con = 0.0;
1822 if (sKerr_norm > 1e-6) {
1823 S_con = sigmaKerr->data[0] * Lhat[0] + sigmaKerr->data[1] * Lhat[1] +
1824 sigmaKerr->data[2] * Lhat[2];
1825 S_con /= (1 - 2 * eta);
1826 S_con += (inner_product(S1_perp, sigmaKerr->data) +
1827 inner_product(S2_perp, sigmaKerr->data)) /
1828 sKerr_norm / (1 - 2 * eta) / 2.;
1829 }
1830
1831 REAL8 a = sqrt(inner_product(sigmaKerr->data, sigmaKerr->data));
1833 seobCoeffs, eta, a, S_con, SpinAlignedEOBversion) == XLAL_FAILURE) {
1834 XLAL_PRINT_ERROR("XLAL Error: Something went wrong evaluating "
1835 "XLALSimIMRCalculateSpinPrecEOBHCoeffs_v2 in step %d of "
1836 "the main loop.\n",
1837 i);
1839 }
1840 ham = XLALSimIMRSpinPrecEOBHamiltonian(eta, &cartPosVec, &cartMomVec,
1841 &s1Vec, &s2Vec, sigmaKerr, sigmaStar,
1842 seobParams->tortoise, seobCoeffs);
1843 /* Output values in seobdynamics */
1844 seobdyn->velVecx[i] = rdotvec[0];
1845 seobdyn->velVecy[i] = rdotvec[1];
1846 seobdyn->velVecz[i] = rdotvec[2];
1847 seobdyn->polarrVec[i] = polarr;
1848 seobdyn->polarphiVec[i] = polarphi;
1849 seobdyn->polarprVec[i] = polarpr;
1850 seobdyn->polarpphiVec[i] = polarpphi;
1851 seobdyn->omegaVec[i] = omega;
1852 seobdyn->s1dotZVec[i] = s1dotZ;
1853 seobdyn->s2dotZVec[i] = s2dotZ;
1854 seobdyn->hamVec[i] = ham;
1855 }
1856
1857 /* Cleanup */
1858 XLALDestroyREAL8Vector(values);
1859 XLALDestroyREAL8Vector(dvalues);
1860 XLALDestroyREAL8Vector(sigmaStar);
1861 XLALDestroyREAL8Vector(sigmaKerr);
1862
1863 return XLAL_SUCCESS;
1864}
1865
1866/**
1867 * This function computes initial conditions for SEOBNRv4P.
1868 */
1870 REAL8Vector **ICvalues, /**<< Output: vector with initial conditions */
1871 REAL8 MfMin, /**<< Input: dimensionless initial frequency (in units of
1872 1/mTotal) */
1873 REAL8 m1, /**<< Input: mass 1 (solar masses) */
1874 REAL8 m2, /**<< Input: mass 2 (solar masses) */
1875 REAL8Vector *chi1, /**<< Input: dimensionless spin 1 (in units of m1^2) */
1876 REAL8Vector *chi2, /**<< Input: dimensionless spin 2 (in units of m2^2) */
1877 SpinEOBParams *seobParams, /**<< SEOB params */
1879 flagHamiltonianDerivative /**<< flag to decide wether to use analytical
1880 or numerical derivatives */
1881) {
1882 UINT4 j;
1883
1884 /* Check that the input double pointer is not NULL */
1885 if (!ICvalues) {
1887 "XLAL Error - %s: pointer to REAL8Vector ICvalues is NULL.\n",
1888 __func__);
1890 }
1891
1892 /* Allocate vector for initial conditions at fMin */
1893 if (!(*ICvalues = XLALCreateREAL8Vector(14))) {
1895 "XLAL Error - %s: failed to allocate REAL8Vector ICvalues.\n",
1896 __func__);
1898 }
1899 memset((*ICvalues)->data, 0, ((*ICvalues)->length) * sizeof(REAL8));
1900
1901 /* Masses in solar masses */
1902 REAL8 eta = m1 * m2 / (m1 + m2) / (m1 + m2);
1903 /* Flags */
1904 UINT4 SpinAlignedEOBversion = seobParams->seobCoeffs->SpinAlignedEOBversion;
1905 UINT4 SpinsAlmostAligned = seobParams->alignedSpins;
1906
1907 /* Needed for the interface of the old code */
1908 REAL8 fMin = MfMin / (m1 + m2) / LAL_MTSUN_SI;
1909 REAL8 inc =
1910 0.; /* Not clear why XLALSimIMRSpinEOBInitialConditions,
1911 XLALSimIMRSpinEOBInitialConditionsPrec need an inclination */
1912
1913 /* Flag for numerical or analytical derivatives */
1914 INT4 use_optimized = 0;
1915 if (flagHamiltonianDerivative ==
1917 use_optimized = 1;
1918 else if (flagHamiltonianDerivative ==
1920 use_optimized = 0;
1921 else {
1923 "XLAL Error - %s: flagHamiltonianDerivative not recognized.\n",
1924 __func__);
1926 }
1927
1928 /* XLALSimIMRSpinEOBInitialConditions takes as input dimensionfull spins in
1929 * units of solar mass square */
1930 REAL8 mSpin1data[3] = {0., 0., 0.};
1931 REAL8 mSpin2data[3] = {0., 0., 0.};
1932
1933 if (SpinsAlmostAligned) {
1934 /* Here the Z-frame axis is the original z, also direction of L and LN - no
1935 * precession */
1936 REAL8 chi1dotZ = chi1->data[2];
1937 REAL8 chi2dotZ = chi2->data[2];
1938 REAL8 chiS = SEOBCalculateChiS(chi1dotZ, chi2dotZ);
1939 REAL8 chiA = SEOBCalculateChiA(chi1dotZ, chi2dotZ);
1940 REAL8 tplspin = SEOBCalculatetplspin(m1, m2, eta, chi1dotZ, chi2dotZ,
1941 SpinAlignedEOBversion);
1943 seobParams->eobParams->hCoeffs, seobParams, m1, m2, eta, tplspin,
1944 chiS, chiA, SpinAlignedEOBversion) ==
1945 XLAL_FAILURE) /* This function returns XLAL_SUCCESS or calls XLAL_ERROR(
1946 XLAL_EINVAL ) */
1947 {
1948 XLALPrintError("XLAL Error - %s: failure in "
1949 "XLALSimIMREOBCalcSpinFacWaveformCoefficients.\n",
1950 __func__);
1952 }
1953 mSpin1data[2] = chi1->data[2] * m1 * m1;
1954 mSpin2data[2] = chi2->data[2] * m2 * m2;
1955 if (XLALSimIMRSpinEOBInitialConditions(*ICvalues, m1, m2, fMin, inc,
1956 mSpin1data, mSpin2data, seobParams,
1957 use_optimized) ==
1958 XLAL_FAILURE) /* This function returns XLAL_SUCCESS or calls XLAL_ERROR
1959 with XLAL_EINVAL, XLAL_ENOMEM, or XLAL_EMAXITER */
1960 {
1962 "XLAL Error - %s: failure in XLALSimIMRSpinEOBInitialConditions.\n",
1963 __func__);
1965 }
1966 } else {
1967 for (j = 0; j < 3; j++) {
1968 mSpin1data[j] = chi1->data[j] * m1 * m1;
1969 mSpin2data[j] = chi2->data[j] * m2 * m2;
1970 }
1971 /* This function returns XLAL_SUCCESS or calls XLAL_ERROR with XLAL_EINVAL,
1972 * XLAL_ENOMEM, or XLAL_EMAXITER */
1974 *ICvalues, m1, m2, fMin, inc, mSpin1data, mSpin2data, seobParams,
1975 use_optimized) == XLAL_FAILURE) {
1976 XLALPrintError("XLAL Error - %s: failure in "
1977 "XLALSimIMRSpinEOBInitialConditionsPrec.\n",
1978 __func__);
1980 }
1981 }
1982
1983 /* Initial phases are set to 0 */
1984 (*ICvalues)->data[12] = 0.;
1985 (*ICvalues)->data[13] = 0.;
1986
1987 return XLAL_SUCCESS;
1988}
1989
1990/**
1991 * This function converts a spin-aligned dynamics as output by the Runge-Kutta
1992 * integrator to a generic-spin dynamics. Spin-aligned dynamics format: t, r,
1993 * phi, pr, pphi Generic-spin dynamics format: t, x, y, z, px, py, pz, s1x, s1y,
1994 * s1z, s2x, s2y, s2z, phiMod, phiDMod
1995 */
1997 REAL8Array **dynamics, /**<< Output: pointer to array for the generic-spin
1998 dynamics */
1999 REAL8Array *dynamics_spinaligned, /**<< Input: array for the aligned-spin
2000 dynamics */
2001 UINT4 retLen, /**<< Input: length of dynamics */
2002 REAL8 chi1, /**<< Input: spin 1 aligned component (dimensionless) */
2003 REAL8 chi2, /**<< Input: spin 2 aligned component (dimensionless) */
2004 SpinEOBParams *seobParams /**<< SEOB params */
2005) {
2006 UINT4 i;
2007
2008 /* Masses */
2009 REAL8 m1 = seobParams->eobParams->m1;
2010 REAL8 m2 = seobParams->eobParams->m2;
2011 REAL8 mTotal = m1 + m2;
2012
2013 /* Create output dynamics */
2014 *dynamics = XLALCreateREAL8ArrayL(2, 15, retLen);
2015
2016 /* Convert the spin-aligned dynamics to a generic-spins dynamics */
2017 REAL8Vector tVec, rVec, phiVec, prVec, pPhiVec;
2018 tVec.length = rVec.length = phiVec.length = prVec.length = pPhiVec.length =
2019 retLen;
2020 tVec.data = dynamics_spinaligned->data;
2021 rVec.data = dynamics_spinaligned->data + retLen;
2022 phiVec.data = dynamics_spinaligned->data + 2 * retLen;
2023 prVec.data = dynamics_spinaligned->data + 3 * retLen;
2024 pPhiVec.data = dynamics_spinaligned->data + 4 * retLen;
2025 for (i = 0; i < retLen; i++) {
2026 (*dynamics)->data[i] = tVec.data[i];
2027 (*dynamics)->data[retLen + i] = rVec.data[i] * cos(phiVec.data[i]);
2028 (*dynamics)->data[2 * retLen + i] = rVec.data[i] * sin(phiVec.data[i]);
2029 (*dynamics)->data[3 * retLen + i] = 0.;
2030 (*dynamics)->data[4 * retLen + i] =
2031 prVec.data[i] * cos(phiVec.data[i]) -
2032 pPhiVec.data[i] / rVec.data[i] * sin(phiVec.data[i]);
2033 (*dynamics)->data[5 * retLen + i] =
2034 prVec.data[i] * sin(phiVec.data[i]) +
2035 pPhiVec.data[i] / rVec.data[i] * cos(phiVec.data[i]);
2036 (*dynamics)->data[6 * retLen + i] = 0.;
2037 (*dynamics)->data[7 * retLen + i] = 0.;
2038 (*dynamics)->data[8 * retLen + i] = 0.;
2039 (*dynamics)->data[9 * retLen + i] = chi1 * (m1 * m1 / mTotal / mTotal);
2040 (*dynamics)->data[10 * retLen + i] = 0.;
2041 (*dynamics)->data[11 * retLen + i] = 0.;
2042 (*dynamics)->data[12 * retLen + i] = chi2 * (m2 * m2 / mTotal / mTotal);
2043 (*dynamics)->data[13 * retLen + i] = phiVec.data[i];
2044 (*dynamics)->data[14 * retLen + i] = 0.;
2045 }
2046
2047 return XLAL_SUCCESS;
2048}
2049
2050/**
2051 * This function integrates the SEOBNRv4P dynamics.
2052 * Output is given either on the adaptive sampling coming out of the Runge Kutta
2053 * integrator, with no interpolation being made, or on the constant sampling
2054 * specified by deltaT. Either analytical or numerical derivatives of the
2055 * Hamiltonian are used depending on the flag flagHamiltonianDerivative.
2056 * Only numerical derivatives have been shown to work as of June 2019.
2057 * When spins are flagged as almost aligned, falls back to
2058 * spin-aligned dynamics.
2059 */
2061 REAL8Array **dynamics, /**<< Output: pointer to array for the dynamics */
2062 UINT4 *retLenOut, /**<< Output: length of the output dynamics */
2063 REAL8Vector *ICvalues, /**<< Input: vector with initial conditions */
2064 REAL8 EPS_ABS, /**<< Input: absolute accuracy for adaptive Runge-Kutta
2065 integrator */
2066 REAL8 EPS_REL, /**<< Input: relative accuracy for adaptive Runge-Kutta
2067 integrator */
2068 REAL8 deltaT, /**<< Input: timesampling step in geometric units - when
2069 flagConstantSampling is False, used internally only to
2070 initialize adaptive step */
2071 REAL8 deltaT_min, /**<< Input: minimal timesampling step in geometric
2072 units when using adaptive steps with
2073 flagConstantSampling set to False -
2074 set to 0 to ignore */
2075 REAL8 tstart, /**<< Input: starting time of the integration */
2076 REAL8 tend, /**<< Input: max time of the integration - normally, the
2077 integration stops when stopping condition is met, and this is
2078 ignored */
2079 SpinEOBParams *seobParams, /**<< SEOB params */
2080 UINT4 flagConstantSampling, /**<< flag to decide wether to use constant
2081 sampling with deltaT in output instead of
2082 adaptive sampling */
2084 flagHamiltonianDerivative /**<< flag to decide wether to use analytical
2085 or numerical derivatives */
2086) {
2087 UINT4 retLen;
2088
2089 /* Integrator */
2090 LALAdaptiveRungeKuttaIntegrator *integrator = NULL;
2091
2092 /* Flags */
2093 UINT4 SpinsAlmostAligned = seobParams->alignedSpins;
2094
2095 /* Dimensions of vectors of dynamical variables to be integrated */
2096 UINT4 nb_Hamiltonian_variables = 14;
2097 UINT4 nb_Hamiltonian_variables_spinsaligned = 4;
2098
2099 /* Used only when falling back to spin-aligned dynamics */
2100 REAL8Array *dynamics_spinaligned = NULL;
2101
2102 /* Initialize internal values vector for the generic-spin case */
2103 REAL8Vector *values = NULL;
2104 if (!(values = XLALCreateREAL8Vector(nb_Hamiltonian_variables))) {
2105 XLALPrintError("XLAL Error - %s: failed to create REAL8Vector values.\n",
2106 __func__);
2108 }
2109 memcpy(values->data, ICvalues->data, values->length * sizeof(REAL8));
2110 /* Initialize internal values vector for the spin-aligned case -- not used in
2111 * the generic-spin case */
2112 REAL8Vector *values_spinaligned = NULL;
2113 if (!(values_spinaligned =
2114 XLALCreateREAL8Vector(nb_Hamiltonian_variables_spinsaligned))) {
2116 "XLAL Error - %s: failed to create REAL8Vector values_spinaligned.\n",
2117 __func__);
2119 }
2120 memset(values_spinaligned->data, 0,
2121 values_spinaligned->length * sizeof(REAL8));
2122
2123 /* Initialization of the integrator */
2124 if (SpinsAlmostAligned) { /* If spins are almost aligned with LNhat, use
2125 SEOBNRv4 dynamics */
2126 /* In SEOBNRv4 the dynamical variables are r, phi, p_r^*, p_phi */
2127
2128 /* Construct the initial conditions */
2129 REAL8 temp_r = sqrt(ICvalues->data[0] * ICvalues->data[0] +
2130 ICvalues->data[1] * ICvalues->data[1] +
2131 ICvalues->data[2] * ICvalues->data[2]);
2132 REAL8 temp_phi = ICvalues->data[12];
2133
2134 values_spinaligned->data[0] = temp_r; // General form of r
2135 values_spinaligned->data[1] = temp_phi; // phi
2136 values_spinaligned->data[2] = ICvalues->data[3] * cos(temp_phi) +
2137 ICvalues->data[4] * sin(temp_phi); // p_r^*
2138 values_spinaligned->data[3] =
2139 temp_r * (ICvalues->data[4] * cos(temp_phi) -
2140 ICvalues->data[3] * sin(temp_phi)); // p_phi
2141
2142 /* We have to use different stopping conditions depending
2143 we are in the low-sampling or high-sampling portion
2144 of the waveform. We can tell this apart because for the low-sampling (or
2145 ada sampling) we always start at t=0
2146 */
2147 if (tstart > 0) {
2148 // High sampling
2149 integrator = XLALAdaptiveRungeKutta4Init(
2150 nb_Hamiltonian_variables_spinsaligned, XLALSpinAlignedHcapDerivative,
2151 XLALSpinPrecAlignedHiSRStopCondition, EPS_ABS, EPS_REL);
2152 } else {
2153 // Low sampling
2154 integrator = XLALAdaptiveRungeKutta4Init(
2155 nb_Hamiltonian_variables_spinsaligned, XLALSpinAlignedHcapDerivative,
2156 XLALEOBSpinPrecAlignedStopCondition, EPS_ABS, EPS_REL);
2157 }
2158 } else {
2159 if (flagHamiltonianDerivative ==
2161 integrator = XLALAdaptiveRungeKutta4Init(
2162 nb_Hamiltonian_variables, XLALSpinPrecHcapExactDerivative,
2163 XLALEOBSpinPrecStopConditionBasedOnPR, EPS_ABS, EPS_REL);
2164 } else if (flagHamiltonianDerivative ==
2166 if (tstart > 0) {
2167 integrator = XLALAdaptiveRungeKutta4Init(
2168 nb_Hamiltonian_variables, XLALSpinPrecHcapNumericalDerivative,
2169 XLALEOBSpinPrecStopConditionBasedOnPR, EPS_ABS, EPS_REL);
2170 } else {
2171 integrator = XLALAdaptiveRungeKutta4Init(
2172 nb_Hamiltonian_variables, XLALSpinPrecHcapNumericalDerivative,
2173 XLALEOBSpinPrecStopConditionBasedOnPR, EPS_ABS, EPS_REL);
2174 }
2175
2176 } else {
2178 "XLAL Error - %s: flagHamiltonianDerivative not recognized.\n",
2179 __func__);
2181 }
2182 }
2183 if (!integrator) {
2185 "XLAL Error - %s: failure in the initialization of the integrator.\n",
2186 __func__);
2188 }
2189 /* Ensure that integration stops ONLY when the stopping condition is True */
2190 integrator->stopontestonly = 1;
2191 /* When this option is set to 0, the integration can be exceedingly slow for
2192 * spin-aligned systems */
2193 integrator->retries = 1;
2194
2195 /* Computing the dynamical evolution of the system */
2196 // NOTE: XLALAdaptiveRungeKutta4NoInterpolate takes an EOBversion as input.
2197 INT4 EOBversion = 2; // NOTE: value 3 is specific to optv3 in
2198 // XLALAdaptiveRungeKutta4NoInterpolate it determines
2199 // what is stored. We set it to 2.
2200 if (SpinsAlmostAligned) {
2201 /* If spins are almost aligned with LNhat, use SEOBNRv4 dynamics */
2202 if (!flagConstantSampling) {
2204 integrator, seobParams, values_spinaligned->data, 0., tend - tstart,
2205 deltaT, deltaT_min, &dynamics_spinaligned, EOBversion);
2206 } else {
2207 retLen = XLALAdaptiveRungeKutta4(
2208 integrator, seobParams, values_spinaligned->data, 0., tend - tstart,
2209 deltaT, &dynamics_spinaligned);
2210 }
2211 if ((INT4)retLen == XLAL_FAILURE) {
2212 XLALPrintError("XLAL Error - %s: failure in the integration of the "
2213 "spin-aligned dynamics.\n",
2214 __func__);
2216 }
2217
2218 /* Convert the spin-aligned dynamics to a generic-spins dynamics */
2220 dynamics, dynamics_spinaligned, retLen, seobParams->chi1,
2221 seobParams->chi2, seobParams) == XLAL_FAILURE) {
2222 XLALPrintError("XLAL Error - %s: failure in "
2223 "SEOBConvertSpinAlignedDynamicsToGenericSpins.\n",
2224 __func__);
2226 }
2227 } else {
2228 if (!flagConstantSampling) {
2230 integrator, seobParams, values->data, 0., tend - tstart, deltaT,
2231 deltaT_min, dynamics, EOBversion);
2232 } else {
2233 retLen = XLALAdaptiveRungeKutta4(integrator, seobParams, values->data, 0.,
2234 tend - tstart, deltaT, dynamics);
2235 }
2236 if ((INT4)retLen == XLAL_FAILURE) {
2237 XLALPrintError("XLAL Error - %s: failure in the integration of the "
2238 "generic-spin dynamics.\n",
2239 __func__);
2241 }
2242 }
2243
2244 // NOTE: functions like XLALAdaptiveRungeKutta4 would give nans if the times
2245 // do not start at 0 -- we have to adjust the starting time after integration
2246 /* Adjust starting time */
2247 for (UINT4 i = 0; i < retLen; i++)
2248 (*dynamics)->data[i] += tstart;
2249
2250 /* Output length of dynamics */
2251 *retLenOut = retLen;
2252
2253 /* Cleanup */
2254 if (dynamics_spinaligned)
2255 XLALDestroyREAL8Array(dynamics_spinaligned);
2256 XLALDestroyREAL8Vector(values_spinaligned);
2257 XLALDestroyREAL8Vector(values);
2258 XLALAdaptiveRungeKuttaFree(integrator);
2259
2260 return XLAL_SUCCESS;
2261}
2262
2263/**
2264 * This function generates a waveform mode for a given SEOB dynamics.
2265 */
2266// NOTE: as is written here, the step
2267// XLALSimIMREOBCalcSpinPrecFacWaveformCoefficients in the loop will be repeated
2268// across modes -- would be more efficient to loop on modes inside the loop on
2269// times
2272 *hlm, /**<< Output: hlm in complex amplitude / phase form */
2273 INT4 l, /**<< Input: mode index l */
2274 INT4 m, /**<< Input: mode index m */
2275 SEOBdynamics *seobdynamics, /**<< Input: SEOB dynamics */
2276 EOBNonQCCoeffs *nqcCoeffs, /**<< Input: NQC coeffs */
2277 SpinEOBParams *seobParams, /**<< SEOB params */
2278 // UINT4 SpinsAlmostAligned, /**<< flag to decide wether to fall back to
2279 // aligned spins */
2280 UINT4 includeNQC /**<< flag to choose wether or not to include NQC */
2281) {
2282 /* Check that the input double pointer are not NULL */
2283 if (!hlm) {
2285 "XLAL Error - %s: pointer to CAmpPhaseSequence hlm is NULL.\n",
2286 __func__);
2288 }
2289
2290 /* Masses */
2291 REAL8 m1 = seobParams->eobParams->m1;
2292 REAL8 m2 = seobParams->eobParams->m2;
2293 REAL8 deltaT = seobdynamics->tVec[1] - seobdynamics->tVec[0];
2294 REAL8 eta = seobParams->eobParams->eta;
2295 REAL8 mtot = m1 + m2;
2296 UINT4 SpinAlignedEOBversion = seobParams->seobCoeffs->SpinAlignedEOBversion;
2297 UINT4 SpinAlignedEOBversionWaveform; // RC: I use this different variable
2298 // because the PN terms in the waveform
2299 // are different from those in the flux
2300
2301 /* Length of dynamics data and sampling step */
2302 UINT4 retLen = seobdynamics->length;
2303
2304 /* Allocate structure for complex amplitude and phase */
2305 if (CAmpPhaseSequence_Init(hlm, retLen) == XLAL_FAILURE) {
2306 XLALPrintError("XLAL Error - %s: failure in CAmpPhaseSequence_Init.\n",
2307 __func__);
2309 }
2310
2311 /* Workspace vectors */
2312 REAL8Vector values, polarDynamics;
2313 REAL8 valuesdata[14] = {0.};
2314 REAL8 polarDynamicsdata[4] = {0.};
2315 values.length = 14;
2316 polarDynamics.length = 4;
2317 values.data = valuesdata;
2318 polarDynamics.data = polarDynamicsdata;
2319 REAL8 tPeakOmega = seobParams->tPeakOmega;
2320
2321 /* Calibration parameter */
2322 if (includeNQC == 0) {
2323 if (((l == 2) && (m == 1)) || ((l == 5) && (m == 5))) {
2325 seobParams, l, m, seobdynamics,
2326 tPeakOmega - seobdynamics->tVec[0], m1, m2,
2327 deltaT) == XLAL_FAILURE) {
2328 XLALPrintError("XLAL Error - %s: failure in "
2329 "XLALSimIMREOBCalcCalibCoefficientHigherModesPrec.\n",
2330 __func__);
2332 }
2333 }
2334 }
2335
2336 /* Loop to compute compute amplitude and phase of the hlm mode */
2337 REAL8 s1dotZ, s2dotZ, chiS, chiA, tplspin, t, omega, ham, v;
2338 UINT4 i, j;
2339 for (i = 0; i < retLen; i++) {
2340 /* Compute waveform coefficients */
2341 t = seobdynamics->tVec[i];
2342 omega = seobdynamics->omegaVec[i];
2343 ham = seobdynamics->hamVec[i];
2344 s1dotZ = seobdynamics->s1dotZVec[i];
2345 s2dotZ = seobdynamics->s2dotZVec[i];
2346 REAL8 chi1dotZ = s1dotZ * mtot * mtot / (m1 * m1);
2347 REAL8 chi2dotZ = s2dotZ * mtot * mtot / (m2 * m2);
2348 chiS = SEOBCalculateChiS(chi1dotZ, chi2dotZ);
2349 chiA = SEOBCalculateChiA(chi1dotZ, chi2dotZ);
2350
2351 tplspin = SEOBCalculatetplspin(m1, m2, eta, s1dotZ, s2dotZ,
2352 SpinAlignedEOBversion);
2353 if (SpinAlignedEOBversion == 4) {
2354 SpinAlignedEOBversionWaveform = v4Pwave;
2355 } else {
2356 SpinAlignedEOBversionWaveform = SpinAlignedEOBversion;
2357 }
2358
2360 seobParams->eobParams->hCoeffs, m1, m2, eta, tplspin, chiS, chiA,
2361 SpinAlignedEOBversionWaveform) == XLAL_FAILURE) {
2362 XLALPrintError("XLAL Error - %s: failure in "
2363 "XLALSimIMREOBCalcSpinPrecFacWaveformCoefficients at step "
2364 "%d of the loop.\n",
2365 __func__, i);
2367 }
2368 seobParams->eobParams->hCoeffs->f21v7c = seobParams->cal21;
2369 seobParams->eobParams->hCoeffs->f55v5c = seobParams->cal55;
2370 // printf("f21v7c = %.16f\n", seobParams->eobParams->hCoeffs->f21v7c);
2371 /* Dynamics, polar dynamics, omega */
2372 for (j = 0; j < 14; j++) {
2373 values.data[j] = seobdynamics->array->data[i + (j + 1) * retLen];
2374 }
2375 polarDynamics.data[0] = seobdynamics->polarrVec[i];
2376 polarDynamics.data[1] = seobdynamics->polarphiVec[i];
2377 polarDynamics.data[2] = seobdynamics->polarprVec[i];
2378 polarDynamics.data[3] = seobdynamics->polarpphiVec[i];
2379 v = cbrt(omega);
2380 COMPLEX16 hlm_val = 0.;
2382 &hlm_val, &polarDynamics, &values, v, ham, l, m, seobParams) ==
2383 XLAL_FAILURE) {
2384 XLALPrintError("XLAL Error - %s: failure in "
2385 "XLALSimIMRSpinEOBGetPrecSpinFactorizedWaveform at step "
2386 "%d of the loop.\n",
2387 __func__, i);
2389 }
2390 /* NQC correction */
2391 COMPLEX16 factor_nqc = 1.;
2392 if (includeNQC) {
2393 if (XLALSimIMRSpinEOBNonQCCorrection(&factor_nqc, &values, omega,
2394 nqcCoeffs) == XLAL_FAILURE) {
2396 "XLAL Error - %s: failure in XLALSimIMRSpinEOBNonQCCorrection at "
2397 "step %d of the loop.\n",
2398 __func__, i);
2400 }
2401 } else
2402 factor_nqc = 1.;
2403 /* Result and output */
2404 COMPLEX16 hlmNQC = hlm_val * factor_nqc;
2405 (*hlm)->xdata->data[i] = t; /* Copy times */
2406 (*hlm)->camp_real->data[i] = cabs(hlmNQC);
2407 (*hlm)->camp_imag->data[i] = 0.; /* We use only real amplitudes */
2408 (*hlm)->phase->data[i] = carg(hlmNQC);
2409 }
2410
2411 /* Unwrap the phase vector, in place */
2412 XLALREAL8VectorUnwrapAngle((*hlm)->phase, (*hlm)->phase);
2413
2414 return XLAL_SUCCESS;
2415}
2416
2417/**
2418 * This function generates all waveform modes as a list for a given SEOB
2419 * dynamics.
2420 */
2423 *listhlm, /**<< Output: list of modes for hlm */
2424 INT4 modes[][2], /**<< Input: array of modes (l,m) */
2425 UINT4 nmodes, /**<< Input: number of modes (l,m) */
2426 SEOBdynamics *seobdynamics, /**<< Input: SEOB dynamics */
2427 SphHarmListEOBNonQCCoeffs *listnqcCoeffs, /**<< Input: list of NQCs */
2428 SpinEOBParams *seobParams, /**<< SEOB params */
2429 UINT4 flagNQC /**<< flag to choose wether or not to include NQC */
2430) {
2431 /* Read version of SEOB to be used */
2432 UINT4 SpinAlignedEOBversion = seobParams->seobCoeffs->SpinAlignedEOBversion;
2433
2434 /* Flag for inclusion of NQC, useful for higher modes that have no NQC
2435 * implemented for some SpinAlignedEOBversion */
2436 UINT4 includeNQC = 0;
2437
2438 /* Loop over modes */
2439 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
2440
2441 INT4 l = modes[nmode][0];
2442 INT4 m = modes[nmode][1];
2443
2444 if ((!(l == 2 && m == 2)) && (SpinAlignedEOBversion == 3)) {
2445 includeNQC = 0; /* For HM beyond 22, no NQC available for
2446 SpinAlignedEOBversion==3 */
2447 } else
2448 includeNQC = flagNQC;
2449
2450 EOBNonQCCoeffs *nqcCoeffs =
2452
2453 CAmpPhaseSequence *hlm = NULL;
2454 SEOBCalculatehlmAmpPhase(&hlm, l, m, seobdynamics, nqcCoeffs, seobParams,
2455 includeNQC);
2456
2458 }
2459
2460 return XLAL_SUCCESS;
2461}
2462
2463/**
2464 * This function finds the peak of omega.
2465 * Note that there are various possibilities as of what is returned if
2466 * tPeakOmega is not found at first. In particular, by default,
2467 * if there is no peak found, the last point of the dynamics is used.
2468 */
2470 REAL8 *tPeakOmega, /**<< Output: time of peak of Omega if found (see inside
2471 XLALSimLocateOmegaTime for what is returned otherwise) */
2472 INT4 *foundPeakOmega, /**<< Output: flag indicating wether tPeakOmega has
2473 been found */
2474 UNUSED REAL8Array *dynamics, /**<< Input: array for dynamics */
2475 SEOBdynamics *seobdynamics, /**<< Input: SEOB dynamics object */
2476 UINT4 retLen, /**<< Input: length of dynamics */
2477 UNUSED SpinEOBParams *seobParams, /**<< SEOB params */
2479 flagHamiltonianDerivative /**<< flag to decide wether to use analytical
2480 or numerical derivatives */
2481) {
2482
2483 REAL8Vector tVec;
2484 tVec.length = retLen;
2485 tVec.data = seobdynamics->tVec;
2486 REAL8Vector omegaVec;
2487 omegaVec.length = retLen;
2488 omegaVec.data = seobdynamics->omegaVec;
2489 XLALEOBFindRobustPeak(tPeakOmega, &tVec, &omegaVec, 3);
2490 *foundPeakOmega = 1;
2491
2492 /*
2493 // Uncomment this block to use old-style finding of the peak of Omega
2494 UNUSED REAL8 m1 = seobParams->eobParams->m1;
2495 UNUSED REAL8 m2 = seobParams->eobParams->m2;
2496 REAL8Vector radiusVec;
2497 radiusVec.length = retLen;
2498 radiusVec.data = seobdynamics->polarrVec;
2499 // Number of variables in the dynamics array (besides time)
2500 UINT4 numdynvars = 14;
2501 // A parameter we don't know what to do with
2502 REAL8 tMaxOmega = 0; // Will not be used, and is not returned
2503
2504 // Flag for numerical or analytical derivatives
2505 INT4 use_optimized = 0;
2506 if (flagHamiltonianDerivative ==
2507 FLAG_SEOBNRv4P_HAMILTONIAN_DERIVATIVE_ANALYTICAL)
2508 use_optimized = 1;
2509 else if (flagHamiltonianDerivative ==
2510 FLAG_SEOBNRv4P_HAMILTONIAN_DERIVATIVE_NUMERICAL)
2511 use_optimized = 0;
2512 else {
2513 XLALPrintError(
2514 "XLAL Error - %s: flagHamiltonianDerivative not
2515 recognized.\n",
2516 __func__);
2517 XLAL_ERROR(XLAL_EINVAL);
2518 }
2519 //Time of peak of Omega
2520 *tPeakOmega = XLALSimLocateOmegaTime(
2521 dynamics, numdynvars, retLen, *seobParams, *(seobParams->seobCoeffs), m1,
2522 m2, &radiusVec, foundPeakOmega, &tMaxOmega, use_optimized);
2523 */
2524
2525 return XLAL_SUCCESS;
2526}
2527
2528/**
2529 * This function looks for the peak of a mode amplitude.
2530 * Note that the internals are complicated, see XLALSimLocateAmplTime.
2531 */
2533 REAL8
2534 *tPeakAmp, /**<< Output: time of peak of amplitude if found (see inside
2535 XLALSimLocateAmplTime for what is returned otherwise) */
2536 INT4 *foundPeakAmp, /**<< Output: flag indicating wether tPeakOmega has been
2537 found */
2538 CAmpPhaseSequence *hlm, /**<< Input: mode in complex amplitude/phase form */
2539 SEOBdynamics *seobdynamics, /**<< Input: SEOB dynamics object */
2540 UINT4 retLen /**<< Input: length of dynamics */
2541) {
2542 /* Vectors with times and radius from dynamics */
2543 REAL8Vector timeVec, radiusVec;
2544 timeVec.length = radiusVec.length = retLen;
2545 radiusVec.data = seobdynamics->polarrVec;
2546 timeVec.data = seobdynamics->tVec;
2547
2548 /* Computing mode */
2549 // NOTE: computing Re/Im is redundant, as XLALSimLocateAmplTime will recompute
2550 // the amplitude internally
2551 COMPLEX16Vector *hmode = NULL;
2552 if (!(hmode = XLALCreateCOMPLEX16Vector(retLen))) {
2554 "XLAL Error - %s: failed to allocate COMPLEX16Vector hmode.\n",
2555 __func__);
2557 }
2558 for (UINT4 i = 0; i < retLen; i++) {
2559 hmode->data[i] = (hlm->camp_real->data[i] + I * hlm->camp_imag->data[i]) *
2560 cexp(I * hlm->phase->data[i]);
2561 }
2562
2563 /* Not clear what this is for, not used */
2564 // REAL8 tMaxAmp = 0.;
2565
2566 /* Time of peak of amplitude */
2567 //*tPeakAmp = XLALSimLocateAmplTime( &timeVec, hmode, &radiusVec,
2568 // foundPeakAmp, &tMaxAmp );
2569 *tPeakAmp = XLALSimLocateMaxAmplTime(&timeVec, hmode, foundPeakAmp);
2570
2571 /* Cleanup */
2573
2574 return XLAL_SUCCESS;
2575}
2576
2577/**
2578 * This function computes all extended dynamics values at a given time by
2579 * interpolating the dynamics array. We build a cubic spline limited to +- 20
2580 * samples on each side of the time of interest.
2581 */
2583 REAL8Vector **seobdynamics_values, /**<< Output: pointer to vector for
2584 seobdynamics interpolated values */
2585 REAL8 t, /**<< Input: time at which to evaluate */
2586 SEOBdynamics *seobdynamics /**<< Input: SEOB dynamics */
2587) {
2588 /* Create output vector */
2589 if (!((*seobdynamics_values) = XLALCreateREAL8Vector(v4PdynamicsVariables))) {
2590 XLALPrintError("XLAL Error - %s: failed to allocate REAL8Vector "
2591 "seobdynamics_values.\n",
2592 __func__);
2594 }
2595 memset((*seobdynamics_values)->data, 0,
2596 ((*seobdynamics_values)->length) * sizeof(REAL8));
2597
2598 /* Check that the time asked for is in range */
2599 UINT4 retLen = seobdynamics->length;
2600 REAL8 *tVec = seobdynamics->tVec;
2601 if ((t < tVec[0]) || (t > tVec[retLen - 1])) {
2603 "XLAL Error - %s: time for interpolation is out of range of "
2604 "the SEOBdynamics data. t = %.17f, tVec[0]=%.17f, tVec[-1]=%.17f\n",
2605 __func__, t, tVec[0], tVec[retLen - 1]);
2607 }
2608
2609 /* Get the start and end indices that we will use to interpolate */
2610 /* indext max index such that tVec[indext] <= t */
2611 UINT4 indext = 0;
2612 while ((indext < retLen - 1) && (tVec[indext + 1] <= t))
2613 indext++;
2614 INT4 indexstart = indext - 20 > 0 ? indext - 20 : 0;
2615 INT4 indexend = indext + 20 < retLen - 1 ? indext + 20 : retLen - 1;
2616 INT4 interp_length = indexend - indexstart + 1;
2617 if (interp_length <= 0) {
2618 XLALPrintError("XLAL Error - %s: not finding a strictly positive number of "
2619 "samples for interpolation.\n",
2620 __func__);
2622 }
2623
2624 /* Spline allocation */
2625 gsl_spline *spline = gsl_spline_alloc(gsl_interp_cspline, interp_length);
2626 gsl_interp_accel *acc = gsl_interp_accel_alloc();
2627
2628 /* Interpolate all quantities */
2629 (*seobdynamics_values)->data[0] = t;
2630 for (UINT4 j = 1; j < v4PdynamicsVariables; j++) {
2631 gsl_spline_init(spline, &(tVec[indexstart]),
2632 &(seobdynamics->array->data[j * retLen + indexstart]),
2633 interp_length);
2634 (*seobdynamics_values)->data[j] = gsl_spline_eval(spline, t, acc);
2635 }
2636
2637 /* Cleanup */
2638 gsl_spline_free(spline);
2639 gsl_interp_accel_free(acc);
2640
2641 return XLAL_SUCCESS;
2642}
2643
2645 REAL8Vector **S1, /**<<Output: S1 in L-n frame */
2646 REAL8Vector **S2, /**<<Output: S2 in L-n frame */
2647 REAL8Vector *seobvalues, /**<<Input: vector of extended dynamics */
2648 REAL8 m1, /**<<Input: mass of the first object in solar masses */
2649 REAL8 m2, /**<<Input: mass of the second object in solar masses */
2651 flagZframe /**<<Input: whether to compute the L_N or L frame */
2652) {
2653
2654 if ((!S1) || (!S2) || (!seobvalues)) {
2655 XLALPrintError("Passed null pointers to SEOBLFrameVectors!\n");
2657 }
2658 REAL8 mTotal = m1 + m2;
2659 // Scaled masses, useful to compute spins in sane units
2660 REAL8 m1_sc = m1 / mTotal;
2661 REAL8 m2_sc = m2 / mTotal;
2662 /* Create output vectors */
2663 if (!((*S1) = XLALCreateREAL8Vector(3))) {
2664 XLALPrintError("XLAL Error: failed to allocate REAL8Vector S1.\n");
2666 }
2667 if (!((*S2) = XLALCreateREAL8Vector(3))) {
2668 XLALDestroyREAL8Vector(*S1); // Free the memory above
2669 XLALPrintError("XLAL Error failed to allocate REAL8Vector S2.\n");
2671 }
2672 memset((*S1)->data, 0, 3 * sizeof(REAL8));
2673 memset((*S2)->data, 0, 3 * sizeof(REAL8));
2674
2675 /* Local variables */
2676 REAL8 rvec[3] = {0, 0, 0};
2677 REAL8 drvec[3] = {0, 0, 0};
2678 REAL8 pvec[3] = {0, 0, 0};
2679 REAL8 spin1vec[3] = {0, 0, 0}; /* in units of mTotal^2 */
2680 REAL8 spin2vec[3] = {0, 0, 0}; /* in units of mTotal^2 */
2681 REAL8 crossp[3] = {0, 0, 0};
2682 REAL8 L_hat[3] = {0, 0, 0};
2683 REAL8 n_hat[3] = {0, 0, 0};
2684 REAL8 lambda_hat[3] = {0, 0, 0};
2685 /* Read from the extended dynamics values */
2686 for (int j = 0; j < 3; j++) {
2687 rvec[j] = seobvalues->data[1 + j];
2688 drvec[j] = seobvalues->data[15 + j];
2689 pvec[j] = seobvalues->data[4 + j];
2690 spin1vec[j] = seobvalues->data[7 + j] / m1_sc / m1_sc;
2691 spin2vec[j] = seobvalues->data[10 + j] / m2_sc / m2_sc;
2692 }
2693 if (flagZframe == FLAG_SEOBNRv4P_ZFRAME_L) {
2694 // Note: pvec is missing a factor of nu, but don't care about it's magnitide
2695 // anyway
2696 cross_product(rvec, pvec, crossp);
2697 } else if (flagZframe == FLAG_SEOBNRv4P_ZFRAME_LN) {
2698 cross_product(rvec, drvec, crossp);
2699 }
2700 REAL8 Lmag = sqrt(inner_product(crossp, crossp));
2701 REAL8 sep = sqrt(inner_product(rvec, rvec));
2702
2703 for (int jj = 0; jj < 3; jj++) {
2704 L_hat[jj] = crossp[jj] / Lmag;
2705 n_hat[jj] = rvec[jj] / sep;
2706 }
2707
2708 cross_product(L_hat, n_hat, lambda_hat);
2709 // Project onto the new frame
2710 (*S1)->data[0] = inner_product(spin1vec, n_hat);
2711 (*S2)->data[0] = inner_product(spin2vec, n_hat);
2712
2713 (*S1)->data[1] = inner_product(spin1vec, lambda_hat);
2714 (*S2)->data[1] = inner_product(spin2vec, lambda_hat);
2715
2716 (*S1)->data[2] = inner_product(spin1vec, L_hat);
2717 (*S2)->data[2] = inner_product(spin2vec, L_hat);
2718 return XLAL_SUCCESS;
2719}
2720
2721/**
2722 * This function computes the J vector.
2723 */
2725 REAL8Vector **J, /**<< Output: pointer to vector J */
2726 REAL8Vector *seobvalues, /**<< Input: vector for extended dynamics values */
2727 SpinEOBParams *seobParams /**<< SEOB params */
2728) {
2729 UINT4 j;
2730 if ((!J) || (!seobvalues) || (!seobParams)) {
2731 XLALPrintError("Some pointers passed to SEOBJfromDynamics were null\n");
2733 }
2734 /* Create output vector */
2735 if (!((*J) = XLALCreateREAL8Vector(3))) {
2736 XLALPrintError("XLAL Error failed to allocate REAL8Vector J.\n");
2738 }
2739 memset((*J)->data, 0, 3 * sizeof(REAL8));
2740
2741 /* Masses */
2742 REAL8 eta = seobParams->eobParams->eta;
2743
2744 /* Local variables */
2745 REAL8 rvec[3] = {0, 0, 0};
2746 REAL8 pvec[3] = {0, 0, 0};
2747 REAL8 spin1vec[3] = {0, 0, 0}; /* in units of mTotal^2 */
2748 REAL8 spin2vec[3] = {0, 0, 0}; /* in units of mTotal^2 */
2749 REAL8 rcrossp[3] = {0, 0, 0};
2750
2751 /* Read from the extended dynamics values */
2752 for (j = 0; j < 3; j++) {
2753 rvec[j] = seobvalues->data[1 + j];
2754 pvec[j] = seobvalues->data[4 + j];
2755 spin1vec[j] = seobvalues->data[7 + j];
2756 spin2vec[j] = seobvalues->data[10 + j];
2757 }
2758 cross_product(rvec, pvec, rcrossp);
2759
2760 /* Compute J - restoring the factor eta in L (p stored in the dynamics is
2761 * p/mu) */
2762 for (j = 0; j < 3; j++) {
2763 (*J)->data[j] = eta * rcrossp[j] + spin1vec[j] + spin2vec[j];
2764 }
2765
2766 return XLAL_SUCCESS;
2767}
2768
2769/**
2770 * This function computes the L-hat vector.
2771 */
2773 REAL8Vector **L, /**<< Output: pointer to vector L */
2774 REAL8Vector *seobvalues, /**<< Input: vector for extended dynamics values */
2775 SpinEOBParams *seobParams /**<< SEOB params */,
2777 flagZframe /**<<Input: whether to compute the L_N or L frame */
2778)
2779{
2780 if ((!L) || (!seobvalues) || (!seobParams))
2781 {
2782 XLALPrintError("Some pointers passed to SEOBLfromDynamics were null\n");
2784 }
2785 /* Create output vector */
2786 if (!((*L) = XLALCreateREAL8Vector(3)))
2787 {
2788 XLALPrintError("XLAL Error failed to allocate REAL8Vector L.\n");
2790 }
2791 memset((*L)->data, 0, 3 * sizeof(REAL8));
2792
2793 /* Local variables */
2794 REAL8 rvec[3] = {0, 0, 0};
2795 REAL8 drvec[3] = {0, 0, 0};
2796 REAL8 pvec[3] = {0, 0, 0};
2797 REAL8 crossp[3] = {0, 0, 0};
2798
2799 /* Read from the extended dynamics values */
2800 for (int j = 0; j < 3; j++)
2801 {
2802 rvec[j] = seobvalues->data[1 + j];
2803 drvec[j] = seobvalues->data[15 + j];
2804 pvec[j] = seobvalues->data[4 + j];
2805 }
2806 if (flagZframe == FLAG_SEOBNRv4P_ZFRAME_L)
2807 {
2808 // Note: pvec is missing a factor of nu, but don't care about it's magnitide
2809 // anyway
2810 cross_product(rvec, pvec, crossp);
2811 }
2812 else if (flagZframe == FLAG_SEOBNRv4P_ZFRAME_LN)
2813 {
2814 cross_product(rvec, drvec, crossp);
2815 }
2816 REAL8 Lmag = sqrt(inner_product(crossp, crossp));
2817
2818 for (int jj = 0; jj < 3; jj++)
2819 {
2820 (*L)->data[jj] = crossp[jj] / Lmag;
2821 }
2822
2823 return XLAL_SUCCESS;
2824}
2825
2826/**
2827 * This function computes the Jframe unit vectors, with e3J along Jhat.
2828 * Convention: if (ex, ey, ez) is the initial I-frame, e1J chosen such that ex
2829 * is in the plane (e1J, e3J) and ex.e1J>0.
2830 * In the case where e3J and x happen to be close to aligned, we continuously
2831 * switch to another prescription with y playing the role of x
2832 */
2834 REAL8Vector *e1J, /**<< Output: vector for e1J, already allocated */
2835 REAL8Vector *e2J, /**<< Output: vector for e2J, already allocated */
2836 REAL8Vector *e3J, /**<< Output: vector for e3J, already allocated */
2837 REAL8Vector *JVec /**<< Input: vector J */
2838) {
2839 UINT4 j;
2840
2841 /* Checking size and of input vectors */
2842 if ((!e1J) || (!e2J) || (!e3J) || (!JVec)) {
2843 XLALPrintError("XLAL Error: at least one input pointer is NULL.\n");
2845 }
2846 if ((!(e1J->length == 3)) || (!(e2J->length == 3)) || (!(e2J->length == 3))) {
2848 "XLAL Error: at least one input vector is not of length 3.\n");
2850 }
2851
2852 /* Set e3J to Jhat */
2853 REAL8 Jnorm = sqrt(inner_product(JVec->data, JVec->data));
2854 for (j = 0; j < 3; j++) {
2855 e3J->data[j] = JVec->data[j] / Jnorm;
2856 }
2857
2858 /* Set e1J to enforce the condition that x is in the plane (e1J, e3J) and
2859 * x.e1J>0 */
2860 /* Added a protection against the degenerate case where e3J, x are aligned:
2861 * let lambda = 1 - |ex.e3J|, measuring the alignment of e3J, x
2862 * for lambda < 1e-5 use y instead of x
2863 * for lambda > 1e-4 use x normally
2864 * for lambda in [1e-4, 1e-5] use a lambda-weighted combination of both
2865 * thresholds are arbitrary */
2866 REAL8 normfacx = 0.;
2867 REAL8 normfacy = 0.;
2868 REAL8 weightx = 0.;
2869 REAL8 weighty = 0.;
2870 REAL8 e1Jblendednorm = 0.;
2871 REAL8 exvec[3] = {1, 0, 0};
2872 REAL8 eyvec[3] = {0, 1, 0};
2873 REAL8 exdote3J = inner_product(exvec, e3J->data);
2874 REAL8 eydote3J = inner_product(eyvec, e3J->data);
2875 REAL8 lambda = 1. - fabs(exdote3J);
2876 if ((lambda < 0.) || (lambda > 1.)) {
2877 XLALPrintError("Problem: lambda=1-|e3J.ex|=%g, should be in [0,1]", lambda);
2879 }
2880 if (lambda > 1e-4) {
2881 normfacx = 1. / sqrt(1. - exdote3J * exdote3J);
2882 for (j = 0; j < 3; j++) {
2883 e1J->data[j] = (exvec[j] - exdote3J * e3J->data[j]) / normfacx;
2884 }
2885 } else if (lambda < 1e-5) {
2886 normfacy = 1. / sqrt(1. - eydote3J * eydote3J);
2887 for (j = 0; j < 3; j++) {
2888 e1J->data[j] = (eyvec[j] - eydote3J * e3J->data[j]) / normfacy;
2889 }
2890 } else {
2891 weightx = (lambda - 1e-5) / (1e-4 - 1e-5);
2892 weighty = 1. - weightx;
2893 normfacx = 1. / sqrt(1. - exdote3J * exdote3J);
2894 normfacy = 1. / sqrt(1. - eydote3J * eydote3J);
2895 for (j = 0; j < 3; j++) {
2896 e1J->data[j] = weightx * (exvec[j] - exdote3J * e3J->data[j]) / normfacx +
2897 weighty * (eyvec[j] - eydote3J * e3J->data[j]) / normfacy;
2898 }
2899 e1Jblendednorm = sqrt(inner_product(e1J->data, e1J->data));
2900 for (j = 0; j < 3; j++) {
2901 e1J->data[j] /= e1Jblendednorm;
2902 }
2903 }
2904
2905 /* Get e2J = e3J * e1J */
2906 cross_product(e3J->data, e1J->data, e2J->data);
2907
2908 /* Normally, vectors already of unit norm - we normalize again to eliminate
2909 * possible round-off error */
2910 REAL8 e1Jnorm = sqrt(inner_product(e1J->data, e1J->data));
2911 REAL8 e2Jnorm = sqrt(inner_product(e2J->data, e2J->data));
2912 REAL8 e3Jnorm = sqrt(inner_product(e3J->data, e3J->data));
2913 for (j = 0; j < 3; j++) {
2914 e1J->data[j] /= e1Jnorm;
2915 e2J->data[j] /= e2Jnorm;
2916 e3J->data[j] /= e3Jnorm;
2917 }
2918
2919 return XLAL_SUCCESS;
2920}
2921
2922/**
2923 * This function computes Euler angles I2J given the unit vectors of the Jframe.
2924 */
2926 REAL8 *alphaI2J, /**<< Output: Euler angle alpha I2J */
2927 REAL8 *betaI2J, /**<< Output: Euler angle beta I2J */
2928 REAL8 *gammaI2J, /**<< Output: Euler angle gamma I2J */
2929 REAL8Vector *e1J, /**<< Input: unit Jframe vector e1J */
2930 REAL8Vector *e2J, /**<< Input: unit Jframe vector e2J */
2931 REAL8Vector *e3J /**<< Input: unit Jframe vector e3J */
2932) {
2933 /* Active rotation matrix from frame (x,y,z) to frame (e1J,e2J,e3J) */
2934 /* The input vectors (eJ) are decomposed on the basis (x,y,z) */
2935 REAL8Array *R = XLALCreateREAL8ArrayL(2, 3, 3);
2936 if (!R) {
2937 XLALPrintError("Allocating the rotation matrix failed!");
2939 }
2941
2942 /* Compute Euler angles in the Z-Y-Z convention */
2943 EulerAnglesZYZFromRotationMatrixActive(alphaI2J, betaI2J, gammaI2J, R);
2944
2945 /* Cleanup */
2947
2948 return XLAL_SUCCESS;
2949}
2950
2951/**
2952 * This function computes the NQC coefficients for a list of mode contributions.
2953 */
2956 *nqcCoeffsList, /**<< Output: non-quasi-circular coefficients as a list
2957 for each mode */
2958 INT4 modes[][2], /**<< Input: array of modes (l,m) */
2959 UINT4 nmodes, /**<< Input: number of modes (l,m) */
2960 REAL8 tPeakOmega, /**<< Input: time of peak of Omega */
2961 SEOBdynamics *seobdynamics, /**<< Input: SEOB dynamics */
2962 SpinEOBParams *seobParams, /**<< Input: SEOB params */
2963 REAL8Vector *chi1_omegaPeak, /**<< Input: dimensionless spin 1 at peak of
2964 omega in L_N frame */
2965 REAL8Vector *chi2_omegaPeak /**<< Input: dimensionless spin 2 at peak of
2966 omega in L_N frame */
2967) {
2968 /* Masses */
2969 REAL8 m1 = seobParams->eobParams->m1;
2970 REAL8 m2 = seobParams->eobParams->m2;
2971 seobParams->tPeakOmega = tPeakOmega;
2972 seobParams->spin1z_omegaPeak = chi1_omegaPeak->data[2];
2973 seobParams->spin2z_omegaPeak = chi2_omegaPeak->data[2];
2974 // REAL8 mtot = m1 + m2;
2975 // UINT4 SpinAlignedEOBversion =
2976 // seobParams->seobCoeffs->SpinAlignedEOBversion;
2977
2978 /* Length of dynamics data and sampling step */
2979 UINT4 retLen = seobdynamics->length;
2980 REAL8 deltaT = seobdynamics->tVec[1] - seobdynamics->tVec[0];
2981
2982 /* Create vectors from dynamics */
2983 REAL8Vector r, pr, omega;
2984 r.length = pr.length = omega.length = retLen;
2985 r.data = seobdynamics->polarrVec;
2986 pr.data = seobdynamics->polarprVec;
2987 omega.data = seobdynamics->omegaVec;
2988
2989 /* Workspace vectors */
2990 /*
2991 REAL8Vector s1Vec, s2Vec, sigmaKerr;
2992 REAL8 s1Vecdata[3] = {0.};
2993 REAL8 s2Vecdata[3] = {0.};
2994 REAL8 sigmaKerrdata[3] = {0.};
2995 s1Vec.length = s2Vec.length = sigmaKerr.length = 3;
2996 s1Vec.data = s1Vecdata;
2997 s2Vec.data = s2Vecdata;
2998 sigmaKerr.data = sigmaKerrdata;
2999 */
3000 /* Final values for a and for the spins projected onto Z */
3001 /*
3002 REAL8 s1dotZfinal = seobdynamics->s1dotZVec[retLen-1];
3003 REAL8 s2dotZfinal = seobdynamics->s2dotZVec[retLen-1];
3004 REAL8 chi1dotZfinal = s1dotZfinal * mtot*mtot / (m1*m1);
3005 REAL8 chi2dotZfinal = s2dotZfinal * mtot*mtot / (m2*m2);
3006 REAL8 chiSfinal = SEOBCalculateChiS( chi1dotZfinal, chi2dotZfinal );
3007 REAL8 chiAfinal = SEOBCalculateChiA( chi1dotZfinal, chi2dotZfinal );
3008 s1Vec.data[0] = seobdynamics->s1Vecx[retLen-1];
3009 s1Vec.data[1] = seobdynamics->s1Vecy[retLen-1];
3010 s1Vec.data[2] = seobdynamics->s1Vecz[retLen-1];
3011 s2Vec.data[0] = seobdynamics->s2Vecx[retLen-1];
3012 s2Vec.data[1] = seobdynamics->s2Vecy[retLen-1];
3013 s2Vec.data[2] = seobdynamics->s2Vecz[retLen-1];
3014 SEOBCalculateSigmaKerr( &sigmaKerr, &s1Vec, &s2Vec );
3015 */
3016 // REAL8 afinal = sqrt( inner_product( sigmaKerr.data, sigmaKerr.data ) );
3017
3018 REAL8 chi1dotZfinal = chi1_omegaPeak->data[2];
3019 REAL8 chi2dotZfinal = chi2_omegaPeak->data[2];
3020 REAL8 chiSfinal = SEOBCalculateChiS(chi1dotZfinal, chi2dotZfinal);
3021 REAL8 chiAfinal = SEOBCalculateChiA(chi1dotZfinal, chi2dotZfinal);
3022 REAL8 q = m1/m2;
3023 //printf("chiA = %.16f\n",chiAfinal);
3024 /* Time elapsed from the start of the dynamics to tPeakOmega */
3025 REAL8 tPeakOmegaFromStartDyn = tPeakOmega - seobdynamics->tVec[0];
3026
3027 /* Compute NQC coefficients - output is nqcCoeffs */
3028 /* NOTE: internally, XLALSimIMRSpinEOBCalculateNQCCoefficientsV4 builds a time
3029 * vector as i*deltaT, starting at 0 - thus tPeakOmega has to be measured from
3030 * the start of the dynamics */
3031
3032 /* Modes are to be generated without NQC */
3033 UINT4 includeNQC = 0;
3034 /* Loop over modes */
3035 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
3036
3037 INT4 l = modes[nmode][0];
3038 INT4 m = modes[nmode][1];
3039
3040 EOBNonQCCoeffs *nqcCoeffs = XLALMalloc(sizeof(EOBNonQCCoeffs));
3041 memset(nqcCoeffs, 0, sizeof(EOBNonQCCoeffs));
3042 /* In the equal mass equal spins case the odd-m modes are 0, so we set the NQCs to 0 */
3043 if (q<1.005 && (m % 2 != 0) && (fabs(chiAfinal) < 0.15)) { /* In this case, set NQC coeffs to 0 for odd m */
3044 nqcCoeffs->a1 = 0.;
3045 nqcCoeffs->a2 = 0.;
3046 nqcCoeffs->a3 = 0.;
3047 nqcCoeffs->a3S = 0.;
3048 nqcCoeffs->a4 = 0.;
3049 nqcCoeffs->a5 = 0.;
3050 nqcCoeffs->b1 = 0.;
3051 nqcCoeffs->b2 = 0.;
3052 nqcCoeffs->b3 = 0.;
3053 nqcCoeffs->b4 = 0.;
3054 } else {
3055
3056 /* Compute amplitude and phase of the mode hlm pre-NQC */
3057 CAmpPhaseSequence *hlm = NULL;
3058 /* Mode hlm is to be generated without NQC */
3059 includeNQC = 0;
3060 SEOBCalculatehlmAmpPhase(&hlm, l, m, seobdynamics, nqcCoeffs, seobParams,
3061 includeNQC);
3062
3063 /* Cast to amp/phase vectors */
3064 REAL8Vector *hlm_amp = NULL;
3065 REAL8Vector *hlm_phase = NULL;
3066 if (!(hlm_amp = XLALCreateREAL8Vector(hlm->xdata->length))) {
3068 "XLAL Error - %s: failed to allocate REAL8Vector hlm_amp.\n",
3069 __func__);
3071 }
3072 if (!(hlm_phase = XLALCreateREAL8Vector(hlm->xdata->length))) {
3074 "XLAL Error - %s: failed to allocate REAL8Vector hlm_phase.\n",
3075 __func__);
3077 }
3078 memcpy(hlm_amp->data, hlm->camp_real->data,
3079 hlm->xdata->length * sizeof(REAL8));
3080 memcpy(hlm_phase->data, hlm->phase->data,
3081 hlm->xdata->length * sizeof(REAL8));
3082
3083 /* Compute NQC */
3085 hlm_amp, hlm_phase, &r, &pr, &omega, l, m, tPeakOmegaFromStartDyn,
3086 deltaT, m1, m2, chiAfinal, chiSfinal,
3087 nqcCoeffs) == XLAL_FAILURE) {
3089 "XLAL Error - %s: failure for the mode (l,m) = (%d, %d).\n",
3090 __func__, l, m);
3092 }
3093
3094 /* Cleanup */
3096 XLALDestroyREAL8Vector(hlm_amp);
3097 XLALDestroyREAL8Vector(hlm_phase);
3098 }
3099
3100 /* Add computed NQCs to the list */
3101 SphHarmListEOBNonQCCoeffs_AddMode(nqcCoeffsList, nqcCoeffs, l, m);
3102 }
3103
3104 return XLAL_SUCCESS;
3105}
3106
3107/**
3108 * This function finds the index in a vector such that the value at the index
3109 * closest to an input value.
3110 * Assumes the input vector is increasing (typically, times or frequencies of
3111 * series).
3112 *
3113 */
3114
3116 REAL8Vector *vec, /**<< Input: monotonically increasing vector */
3117 REAL8 value /**<< Input: value to look for */
3118) {
3119 REAL8 *data = vec->data;
3120 UINT4 length = vec->length;
3121 UINT4 index = 0;
3122 /* Get to the last data point lower than input */
3123 while ((index < length - 1) && (data[index + 1] <= value))
3124 index++;
3125
3126 /* Check if this one or the next (first higher) is closest to input */
3127 if (index < length - 1) {
3128 if (fabs(data[index] - value) > fabs(data[index + 1] - value))
3129 index++;
3130 }
3131 return index;
3132}
3133
3134/**
3135 * This function finds the value in a vector that is closest to an input value.
3136 * Assumes the input vector is increasing (typically, times or frequencies of
3137 * series). Purely for convenience.
3138 */
3140 REAL8Vector *vec, /**<< Input: monotonically increasing vector */
3141 REAL8 value /**<< Input: value to look for */
3142) {
3143 UINT4 index = FindClosestIndex(vec, value);
3144 return vec->data[index];
3145}
3146
3148 REAL8
3149 *finalMass, /**<< Output: final mass computed from fit (scaled by M) */
3150 REAL8 *
3151 finalSpin, /**<< Output: final spin computed from fit (dimensionless) */
3152 REAL8Vector *seobvalues, /**<< Input: vector for dynamics values at time of
3153 peak of omega */
3154 SpinEOBParams *seobParams, /**<< Input: SEOB params */
3156 flagZframe /**<< Input: Whether to use the L_N or L frame */
3157) {
3158 // int debug = 0;
3159 /* Masses */
3160 REAL8 m1 = seobParams->eobParams->m1;
3161 REAL8 m2 = seobParams->eobParams->m2;
3162
3163 Approximant seobApproximant = seobParams->seobApproximant;
3164
3165 /* Compute components of vectors chi1, chi2 from seobvalues at tPeakOmega in
3166 * the L-frame */
3167
3168 REAL8Vector *chi1temp = NULL;
3169 REAL8Vector *chi2temp = NULL;
3170 if (SEOBLFrameVectors(&chi1temp, &chi2temp, seobvalues, m1, m2, flagZframe) ==
3171 XLAL_FAILURE) {
3172 XLALPrintError("XLAL Error - %s: failure in SEOBLFrameVectors.\n",
3173 __func__);
3175 }
3176
3177 REAL8 chi1L[3] = {chi1temp->data[0], chi1temp->data[1], chi1temp->data[2]};
3178 REAL8 chi2L[3] = {chi2temp->data[0], chi2temp->data[1], chi2temp->data[2]};
3179 if (XLALSimIMREOBFinalMassSpinPrec(finalMass, finalSpin, m1, m2, chi1L, chi2L,
3180 seobApproximant) == XLAL_FAILURE) {
3182 "XLAL Error - %s: failure in XLALSimIMREOBFinalMassSpinPrec.\n",
3183 __func__);
3185 }
3186
3187 XLALDestroyREAL8Vector(chi1temp);
3188 XLALDestroyREAL8Vector(chi2temp);
3189
3190 return XLAL_SUCCESS;
3191}
3192
3193/**
3194 * This function attaches the ringdown to the P-frame modes hlm.
3195 * Here seobvalues have been interpolated from seobdynamics at tPeakOmega (can
3196 * differ from tAttach).
3197 */
3200 *listhPlm_RDattached, /**<< Output: list of extended modes hlm with RD
3201 attached */
3203 *sigmaQNMlm0, /**<< Output: list of QNM complex frequency for modes lm,
3204 0th overtone (dimensionless) */
3205 INT4 modes[][2], /**<< Input: array of modes (l,m) */
3206 UINT4 nmodes, /**<< Input: number of modes (l,m) */
3207 REAL8 finalMass, /**<< Input: final mass computed from fit (scaled by M) */
3208 REAL8
3209 finalSpin, /**<< Input: final spin computed from fit (dimensionless) */
3210 SphHarmListCAmpPhaseSequence *listhPlm, /**<< Input: list of modes hlm */
3211 REAL8 deltaT, /**<< Input: time step */
3212 UINT4 retLen, /**<< Input: length of the input modes and dynamics */
3213 UINT4 retLenRDPatch, /**<< Input: length of the ringdown patch */
3214 REAL8 tAttach, /**<< Input: time of attachment */
3215 // REAL8 tStart, /**<< Input: starting time (of the HiS part) */
3216 REAL8Vector *seobvalues, /**<< Input: vector for dynamics values at time of
3217 peak of omega */
3218 SEOBdynamics *seobdynamics, /**<< Input: SEOB dynamics */
3219 SpinEOBParams *seobParams, /**<< SEOB params */
3220 const flagSEOBNRv4P_Zframe flagZframe, /**<< Input: whether to use L_N or L
3221 frame for spin projections */
3222 const INT4 debug /**<< Input: flag to print debug information */
3223) {
3224
3225 /* Check that the input list pointer and vector pointer are NULL */
3226 if (!(*listhPlm_RDattached == NULL)) {
3227 XLALPrintError("XLAL Error - %s: output pointer for the list "
3228 "hPlm_RDattached is not NULL.\n",
3229 __func__);
3231 }
3232 if (!(*sigmaQNMlm0 == NULL)) {
3233 XLALPrintError("XLAL Error - %s: output pointer for the vector sigmaQNMlm0 "
3234 "is not NULL.\n",
3235 __func__);
3237 }
3238
3239 /* Create list of output modes */
3240 UINT4 retLen_RDattached = retLen + retLenRDPatch;
3241 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
3242
3243 INT4 l = modes[nmode][0];
3244 INT4 m = modes[nmode][1];
3245
3246 CAmpPhaseSequence *hPlm_RDattached = NULL;
3247 if (CAmpPhaseSequence_Init(&hPlm_RDattached, retLen_RDattached) ==
3248 XLAL_FAILURE) {
3249 XLALPrintError("XLAL Error - %s: failed to allocate CAmpPhaseSequence "
3250 "hlm for mode (l,m) = (%d,%d).\n",
3251 __func__, l, m);
3253 }
3254
3255 SphHarmListCAmpPhaseSequence_AddMode(listhPlm_RDattached, hPlm_RDattached,
3256 l, m);
3257 }
3258
3259 /* Masses */
3260 REAL8 m1 = seobParams->eobParams->m1;
3261 REAL8 m2 = seobParams->eobParams->m2;
3262 // REAL8 eta = seobParams->eobParams->eta;
3263 REAL8 mTotal = m1 + m2;
3264 REAL8 mTScaled = mTotal * LAL_MTSUN_SI;
3265 Approximant seobApproximant = seobParams->seobApproximant;
3266
3267 /* Vector of times without attachment */
3268 REAL8Vector timeVec;
3269 timeVec.length = retLen;
3270 timeVec.data = seobdynamics->tVec;
3271 REAL8 finalM, finalS = 0;
3272
3273 REAL8Vector *chi1temp = NULL;
3274 REAL8Vector *chi2temp = NULL;
3275 SEOBLFrameVectors(&chi1temp, &chi2temp, seobvalues, m1, m2, flagZframe);
3276
3277 REAL8 chi1Lx = chi1temp->data[0];
3278 REAL8 chi1Ly = chi1temp->data[1];
3279 REAL8 chi1Lz = chi1temp->data[2];
3280 REAL8 chi2Lx = chi2temp->data[0];
3281 REAL8 chi2Ly = chi2temp->data[1];
3282 REAL8 chi2Lz = chi2temp->data[2];
3283
3284 /* finalSpin interpolation is available only between -0.9996 and 0.9996 */
3285 /* Set finalSpin to +/- 0.9996 if it is out of this range */
3286 finalS = finalSpin;
3287 finalM = finalMass;
3288
3289 if (finalS < -0.9996)
3290 finalS = -0.9996;
3291 if (finalS > 0.9996)
3292 finalS = 0.9996;
3293
3294 if (debug) {
3295 XLAL_PRINT_INFO("In RD attachment: final mass = %e, final spin = %e, "
3296 "total_mass = %e \n",
3297 finalM, finalS, mTotal);
3298 }
3299
3300 /* Compute QNM frequencies */
3301 /* Generate 1 overtone (the 0th overtone) */
3302 // NOTE: this is redone internally in XLALSimIMREOBAttachFitRingdown
3303
3304 *sigmaQNMlm0 = XLALCreateCOMPLEX16Vector(nmodes);
3305 COMPLEX16Vector sigmaQNMlm0physicalVec;
3306 sigmaQNMlm0physicalVec.length = 1;
3307 COMPLEX16 sigmaQNMlm0physicalval = 0.;
3308 sigmaQNMlm0physicalVec.data = &sigmaQNMlm0physicalval;
3309 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
3310
3311 INT4 l = modes[nmode][0];
3312 INT4 m = modes[nmode][1];
3313
3314 /* NOTE: XLALSimIMREOBGenerateQNMFreqV2Prec returns the complex frequency in
3315 * physical units... */
3316 if (XLALSimIMREOBGenerateQNMFreqV2FromFinalPrec(&sigmaQNMlm0physicalVec, m1,
3317 m2, finalM, finalS, l, m,
3318 1) == XLAL_FAILURE) {
3319 XLALPrintError("XLAL Error - %s: failure in "
3320 "XLALSimIMREOBGenerateQNMFreqV2FromFinalPrec for mode "
3321 "(l,m) = (%d,%d).\n",
3322 __func__, l, m);
3324 }
3325
3326 (*sigmaQNMlm0)->data[nmode] = mTScaled * sigmaQNMlm0physicalVec.data[0];
3327
3328 if (debug) {
3329 XLAL_PRINT_INFO("Complex QNM frequency: (%d,%d,0) = %.16e + I*%.16e\n", l,
3330 m, creal((*sigmaQNMlm0)->data[nmode]),
3331 cimag((*sigmaQNMlm0)->data[nmode]));
3332 }
3333 }
3334
3335 /* Find the time sample that is closest to tAttach */
3336 REAL8 timeNeartAttach = FindClosestValueInIncreasingVector(&timeVec, tAttach);
3337 // R.C: The attachment point is different for the 55 mode wrt the other modes
3338 // they are related by tAttach55 = tAttach -10M
3339 REAL8 timeNeartAttach55 =
3340 FindClosestValueInIncreasingVector(&timeVec, tAttach - 10.);
3341 /* This structure is inherited from the time when the attachment was done with
3342 * a comb */
3343 /* Only the value data[1] will be used -- the other two are ignored */
3344 REAL8Vector rdMatchPoint;
3345 REAL8 rdMatchPointdata[4] = {0.};
3346 rdMatchPoint.length = 3;
3347 rdMatchPoint.data = rdMatchPointdata;
3348 rdMatchPoint.data[0] = 0.; /* unused */
3349 rdMatchPoint.data[1] =
3350 timeNeartAttach; /* this is the time closest to tAttach */
3351 rdMatchPoint.data[2] = 0.; /* unused */
3352 rdMatchPoint.data[3] = timeNeartAttach55; /* tAttach55 = tAttach -10M */
3353
3354 /* Create real and imaginary part vectors for the mode with RD attached */
3355 REAL8Vector *hPlmRe = NULL;
3356 REAL8Vector *hPlmIm = NULL;
3357 if (!(hPlmRe = XLALCreateREAL8Vector(retLen_RDattached))) {
3358 XLALPrintError("XLAL Error - %s: failed to allocate REAL8Vector hPlmRe.\n",
3359 __func__);
3361 }
3362 if (!(hPlmIm = XLALCreateREAL8Vector(retLen_RDattached))) {
3363 XLALPrintError("XLAL Error - %s: failed to allocate REAL8Vector hPlmIm.\n",
3364 __func__);
3366 }
3367 memset(hPlmRe->data, 0, (hPlmRe->length) * sizeof(REAL8));
3368 memset(hPlmIm->data, 0, (hPlmIm->length) * sizeof(REAL8));
3369
3370 /* This is used to keep track of the location of the max amplitude of the 22
3371 * mode */
3372 UINT4 indAmpMax = 0;
3373
3374 /* Loop over modes */
3375 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
3376
3377 INT4 l = modes[nmode][0];
3378 INT4 m = modes[nmode][1];
3379
3380 /* Get the relevant modes in lists */
3381 CAmpPhaseSequence *hPlm =
3383 CAmpPhaseSequence *hPlm_RDattached =
3384 SphHarmListCAmpPhaseSequence_GetMode(*listhPlm_RDattached, l, m)
3385 ->campphase;
3386
3387 COMPLEX16 hPlm_val = 0.;
3388 for (UINT4 i = 0; i < retLen; i++) {
3389 hPlm_val = (hPlm->camp_real->data[i] + I * hPlm->camp_imag->data[i]) *
3390 cexp(I * hPlm->phase->data[i]);
3391 hPlmRe->data[i] = creal(hPlm_val);
3392 hPlmIm->data[i] = cimag(hPlm_val);
3393 }
3394
3395 /* NOTE: deltaT here is in physical units (s) */
3396 REAL8 deltaTseconds = deltaT * mTScaled;
3398 hPlmRe, hPlmIm, l, m, deltaTseconds, m1, m2, chi1Lx, chi1Ly, chi1Lz,
3399 chi2Lx, chi2Ly, chi2Lz, finalM, finalS, &timeVec, &rdMatchPoint,
3400 seobApproximant, &indAmpMax) == XLAL_FAILURE) {
3401 XLALPrintError("XLAL Error - %s: XLALSimIMREOBAttachFitRingdown failed "
3402 "for mode (l,m) = (%d,%d).\n",
3403 __func__, l, m);
3405 }
3406
3407 /* Copy times in output, using deltaT */
3408 for (UINT4 i = 0; i < retLen; i++) {
3409 hPlm_RDattached->xdata->data[i] = hPlm->xdata->data[i];
3410 }
3411 for (UINT4 i = 0; i < retLen_RDattached - retLen; i++) {
3412 hPlm_RDattached->xdata->data[retLen + i] =
3413 hPlm_RDattached->xdata->data[retLen - 1] + i * deltaT;
3414 }
3415
3416 /* Translate result in amp/phase, unwrap phase in place */
3417 for (UINT4 i = 0; i < retLen_RDattached; i++) {
3418 hPlm_val = hPlmRe->data[i] + I * hPlmIm->data[i];
3419 hPlm_RDattached->camp_real->data[i] = cabs(hPlm_val);
3420 hPlm_RDattached->camp_imag->data[i] = 0.;
3421 hPlm_RDattached->phase->data[i] = carg(hPlm_val);
3422 }
3423 XLALREAL8VectorUnwrapAngle(hPlm_RDattached->phase, hPlm_RDattached->phase);
3424 }
3425
3426 /* Cleanup */
3427 XLALDestroyREAL8Vector(hPlmRe);
3428 XLALDestroyREAL8Vector(hPlmIm);
3429 XLALDestroyREAL8Vector(chi1temp);
3430 XLALDestroyREAL8Vector(chi2temp);
3431 return XLAL_SUCCESS;
3432}
3433
3434/**
3435 * This function constructs the joined vector of times (AdaS+HiS+RDpatch) and
3436 * keeps the jonction indices and times RDpatch is the extension of HiS for the
3437 * RD, keeping the same constant sampling (indexJoinHiS, tJoinHiS) is given by
3438 * the first time sample >=tstartHiS (indexJoinAttach, tJoinAttach) is given by
3439 * the first time sample >=tAttach
3440 */
3442 REAL8Vector **tVecPmodes, /**<< Output: vector of times for P-modes
3443 (AdaS+HiS+RDpatch) */
3444 UINT4 *retLenPmodes, /**<< Output: length of output vector of times for
3445 P-modes */
3446 REAL8 *tJoinHiS, /**<< Output: first time >= tstartHiS */
3447 UINT4 *indexJoinHiS, /**<< Output: first index >= tstartHiS */
3448 REAL8 *tJoinAttach, /**<< Output: first time >= tAttach */
3449 UINT4 *indexJoinAttach, /**<< Output: first index >= tAttach */
3450 UINT4 retLenHiSRDpatch, /**<< Input: length of RD patch to be added at the
3451 end of HiS with the same constant sampling */
3452 REAL8 deltaTHiS, /**<< Input: time step for the high sampling */
3453 REAL8 tstartHiS, /**<< Input: time of start of HiS */
3454 REAL8 tAttach, /**<< Input: time of attachment */
3456 *seobdynamicsAdaS, /**<< Input: SEOB dynamics with adaptive-sampling */
3458 *seobdynamicsHiS /**<< Input: SEOB dynamics with high-sampling */
3459) {
3460 /* Read from inputs */
3461 INT4 lenAdaS = seobdynamicsAdaS->length;
3462 REAL8 *tAdaS = seobdynamicsAdaS->tVec;
3463 INT4 lenHiS = seobdynamicsHiS->length;
3464 REAL8 *tHiS = seobdynamicsHiS->tVec;
3465
3466 /* Determine how many time samples of AdaS to include */
3467 INT4 iJoinHiS = 0;
3468 while ((iJoinHiS < lenAdaS - 1) && (tAdaS[iJoinHiS] < tstartHiS))
3469 iJoinHiS++;
3470
3471 /* Total length of output and create output vector */
3472 INT4 lenPmodes = iJoinHiS + lenHiS + retLenHiSRDpatch;
3473 if (!((*tVecPmodes) = XLALCreateREAL8Vector(lenPmodes))) {
3475 "XLAL Error - %s: failed to allocate REAL8Vector tVecPmodes.\n",
3476 __func__);
3478 }
3479 *retLenPmodes = lenPmodes;
3480
3481 /* Copy time values for AdaS and HiS */
3482 memcpy(&((*tVecPmodes)->data[0]), tAdaS, iJoinHiS * sizeof(REAL8));
3483 memcpy(&((*tVecPmodes)->data[iJoinHiS]), tHiS, lenHiS * sizeof(REAL8));
3484
3485 /* Set time values for RD patch */
3486 for (UINT4 i = 0; i < retLenHiSRDpatch; i++) {
3487 (*tVecPmodes)->data[iJoinHiS + lenHiS + i] =
3488 tHiS[lenHiS - 1] + (i + 1) * deltaTHiS;
3489 }
3490
3491 /* Determine how many time samples after tAttach */
3492 INT4 iJoinAttach = lenPmodes - 1;
3493 while ((iJoinAttach > 0) && ((*tVecPmodes)->data[iJoinAttach] > tAttach))
3494 iJoinAttach--;
3495
3496 /* Output joining indices and times */
3497 *indexJoinHiS = iJoinHiS;
3498 *tJoinHiS = (*tVecPmodes)->data[iJoinHiS];
3499 *indexJoinAttach = iJoinAttach;
3500 *tJoinAttach = (*tVecPmodes)->data[iJoinAttach];
3501
3502 return XLAL_SUCCESS;
3503}
3504
3505/**
3506 * This function copies dynamics from AdaS<HiS and HiS<tAttach to form joined
3507 * dynamics, ending at the last time sample <tAttach
3508 */
3509// NOTE: we cut the dynamics at tAttach, as we will extend the Euler
3510// angles for t>=tAttach -- but we could also choose to finish at tPeakOmega
3511// which is used for final-J and for the final mass/spin fit
3513 SEOBdynamics *
3514 *seobdynamicsJoined, /**<< Output: pointer to joined dynamics */
3515 SEOBdynamics *seobdynamics1, /**<< Input: first dynamics */
3516 SEOBdynamics *seobdynamics2, /**<< Input: second dynamics */
3517 UINT4 indexJoin12, /**<< Input: index where to join the two dynamics */
3518 UINT4 indexEnd2 /**<< Input: index of the joined dynamics where to stop
3519 dynamics 2 (excluded) */
3520) {
3521 UINT4 j;
3522
3523 /* Lengths */
3524 INT4 lenJoined = indexEnd2;
3525 INT4 lendyn1Joined = indexJoin12;
3526 INT4 lendyn2Joined = indexEnd2 - indexJoin12;
3527 INT4 lendyn1 = seobdynamics1->length;
3528 INT4 lendyn2 = seobdynamics2->length;
3529
3530 /* Initialize output dynamics */
3531 SEOBdynamics_Init(seobdynamicsJoined, lenJoined);
3532
3533 /* Copy truncated dynamics 1 - v4PdynamicsVariables data fields */
3534 for (j = 0; j < v4PdynamicsVariables; j++) {
3535 memcpy(&((*seobdynamicsJoined)->array->data[j * lenJoined]),
3536 &(seobdynamics1->array->data[j * lendyn1]),
3537 lendyn1Joined * sizeof(REAL8));
3538 }
3539
3540 /* Copy truncated dynamics 2 - v4PdynamicsVariables data fields */
3541 for (j = 0; j < v4PdynamicsVariables; j++) {
3542 memcpy(&((*seobdynamicsJoined)->array->data[j * lenJoined + lendyn1Joined]),
3543 &(seobdynamics2->array->data[j * lendyn2]),
3544 lendyn2Joined * sizeof(REAL8));
3545 }
3546
3547 return XLAL_SUCCESS;
3548}
3549
3550/**
3551 * This function copies dynamics from AdaS<HiS and HiS<tAttach to form joined
3552 * dynamics, ending at the last time sample <tAttach
3553 */
3554// NOTE: we cut the dynamics at tAttach, as we will extend the Euler
3555// angles for t>=tAttach -- but we could also choose to finish at tPeakOmega
3556// which is used for final-J and for the final mass/spin fit
3559 *listhlm_joined, /**<< Output: list of joined modes */
3560 SphHarmListCAmpPhaseSequence *listhlm_1, /**<< Input: list of modes 1 */
3561 SphHarmListCAmpPhaseSequence *listhlm_2, /**<< Input: list of modes 2 */
3562 INT4 modes[][2], /**<< Input: array of modes (l,m) */
3563 UINT4 nmodes, /**<< Input: number of modes (l,m) */
3564 UINT4 indexJoin12 /**<< Input: index where to join the two dynamics */
3565) {
3566 /* Loop over modes */
3567 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
3568
3569 INT4 l = modes[nmode][0];
3570 INT4 m = modes[nmode][1];
3571
3572 /* Get the relevant modes in lists */
3573 CAmpPhaseSequence *hlm_1 =
3575 CAmpPhaseSequence *hlm_2 =
3577
3578 /* Lengths */
3579 UINT4 len2 = hlm_2->xdata->length;
3580 INT4 lenJoined1 = indexJoin12;
3581 INT4 lenJoined = lenJoined1 + len2;
3582
3583 /* Real and imaginary part vectors for the mode with RD attached */
3584 CAmpPhaseSequence *hlm_joined = NULL;
3585 if (CAmpPhaseSequence_Init(&hlm_joined, lenJoined) == XLAL_FAILURE) {
3586 XLALPrintError("XLAL Error - %s: failed to allocate CAmpPhaseSequence "
3587 "hlm_joined for mode (l,m) = (%d,%d).\n",
3588 __func__, l, m);
3590 }
3591
3592 /* Copy data, stopping part 1 at the specified index */
3593 memcpy(&(hlm_joined->xdata->data[0]), hlm_1->xdata->data,
3594 lenJoined1 * sizeof(REAL8));
3595 memcpy(&(hlm_joined->camp_real->data[0]), hlm_1->camp_real->data,
3596 lenJoined1 * sizeof(REAL8));
3597 memcpy(&(hlm_joined->camp_imag->data[0]), hlm_1->camp_imag->data,
3598 lenJoined1 * sizeof(REAL8));
3599 memcpy(&(hlm_joined->phase->data[0]), hlm_1->phase->data,
3600 lenJoined1 * sizeof(REAL8));
3601 /* Copy data, for part 2 starting from the specified index */
3602 memcpy(&(hlm_joined->xdata->data[lenJoined1]), hlm_2->xdata->data,
3603 len2 * sizeof(REAL8));
3604 memcpy(&(hlm_joined->camp_real->data[lenJoined1]), hlm_2->camp_real->data,
3605 len2 * sizeof(REAL8));
3606 memcpy(&(hlm_joined->camp_imag->data[lenJoined1]), hlm_2->camp_imag->data,
3607 len2 * sizeof(REAL8));
3608 memcpy(&(hlm_joined->phase->data[lenJoined1]), hlm_2->phase->data,
3609 len2 * sizeof(REAL8));
3610 /* Adjust for a 2kpi-shift in the phase - use the difference between the
3611 * last included sample of 1 and the first sample of 2 */
3612 REAL8 phase_diff =
3613 hlm_2->phase->data[0] - hlm_1->phase->data[lenJoined1 - 1];
3614 REAL8 shift_2kpi =
3615 -floor((phase_diff + LAL_PI) / (2 * LAL_PI)) * (2 * LAL_PI);
3616 for (UINT4 i = 0; i < len2; i++) {
3617 hlm_joined->phase->data[lenJoined1 + i] += shift_2kpi;
3618 }
3619
3620 SphHarmListCAmpPhaseSequence_AddMode(listhlm_joined, hlm_joined, l, m);
3621 }
3622
3623 return XLAL_SUCCESS;
3624}
3625
3626/**
3627 * This function finds the (first) index and time with the largest
3628 * sum-of-squares amplitude - discrete, no interpolation The sum-of-squares
3629 * amplitude is the sum of the amplitude square of all modes of the waveform,
3630 * rotationally invariant for any l. This function is l=2 only, requires 22
3631 * to be present and uses 21 if present.
3632 */
3634 REAL8 *tPeak, /**<< Output: time of peak */
3635 UINT4 *indexPeak, /**<< Output: index of peak */
3636 SphHarmListCAmpPhaseSequence *listhPlm, /**<< Input: list of modes hlm */
3637 INT4 modes[][2], /**<< Input: array of modes (l,m) */
3638 UINT4 nmodes, /**<< Input: number of modes (l,m) */
3639 REAL8Vector *tVec /**<< Input: vector of times */
3640) {
3641 UINT4 length = tVec->length;
3642
3643 /* Looking form modes 22 and 21 */
3644 UINT4 found22 = 0, found21 = 0;
3645 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
3646 if (modes[nmode][0] == 2 && modes[nmode][1] == 2)
3647 found22 = 1;
3648 if (modes[nmode][0] == 2 && modes[nmode][1] == 1)
3649 found21 = 1;
3650 }
3651 if ((!found22)) {
3652 XLALPrintError("XLAL Error - %s: mode 22 not found.\n", __func__);
3654 }
3655
3656 CAmpPhaseSequence *hP22 = NULL;
3657 CAmpPhaseSequence *hP21 = NULL;
3658
3659 hP22 = SphHarmListCAmpPhaseSequence_GetMode(listhPlm, 2, 2)->campphase;
3660 if (found21)
3661 hP21 = SphHarmListCAmpPhaseSequence_GetMode(listhPlm, 2, 1)->campphase;
3662
3663 /* Check lengths */
3664 if ((!(hP22->xdata->length == length)) ||
3665 (found21 && !(hP21->xdata->length == length))) {
3666 XLALPrintError("XLAL Error - %s: lengths of input amplitude and time "
3667 "REAL8Vector do not match.\n",
3668 __func__);
3670 }
3671
3672 /* Look for max combined amplitude - done discretely, no interpolation */
3673 UINT4 indexMax = 0;
3674 REAL8 AsquareMax = 0.;
3675 REAL8 A22_real = 0., A22_imag = 0., A21_real = 0., A21_imag = 0.,
3676 Asquare = 0.;
3677 for (UINT4 i = 0; i < length; i++) {
3678 A22_real = hP22->camp_real->data[i];
3679 A22_imag = hP22->camp_imag->data[i];
3680 Asquare = A22_real * A22_real + A22_imag * A22_imag;
3681 if (found21) {
3682 A21_real = hP21->camp_real->data[i];
3683 A21_imag = hP21->camp_imag->data[i];
3684 Asquare += A21_real * A21_real + A21_imag * A21_imag;
3685 }
3686 if (Asquare > AsquareMax) {
3687 AsquareMax = Asquare;
3688 indexMax = i;
3689 }
3690 }
3691
3692 /* Output */
3693 *indexPeak = indexMax;
3694 *tPeak = tVec->data[indexMax];
3695
3696 return XLAL_SUCCESS;
3697}
3698
3699/**
3700 * This function computes the Euler angles from J-frame to P-frame given the
3701 * dynamics and basis vectors of the J-frame Angle gamma computed according to
3702 * minimal rotation condition with gamma=-alpha initially Note that all
3703 * quantities in the dynamics and the basis vectors eJ are expressed in the
3704 * initial I-frame
3705 */
3707 REAL8Vector **alphaJ2P, /**<< Output: pointer to vector for alpha J2P */
3708 REAL8Vector **betaJ2P, /**<< Output: pointer to vector for beta J2P */
3709 REAL8Vector **gammaJ2P, /**<< Output: pointer to vector for gamma J2P */
3710 REAL8Vector *e1J, /**<< Input: unit Jframe vector e1J */
3711 REAL8Vector *e2J, /**<< Input: unit Jframe vector e2J */
3712 REAL8Vector *e3J, /**<< Input: unit Jframe vector e3J */
3713 UINT4 retLen, /**<< Input: total length of Euler angles data to be allocated
3714 (length of P-modes) */
3715 UINT4 indexStop, /**<< Input: index where we stop the computation (excluded,
3716 index of time of attachment) */
3717 SEOBdynamics *seobdynamics, /**<<Input: SEOB dynamics (joined AdaS+HiS, up
3718 to tAttach) */
3719 SpinEOBParams *seobParams, /**<< SEOB params */
3721 flagZframe /**<< flag to choose Z direction of the frame, LN or L */
3722) {
3723 UINT4 i, j;
3724 UINT4 dynlength = seobdynamics->length;
3725 UINT4 SpinsAlmostAligned = seobParams->alignedSpins;
3726
3727 /* Length of the subset where we want to compute the Euler angles from the
3728 * dynamics */
3729 UINT4 retLenDyn = indexStop;
3730 /* Check lengths -- note that indexStop is excluded */
3731 if (!((retLenDyn <= dynlength) && (dynlength <= retLen))) {
3732 XLALPrintError("XLAL Error - %s: incompatible lengths.\n", __func__);
3734 }
3735
3736 /* Create output vectors */
3737 if (!((*alphaJ2P) = XLALCreateREAL8Vector(retLen))) {
3739 "XLAL Error - %s: failed to allocate REAL8Vector alphaJ2P.\n",
3740 __func__);
3742 }
3743 if (!((*betaJ2P) = XLALCreateREAL8Vector(retLen))) {
3744 XLALPrintError("XLAL Error - %s: failed to allocate REAL8Vector betaJ2P.\n",
3745 __func__);
3747 }
3748 if (!((*gammaJ2P) = XLALCreateREAL8Vector(retLen))) {
3750 "XLAL Error - %s: failed to allocate REAL8Vector gammaJ2P.\n",
3751 __func__);
3753 }
3754 memset((*alphaJ2P)->data, 0, retLen * sizeof(REAL8));
3755 memset((*betaJ2P)->data, 0, retLen * sizeof(REAL8));
3756 memset((*gammaJ2P)->data, 0, retLen * sizeof(REAL8));
3757
3758 if (!SpinsAlmostAligned) /* if spins are almost aligned, leave all Euler
3759 angles to 0 */
3760 {
3761 /* Time vector of SEOB dynamics */
3762 REAL8 *tVec = seobdynamics->tVec;
3763
3764 /* Local variables */
3765 REAL8 rvec[3] = {0, 0, 0};
3766 REAL8 pvec[3] = {0, 0, 0};
3767 REAL8 rdotvec[3] = {0, 0, 0};
3768 REAL8 Lhat[3] = {0, 0, 0};
3769 REAL8 LNhat[3] = {0, 0, 0};
3770 REAL8 Zframe[3] = {0, 0, 0};
3771 REAL8 e1PiniIbasis[3] = {0, 0, 0};
3772 REAL8 e2PiniIbasis[3] = {0, 0, 0};
3773 REAL8 e3PiniIbasis[3] = {0, 0, 0};
3774 REAL8 e1PiniJbasis[3] = {0, 0, 0};
3775 REAL8 e2PiniJbasis[3] = {0, 0, 0};
3776
3777 /* Loop over time samples to compute alpha and beta, stopping at retLenDyn
3778 */
3779 for (i = 0; i < retLenDyn; i++) {
3780
3781 /* Read from the extended dynamics values */
3782 for (j = 0; j < 3; j++) {
3783 rvec[j] = seobdynamics->array->data[(1 + j) * dynlength + i];
3784 pvec[j] = seobdynamics->array->data[(4 + j) * dynlength + i];
3785 rdotvec[j] = seobdynamics->array->data[(15 + j) * dynlength + i];
3786 }
3787
3788 /* Compute Z-axis of the precessing frame, L or LN */
3789 if (flagZframe == FLAG_SEOBNRv4P_ZFRAME_L) {
3790 /* Compute Lhat */
3791 cross_product(rvec, pvec, Lhat);
3792 REAL8 Lhatnorm = sqrt(inner_product(Lhat, Lhat));
3793 for (j = 0; j < 3; j++) {
3794 Lhat[j] /= Lhatnorm;
3795 }
3796 memcpy(Zframe, Lhat, 3 * sizeof(REAL8));
3797 } else if (flagZframe == FLAG_SEOBNRv4P_ZFRAME_LN) {
3798 /* Compute LNhat */
3799 cross_product(rvec, rdotvec, LNhat);
3800 REAL8 LNhatnorm = sqrt(inner_product(LNhat, LNhat));
3801 for (j = 0; j < 3; j++) {
3802 LNhat[j] /= LNhatnorm;
3803 }
3804 memcpy(Zframe, LNhat, 3 * sizeof(REAL8));
3805 } else {
3806 XLALPrintError("XLAL Error - %s: flagZframe not recognized.\n",
3807 __func__);
3809 }
3810
3811 /* Compute Z projected in the J-frame (e1J,e2J,e3J) */
3812 REAL8 Ze1J = inner_product(Zframe, e1J->data);
3813 REAL8 Ze2J = inner_product(Zframe, e2J->data);
3814 REAL8 Ze3J = inner_product(Zframe, e3J->data);
3815
3816 /* Get Euler angles alpha (to be unwrapped later) and beta */
3817 (*alphaJ2P)->data[i] = atan2(Ze2J, Ze1J);
3818 (*betaJ2P)->data[i] = acos(Ze3J);
3819
3820 /* At initial time, compute the initial vectors (e1P, e2P, e3P) decomposed
3821 * in the frame (e1J, e2J, e3J) */
3822 /* This will be needed to set initial gamma angle */
3823 if (i == 0) {
3824 /* e3P is the Z axis of the precessing frame */
3825 memcpy(e3PiniIbasis, Zframe, 3 * sizeof(REAL8));
3826 /* e1P is the unit separation vector n */
3827 memcpy(e1PiniIbasis, rvec, 3 * sizeof(REAL8));
3828 REAL8 e1PiniIbasisnorm =
3829 sqrt(inner_product(e1PiniIbasis, e1PiniIbasis));
3830 for (j = 0; j < 3; j++) {
3831 e1PiniIbasis[j] /= e1PiniIbasisnorm;
3832 }
3833 /* e2P is obtained by completing the triad */
3834 cross_product(e3PiniIbasis, e1PiniIbasis, e2PiniIbasis);
3835 /* Components of vectors eP in the frame eJ */
3836 e1PiniJbasis[0] = inner_product(e1PiniIbasis, e1J->data);
3837 e1PiniJbasis[1] = inner_product(e1PiniIbasis, e2J->data);
3838 e1PiniJbasis[2] = inner_product(e1PiniIbasis, e3J->data);
3839 e2PiniJbasis[0] = inner_product(e2PiniIbasis, e1J->data);
3840 e2PiniJbasis[1] = inner_product(e2PiniIbasis, e2J->data);
3841 e2PiniJbasis[2] = inner_product(e2PiniIbasis, e3J->data);
3842 }
3843 }
3844
3845 /* Unwrap alpha in-place */
3846 XLALREAL8VectorUnwrapAngle((*alphaJ2P), (*alphaJ2P));
3847
3848 /* Compute gamma according to minimal rotation condition */
3849 /* gamma is set initially so that the initial P-frame reproduces the initial
3850 * (n, lambda, Lhat or LNhat) frame */
3851 REAL8 InitialGamma = atan2(e2PiniJbasis[2], -e1PiniJbasis[2]);
3852
3853 // Integrate \dot{\alpha} \cos{\beta} to get the final Euler angle
3854 // Eq. 20 of PRD 89, 084006 (2014) [arXiv:1307.6232]
3855
3856 // Here 1000 referes to the number of subintervals that can be used when
3857 // performing adaptive quadrature to compute the integral.
3858 // See
3859 // https://www.gnu.org/software/gsl/manual/html_node/QAG-adaptive-integration.html
3860 REAL8 precEulerresult = 0, precEulererror = 0;
3861 gsl_integration_workspace *precEulerw =
3862 gsl_integration_workspace_alloc(1000);
3863 gsl_function precEulerF;
3864 PrecEulerAnglesIntegration precEulerparams;
3865 gsl_spline *x_spline = gsl_spline_alloc(gsl_interp_cspline, retLenDyn);
3866 gsl_spline *y_spline = gsl_spline_alloc(gsl_interp_cspline, retLenDyn);
3867 gsl_interp_accel *x_acc = gsl_interp_accel_alloc();
3868 gsl_interp_accel *y_acc = gsl_interp_accel_alloc();
3869 gsl_spline_init(x_spline, seobdynamics->tVec, (*alphaJ2P)->data, retLenDyn);
3870 gsl_spline_init(y_spline, seobdynamics->tVec, (*betaJ2P)->data, retLenDyn);
3871
3872 precEulerparams.alpha_spline = x_spline;
3873 precEulerparams.alpha_acc = x_acc;
3874 precEulerparams.beta_spline = y_spline;
3875 precEulerparams.beta_acc = y_acc;
3876
3877 precEulerF.function = &f_alphadotcosi;
3878 precEulerF.params = &precEulerparams;
3879
3880 for (i = 0; i < retLenDyn; i++) {
3881 if (i == 0) {
3882 (*gammaJ2P)->data[i] = InitialGamma;
3883 } else {
3884 gsl_integration_qags(&precEulerF, tVec[i - 1], tVec[i], 1e-9, 1e-9,
3885 1000, precEulerw, &precEulerresult,
3886 &precEulererror);
3887 (*gammaJ2P)->data[i] = (*gammaJ2P)->data[i - 1] + precEulerresult;
3888 }
3889 }
3890 gsl_integration_workspace_free(precEulerw);
3891 gsl_spline_free(x_spline);
3892 gsl_spline_free(y_spline);
3893 gsl_interp_accel_free(x_acc);
3894 gsl_interp_accel_free(y_acc);
3895
3896 /* Unwrap gamma in-place -- note that with integration, this should not be
3897 * necessary */
3898 XLALREAL8VectorUnwrapAngle((*gammaJ2P), (*gammaJ2P));
3899 }
3900
3901 return XLAL_SUCCESS;
3902}
3903
3904/**
3905 * This function extends Euler angles J2P according to the prescription
3906 * flagEulerextension after attachment point Two prescriptions: constant angles,
3907 * or simple precession around Jfinal at rate omegaQNM220-omegaQNM210 If
3908 * SpinsAlmostAligned, all Euler angles are set to 0
3909 */
3910// NOTE: the extension is based on l=2 qualitative behaviour in NR
3911// Has not been investigated for generic l
3914 *alphaJ2P, /**<< Output: vector for alpha J2P, already allocated */
3916 *betaJ2P, /**<< Output: vector for beta J2P, already allocated */
3918 *gammaJ2P, /**<< Output: vector for gamma J2P, already allocated */
3919 COMPLEX16
3920 sigmaQNM220, /**<< Input: complex frequency for QNM 22, 0th overtone */
3921 COMPLEX16
3922 sigmaQNM210, /**<< Input: complex frequency for QNM 21, 0th overtone */
3923 REAL8Vector *tVec, /**<< Input: time vector for Euler angles data (length of
3924 P-modes) */
3925 UINT4 retLen, /**<< Input: total length of Euler angles data (length of
3926 P-modes) */
3927 UINT4 indexStart, /**<< Input: index where we start the extension (included,
3928 index of time of attachment) */
3929 SpinEOBParams *seobParams, /**<< SEOB params */
3931 flagEulerextension, /**<< flag indicating how to extend the Euler angles
3932 post-merger */
3933 INT4 flip /** << a flag of whether to flip the sign of the precession frequency
3934 */
3935) {
3936 UINT4 i;
3937 UINT4 SpinsAlmostAligned = seobParams->alignedSpins;
3938
3939 if (!SpinsAlmostAligned) {
3940
3941 /* Initial values, that were computed from the dynamics */
3942 REAL8 timeAttach = tVec->data[indexStart - 1];
3943 REAL8 alphaAttach = alphaJ2P->data[indexStart - 1];
3944 REAL8 betaAttach = betaJ2P->data[indexStart - 1];
3945 REAL8 gammaAttach = gammaJ2P->data[indexStart - 1];
3946
3947 if (flagEulerextension == FLAG_SEOBNRv4P_EULEREXT_QNM_SIMPLE_PRECESSION) {
3948 /* Precession rate around final J */
3949 REAL8 omegaQNM220 = creal(sigmaQNM220);
3950 REAL8 omegaQNM210 = creal(sigmaQNM210);
3951 REAL8 precRate = omegaQNM220 - omegaQNM210;
3952 // flip is either 1 or -1. This is needed because we want the sign of the precession frequency
3953 // to be correct even when the projected final spin is negative, at which point the QNMs
3954 // flip sign
3955 precRate *= flip;
3956 REAL8 cosbetaAttach = cos(betaAttach);
3957 for (i = indexStart; i < retLen; i++) {
3958 alphaJ2P->data[i] =
3959 alphaAttach + (tVec->data[i] - timeAttach) * precRate;
3960 betaJ2P->data[i] = betaAttach;
3961 gammaJ2P->data[i] = gammaAttach - cosbetaAttach *
3962 (tVec->data[i] - timeAttach) *
3963 precRate;
3964 }
3965 }
3966
3967 else if (flagEulerextension == FLAG_SEOBNRv4P_EULEREXT_CONSTANT) {
3968 for (i = indexStart; i < retLen; i++) {
3969 alphaJ2P->data[i] = alphaAttach;
3970 betaJ2P->data[i] = betaAttach;
3971 gammaJ2P->data[i] = gammaAttach;
3972 }
3973 }
3974
3975 else {
3976 XLALPrintError("XLAL Error - %s: flagEulerextension not recognized.\n",
3977 __func__);
3979 }
3980
3981 }
3982
3983 else /* if spins are almost aligned, set all Euler angles to 0 */
3984 {
3985 memset(&(alphaJ2P->data[indexStart]), 0,
3986 (retLen - indexStart) * sizeof(REAL8));
3987 memset(&(betaJ2P->data[indexStart]), 0,
3988 (retLen - indexStart) * sizeof(REAL8));
3989 memset(&(gammaJ2P->data[indexStart]), 0,
3990 (retLen - indexStart) * sizeof(REAL8));
3991 }
3992
3993 return XLAL_SUCCESS;
3994}
3995
3996/**
3997 * These functions compute the amplitude and phase of a Wigner coefficient
3998 * Dlmmp, given Euler angles of an active rotation.
3999 */
4000/**
4001 * Convention for Wigner matrices (mp stands for m', * for conjugation):
4002 * for the active rotation from the I-frame to the P-frame, parameterized by
4003 * Euler angles (alpha, beta, gamma) in the ZYZ convention \f[ h^{P}_{lm} =
4004 * \sum_{m'} D^{l}_{m m'} h^{I}_{lm'}\f] \f[ h^{I}_{lm} = \sum_{m'} D^{l}_{m
4005 * m'}* h^{P}_{lm'}\f] \f[ D^{l}_{m m'} = d^{l}_{m m'}(\beta) \exp{i m \alpha}
4006 * \exp{i m' \gamma}\f] with the notation \f$ c,s = \cos, \sin (\beta/2)\f$, \f$
4007 * k_{min} = \max(0,m-m'), k_{max} = \min(l+m, l-m')\f$:
4008 *
4009 * \f[d^{l}_{m m'}(\beta) = \sum_{k=k_{min}}^{k_{max}} \frac{(-1)^{k+m'-m}}{k!}
4010 * \frac{\sqrt{(l+m)!(l-m)!(l+m')!(l-m')!}}{(l+m-k)!(l-m'-k)!(k-m+m')!}
4011 * c^{2l+m-m'-2k} s^{2k-m+m'}\f]
4012 */
4014 return XLALWignerdMatrix(l, m, mp, beta);
4015}
4017 return m * alpha + mp * gamma;
4018}
4019
4020/**
4021 * This function computes the hJlm Re/Im timeseries (fixed sampling) from hPlm
4022 * amp/phase modes and Euler angles J2P (irregular sampling). The Wigner
4023 * rotation coefficients are first computed on the irregular sampling. We assume
4024 * that the hPlm and Euler angles are all given on a common time vector. P-frame
4025 * modes hPlmp should have mp>0 only We will generate the modes mp<0 using the
4026 * symmetry relation hPl-mp = (-1)^l hPlmp* All modes with l<=modes_lmax will be
4027 * created in output, and will be 0 for values of l possibly absent from
4028 * listhPlm
4029 */
4030
4032
4033 SphHarmTimeSeries **hJlm, /**<< Output: hJlm time series, will contain
4034 complex values on fixed sampling */
4035 INT4 modes[][2], /**<< Input: array of modes (l,m) */
4036 UINT4 nmodes, /**<< Input: number of modes (l,m) */
4037 INT4 modes_lmax, /**<< Input: maximum value of l in modes (l,m) */
4038 REAL8 deltaT, /**<< Input: time step for the hJlm timeseries */
4039 UINT4 retLenTS, /**<< Input: number of samples for the hJlm timeseries */
4040 REAL8Vector *tVecPmodes, /**<< Input: irregular time vector on which the
4041 hPlm and Euler angles are given */
4043 *listhPlm, /**<< Input: list of P-frame modes hPlm */
4044 REAL8Vector *alphaJ2P, /**<< Input: vector for Euler angle alpha J2P */
4045 REAL8Vector *betaJ2P, /**<< Input: vector for Euler angle beta J2P */
4046 REAL8Vector *gammaJ2P, /**<< Input: vector for Euler angle gamma J2P */
4047 UINT4 flagSymmetrizehPlminusm /**<< Input: flag indicating wether the
4048 P-frame modes m<0 are generated with the
4049 symmetry hP_l-m ~ (-1)^l hP_lm* */
4050) {
4051 UINT4 i;
4052 INT4 l, m, mp;
4053 REAL8 t = 0., camp_real = 0., camp_imag = 0., phase = 0., alpha = 0.,
4054 beta = 0., gamma = 0.;
4055 UINT4 retLenP = tVecPmodes->length;
4056
4057 /* Create output time vector */
4058 REAL8Vector *timeTS = XLALCreateREAL8Vector(retLenTS);
4059 REAL8 *timeTSdata = timeTS->data;
4060 for (i = 0; i < retLenTS; i++) {
4061 timeTSdata[i] = i * deltaT;
4062 }
4063
4064 /* Create output list of timeseries, with all (l,m) up to modes_lmax */
4065 *hJlm = NULL;
4067 char mode_string[32];
4068 for (l = 2; l <= modes_lmax; l++) {
4069 for (m = -l; m <= l; m++) {
4070 sprintf(mode_string, "H_%d%d", l, m);
4072 mode_string, &tGPS, 0., deltaT, &lalStrainUnit, retLenTS);
4073 memset(hJlm_TS->data->data, 0, retLenTS * sizeof(COMPLEX16));
4074
4075 /* Note: with the AddMode function, data is copied over */
4076 *hJlm = XLALSphHarmTimeSeriesAddMode(*hJlm, hJlm_TS, l, m);
4077
4078 /* Data has been copied over, we need to destroy */
4080 }
4081 }
4082
4083 /* Set time data */
4084 XLALSphHarmTimeSeriesSetTData(*hJlm, timeTS);
4085
4086 /* Create working space for amp/phase of Wigner coefficient */
4087 REAL8Vector *Dlmmp_amp = XLALCreateREAL8Vector(retLenP);
4088 REAL8Vector *Dlmmp_phase = XLALCreateREAL8Vector(retLenP);
4089 REAL8Vector *Dlmminusmp_amp = XLALCreateREAL8Vector(retLenP);
4090 REAL8Vector *Dlmminusmp_phase = XLALCreateREAL8Vector(retLenP);
4091 memset(Dlmmp_amp->data, 0, (Dlmmp_amp->length) * sizeof(REAL8));
4092 memset(Dlmmp_phase->data, 0, (Dlmmp_phase->length) * sizeof(REAL8));
4093 memset(Dlmminusmp_amp->data, 0, (Dlmminusmp_amp->length) * sizeof(REAL8));
4094 memset(Dlmminusmp_phase->data, 0, (Dlmminusmp_phase->length) * sizeof(REAL8));
4095
4096 /* Interpolating splines for Wigner coefficients */
4097 gsl_spline *spline_Dlmmp_amp = gsl_spline_alloc(gsl_interp_cspline, retLenP);
4098 gsl_spline *spline_Dlmmp_phase =
4099 gsl_spline_alloc(gsl_interp_cspline, retLenP);
4100 gsl_spline *spline_Dlmminusmp_amp =
4101 gsl_spline_alloc(gsl_interp_cspline, retLenP);
4102 gsl_spline *spline_Dlmminusmp_phase =
4103 gsl_spline_alloc(gsl_interp_cspline, retLenP);
4104 gsl_interp_accel *accel_Dlmmp_amp = gsl_interp_accel_alloc();
4105 gsl_interp_accel *accel_Dlmmp_phase = gsl_interp_accel_alloc();
4106 gsl_interp_accel *accel_Dlmminusmp_amp = gsl_interp_accel_alloc();
4107 gsl_interp_accel *accel_Dlmminusmp_phase = gsl_interp_accel_alloc();
4108
4109 /* Interpolating splines for hPlm modes */
4110 gsl_spline *spline_camp_real = gsl_spline_alloc(gsl_interp_cspline, retLenP);
4111 gsl_spline *spline_camp_imag = gsl_spline_alloc(gsl_interp_cspline, retLenP);
4112 gsl_spline *spline_phase = gsl_spline_alloc(gsl_interp_cspline, retLenP);
4113 gsl_interp_accel *accel_camp_real = gsl_interp_accel_alloc();
4114 gsl_interp_accel *accel_camp_imag = gsl_interp_accel_alloc();
4115 gsl_interp_accel *accel_phase = gsl_interp_accel_alloc();
4116
4117 /* Interpolate P-frame modes hPlm on the time samples needed for the output as
4118 * a time series */
4119 SphHarmListCAmpPhaseSequence *listhPlm_TS = NULL;
4120 /* Loop over modes */
4121 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
4122
4123 l = modes[nmode][0];
4124 m = modes[nmode][1];
4125
4126 CAmpPhaseSequence *hPlm =
4128
4129 CAmpPhaseSequence *hPlm_TS = NULL;
4130 if (CAmpPhaseSequence_Init(&hPlm_TS, retLenTS) == XLAL_FAILURE) {
4131 XLALPrintError("XLAL Error - %s: failure in CAmpPhaseSequence_Init for "
4132 "mode (l,m) = (%d,%d).\n",
4133 __func__, l, m);
4135 }
4136
4137 gsl_spline_init(spline_camp_real, tVecPmodes->data, hPlm->camp_real->data,
4138 retLenP);
4139 gsl_spline_init(spline_camp_imag, tVecPmodes->data, hPlm->camp_imag->data,
4140 retLenP);
4141 gsl_spline_init(spline_phase, tVecPmodes->data, hPlm->phase->data, retLenP);
4142
4143 COMPLEX16 hPlm_val = 0.;
4144 REAL8 *hPlmTS_tdata = hPlm_TS->xdata->data;
4145 REAL8 *hPlmTS_camprealdata = hPlm_TS->camp_real->data;
4146 REAL8 *hPlmTS_campimagdata = hPlm_TS->camp_imag->data;
4147 REAL8 *hPlmTS_phasedata = hPlm_TS->phase->data;
4148 for (i = 0; i < retLenTS; i++) {
4149 t = timeTSdata[i];
4150 /* Here we include a possible imaginary part of the complex envelope, but
4151 * at the moment it is simply 0 (only real part is used) */
4152 camp_real = gsl_spline_eval(spline_camp_real, t, accel_camp_real);
4153 camp_imag = gsl_spline_eval(spline_camp_imag, t, accel_camp_imag);
4154 phase = gsl_spline_eval(spline_phase, t, accel_phase);
4155 hPlm_val = (camp_real + I * camp_imag) * cexp(I * phase);
4156 /* We output the interpolated value for the mode hPlm in Re/Im form,
4157 * setting the phase to 0 */
4158 hPlmTS_tdata[i] = t;
4159 hPlmTS_camprealdata[i] = creal(hPlm_val);
4160 hPlmTS_campimagdata[i] = cimag(hPlm_val);
4161 hPlmTS_phasedata[i] = 0.;
4162 }
4163
4164 SphHarmListCAmpPhaseSequence_AddMode(&listhPlm_TS, hPlm_TS, l, m);
4165 }
4166
4167 /* Main computation */
4168 /* hJlm = \sum_mp Dlmmpstar hPlmp */
4169
4170 REAL8 *Dlmmp_amp_data = Dlmmp_amp->data;
4171 REAL8 *Dlmmp_phase_data = Dlmmp_phase->data;
4172 REAL8 *Dlmminusmp_amp_data = Dlmminusmp_amp->data;
4173 REAL8 *Dlmminusmp_phase_data = Dlmminusmp_phase->data;
4174 REAL8 *alphadata = alphaJ2P->data;
4175 REAL8 *betadata = betaJ2P->data;
4176 REAL8 *gammadata = gammaJ2P->data;
4177 COMPLEX16TimeSeries *hJlmmode = NULL;
4178 COMPLEX16 *hJlmmode_data = NULL;
4179 REAL8 *hPlmp_campreal_data;
4180 REAL8 *hPlmp_campimag_data;
4181 COMPLEX16 hPlmp_val;
4182 COMPLEX16 Dlmmp_amp_val, Dlmmp_phase_val, Dlmmp_val;
4183 COMPLEX16 Dlmminusmp_amp_val, Dlmminusmp_phase_val, Dlmminusmp_val;
4184
4185 /* Loop on l */
4186 for (l = 2; l <= modes_lmax; l++) {
4187
4188 /* Loop on m */
4189 for (m = -l; m <= l; m++) {
4190
4191 /* Get hJlm mode */
4192 hJlmmode = XLALSphHarmTimeSeriesGetMode(*hJlm, l, m);
4193 hJlmmode_data = hJlmmode->data->data;
4194
4195 /* Loop on the modes hPlmp */
4196 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
4197
4198 /* Select modes with the same l */
4199 if (modes[nmode][0] != l)
4200 continue;
4201
4202 mp = modes[nmode][1];
4203
4204 /* We do not allow mp<=0 in the P-frame modes hPlmp */
4205 /* We will use hPl-mp = (-1)^l hPlmp* */
4206 if (mp <= 0) {
4207 XLALPrintError("XLAL Error - %s: mode (l,mp) = (%d,%d) is not "
4208 "allowed as mp<=0.\n",
4209 __func__, l, mp);
4211 }
4212
4213 /* Get interpolated mode hPlmp */
4214 CAmpPhaseSequence *hPlmp_TS =
4216 hPlmp_campreal_data = hPlmp_TS->camp_real->data;
4217 hPlmp_campimag_data = hPlmp_TS->camp_imag->data;
4218
4219 /* Compute Wigner coefficients amp/phase on the same input sampling as
4220 * the P-frame modes */
4221 for (i = 0; i < retLenP; i++) {
4222 alpha = alphadata[i];
4223 beta = betadata[i];
4224 gamma = gammadata[i];
4225 Dlmmp_amp_data[i] = SEOBWignerDAmp(l, m, mp, beta);
4226 Dlmmp_phase_data[i] = SEOBWignerDPhase(m, mp, alpha, gamma);
4227 if (flagSymmetrizehPlminusm) {
4228 Dlmminusmp_amp_data[i] = SEOBWignerDAmp(l, m, -mp, beta);
4229 Dlmminusmp_phase_data[i] = SEOBWignerDPhase(m, -mp, alpha, gamma);
4230 }
4231 }
4232
4233 /* Interpolate amplitude/phase of the Wigner coefficient, add
4234 * contribution to hJlm mode */
4235 gsl_spline_init(spline_Dlmmp_amp, tVecPmodes->data, Dlmmp_amp->data,
4236 retLenP);
4237 gsl_spline_init(spline_Dlmmp_phase, tVecPmodes->data, Dlmmp_phase->data,
4238 retLenP);
4239 if (flagSymmetrizehPlminusm) {
4240 gsl_spline_init(spline_Dlmminusmp_amp, tVecPmodes->data,
4241 Dlmminusmp_amp->data, retLenP);
4242 gsl_spline_init(spline_Dlmminusmp_phase, tVecPmodes->data,
4243 Dlmminusmp_phase->data, retLenP);
4244 }
4245 for (i = 0; i < retLenTS; i++) {
4246 t = timeTSdata[i];
4247 Dlmmp_amp_val = gsl_spline_eval(spline_Dlmmp_amp, t, accel_Dlmmp_amp);
4248 Dlmmp_phase_val =
4249 gsl_spline_eval(spline_Dlmmp_phase, t, accel_Dlmmp_phase);
4250 Dlmmp_val =
4251 Dlmmp_amp_val *
4252 cexp(-I * Dlmmp_phase_val); /* mind the conjugation Dlmmpstar */
4253 hPlmp_val = hPlmp_campreal_data[i] + I * hPlmp_campimag_data[i];
4254 hJlmmode_data[i] += Dlmmp_val * hPlmp_val;
4255 if (flagSymmetrizehPlminusm) {
4256 Dlmminusmp_amp_val =
4257 gsl_spline_eval(spline_Dlmminusmp_amp, t, accel_Dlmminusmp_amp);
4258 Dlmminusmp_phase_val = gsl_spline_eval(spline_Dlmminusmp_phase, t,
4259 accel_Dlmminusmp_phase);
4260 Dlmminusmp_val =
4261 Dlmminusmp_amp_val *
4262 cexp(-I * Dlmminusmp_phase_val); /* mind the conjugation
4263 Dlmminusmpstar */
4264 hJlmmode_data[i] += Dlmminusmp_val * pow(-1, l) * conj(hPlmp_val);
4265 }
4266 }
4267 }
4268 }
4269 }
4270
4271 /* Cleanup */
4273 gsl_spline_free(spline_camp_real);
4274 gsl_spline_free(spline_camp_imag);
4275 gsl_spline_free(spline_phase);
4276 gsl_interp_accel_free(accel_camp_real);
4277 gsl_interp_accel_free(accel_camp_imag);
4278 gsl_interp_accel_free(accel_phase);
4279 gsl_spline_free(spline_Dlmmp_amp);
4280 gsl_spline_free(spline_Dlmmp_phase);
4281 gsl_spline_free(spline_Dlmminusmp_amp);
4282 gsl_spline_free(spline_Dlmminusmp_phase);
4283 gsl_interp_accel_free(accel_Dlmmp_amp);
4284 gsl_interp_accel_free(accel_Dlmmp_phase);
4285 gsl_interp_accel_free(accel_Dlmminusmp_amp);
4286 gsl_interp_accel_free(accel_Dlmminusmp_phase);
4287 XLALDestroyREAL8Vector(Dlmmp_amp);
4288 XLALDestroyREAL8Vector(Dlmmp_phase);
4289 XLALDestroyREAL8Vector(Dlmminusmp_amp);
4290 XLALDestroyREAL8Vector(Dlmminusmp_phase);
4291
4292 return XLAL_SUCCESS;
4293}
4294
4295/**
4296 * This function computes the hIlm Re/Im timeseries (fixed sampling) from hJlm
4297 * Re/Im timeseries (same sampling). This is a simple rotation,
4298 * sample-by-sample, with constant Wigner coefficients.
4299 * See the comment before SEOBWignerDAmp for explanation of conventions,
4300 * and Appendix A of Babak et al, Phys. Rev. D 95, 024010, 2017 [arXiv:1607.05661] for a general
4301 * discussion.
4302 */
4304 SphHarmTimeSeries **hIlm, /**<< Output: hIlm time series, complex values on
4305 fixed sampling */
4306 SphHarmTimeSeries *hJlm, /**<< Output: hJlm time series, complex values on
4307 fixed sampling */
4308 INT4 modes_lmax, /**<< Input: maximum value of l in modes (l,m) */
4309 REAL8 alphaI2J, /**<< Input: Euler angle alpha I->J */
4310 REAL8 betaI2J, /**<< Input: Euler angle beta I->J */
4311 REAL8 gammaI2J, /**<< Input: Euler angle gamma I->J */
4312 REAL8 deltaT /**<< Input: time step, necessary to initialize new timeseries */
4313) {
4314
4315 UINT4 i;
4316 INT4 l, m, mp;
4317 REAL8 amp_wigner = 0., phase_wigner = 0.;
4318 COMPLEX16 D_wigner = 0.;
4319 UINT4 retLen = hJlm->tdata->length;
4320 REAL8 *tJdata = hJlm->tdata->data;
4321
4322 /* Copy time vector */
4323 REAL8Vector *tI = XLALCreateREAL8Vector(retLen);
4324 memcpy(tI->data, tJdata, retLen * sizeof(REAL8));
4325
4326 /* Create output list of timeseries, with all (l,m) up to modes_lmax */
4327 *hIlm = NULL;
4329 char mode_string[32];
4330 for (l = 2; l <= modes_lmax; l++) {
4331 for (m = -l; m <= l; m++) {
4332 sprintf(mode_string, "H_%d%d", l, m);
4334 mode_string, &tGPS, 0., deltaT, &lalStrainUnit, retLen);
4335 memset(hIlm_TS->data->data, 0, retLen * sizeof(COMPLEX16));
4336
4337 /* Note: with the AddMode function, data is copied over */
4338 *hIlm = XLALSphHarmTimeSeriesAddMode(*hIlm, hIlm_TS, l, m);
4339
4340 /* Data has been copied over, we need to destroy */
4342 }
4343 }
4344
4345 /* Set time data */
4347
4348 /* Main computation */
4349 /* hIlm = \sum_mp Dlmpm hJlmp */
4350
4351 COMPLEX16TimeSeries *hIlmmode = NULL;
4352 COMPLEX16TimeSeries *hJlmpmode = NULL;
4353 COMPLEX16 *hIlmmode_data = NULL;
4354 COMPLEX16 *hJlmpmode_data = NULL;
4355
4356 /* Loop on l */
4357 for (l = 2; l <= modes_lmax; l++) {
4358
4359 /* Loop on m */
4360 for (m = -l; m <= l; m++) {
4361
4362 /* Get hJlm mode */
4363 hIlmmode = XLALSphHarmTimeSeriesGetMode(*hIlm, l, m);
4364 hIlmmode_data = hIlmmode->data->data;
4365
4366 /* Loop on mp - exclude value 0, since hPl0=0 in our approximation */
4367 for (mp = -l; mp <= l; mp++) {
4368
4369 /* Get hJlm mode */
4370 hJlmpmode = XLALSphHarmTimeSeriesGetMode(hJlm, l, mp);
4371 hJlmpmode_data = hJlmpmode->data->data;
4372
4373 /* Compute constant Wigner coefficient */
4374 amp_wigner = SEOBWignerDAmp(l, m, mp, betaI2J);
4375 phase_wigner = SEOBWignerDPhase(m, mp, alphaI2J, gammaI2J);
4376 D_wigner = amp_wigner *
4377 cexp(-I * phase_wigner); /* mind the conjugation Dlmmpstar */
4378
4379 /* Evaluate mode contribution */
4380 for (i = 0; i < retLen; i++) {
4381 hIlmmode_data[i] += D_wigner * hJlmpmode_data[i];
4382 }
4383 }
4384 }
4385 }
4386
4387 return XLAL_SUCCESS;
4388}
4389
4390/**
4391 * This function combines the modes hIlm timeseries with the sYlm to produce the
4392 * polarizations hplus, hcross.
4393 */
4394// NOTE: azimuthal angle of the observer entering the -2Ylm is pi/2-phi
4395// according to LAL conventions
4398 *hplusTS, /**<< Output: time series for hplus, already created */
4400 *hcrossTS, /**<< Output: time series for hplus, already created */
4401 INT4 modes_lmax, /**<< Input: maximum value of l */
4403 *hIlm, /**<< Input: list with time series for each mode hIlm */
4404 REAL8 amp0, /**<< Input: amplitude prefactor */
4405 REAL8 inc, /**<< Input: inclination */
4406 REAL8 phi /**<< Input: phase */
4407) {
4408 INT4 l, m;
4409
4410 /* hplus, hcross */
4411 REAL8 *hplusdata = hplusTS->data->data;
4412 memset(hplusdata, 0, hplusTS->data->length * sizeof(REAL8));
4413 REAL8 *hcrossdata = hcrossTS->data->data;
4414 memset(hcrossdata, 0, hplusTS->data->length * sizeof(REAL8));
4415 COMPLEX16 hpc_contrib = 0.;
4416
4417 /* Loop over modes */
4418 for (l = 2; l <= modes_lmax; l++) {
4419 for (m = -l; m <= l; m++) {
4420
4421 /* Compute sYlm */
4422 COMPLEX16 sYlm =
4423 XLALSpinWeightedSphericalHarmonic(inc, LAL_PI / 2. - phi, -2, l, m);
4424
4425 /* Get mode hIlm */
4427 COMPLEX16 *hIlm_data = hIlmTS->data->data;
4428
4429 for (UINT4 i = 0; i < hplusTS->data->length; i++) {
4430 hpc_contrib = sYlm * hIlm_data[i];
4431 hplusdata[i] += amp0 * creal(hpc_contrib);
4432 hcrossdata[i] += -amp0 * cimag(hpc_contrib);
4433 }
4434 }
4435 }
4436
4437 return XLAL_SUCCESS;
4438}
4439
4440/**
4441 * This function gets the number of modes present in a mode array, ignoring
4442 * modes l>lmax (and l<2) Note that m<=0 modes are also ignored and a warning
4443 * given
4444 */
4446 LALValue *modearray, /**<< Input: ModeArray structure */
4447 int lmax /**<< Input: maximum value of l to explore -- possible modes with
4448 l>lmax will be ignored */
4449) {
4450 UINT4 nmodes = 0;
4451 for (INT4 l = 2; l <= lmax; l++) {
4452 for (INT4 m = -l; m <= l; m++) {
4453 if (XLALSimInspiralModeArrayIsModeActive(modearray, l, m)) {
4454 if (m > 0)
4455 nmodes++;
4456 else {
4458 "XLAL Warning - %s: mode (l,m)=(%d,%d) present in mode array -- "
4459 "in our conventions, we only consider m>0. Mode ignored.\n",
4460 __func__, l, m);
4461 }
4462 }
4463 }
4464 }
4465 return nmodes;
4466}
4467/**
4468 * This function populates a dynamically allocated INT4 array to with the modes
4469 * active in a ModeArray Possible modes with l>lmax are ignored (as well as l<2)
4470 * Note that m<=0 modes are also ignored and a warning given
4471 */
4473 INT4 modes[][2], /**<< Output: array of dimension (nmodes,2) with mode
4474 numbers (l,m) */
4475 LALValue *modearray, /**<< Input: ModeArray structure */
4476 int lmax /**<< Input: maximum value of l to explore -- possible modes with
4477 l>lmax will be ignored */
4478) {
4479 /* Populate array */
4480 UINT4 nmode = 0;
4481 for (INT4 l = 2; l <= lmax; l++) {
4482 for (INT4 m = l; m >= -l; m--) {
4483 if (XLALSimInspiralModeArrayIsModeActive(modearray, l, m)) {
4484 if (m > 0) {
4485 modes[nmode][0] = l;
4486 modes[nmode][1] = m;
4487 nmode++;
4488 } else {
4490 "XLAL Warning - %s: mode (l,m)=(%d,%d) present in mode array -- "
4491 "in our conventions, we only consider m>0. Mode ignored.\n",
4492 __func__, l, m);
4493 }
4494 }
4495 }
4496 }
4497
4498 return XLAL_SUCCESS;
4499}
4500
4501/* Check that the ringdown frequency of the highest ell mode is less than the
4502 * Nyquist frequency */
4504 REAL8 spin2[3], UINT4 ell_max,
4505 Approximant approx, REAL8 deltaT) {
4506 UINT4 mode_highest_freqL = ell_max;
4507 UINT4 mode_highest_freqM = ell_max;
4508 /* Ringdown freq used to check the sample rate */
4509 COMPLEX16Vector modefreqVec;
4510 COMPLEX16 modeFreq;
4511 modefreqVec.length = 1;
4512 modefreqVec.data = &modeFreq;
4513
4514 if (XLALSimIMREOBGenerateQNMFreqV2Prec(&modefreqVec, m1, m2, spin1, spin2,
4515 mode_highest_freqL, mode_highest_freqM,
4516 1, approx) == XLAL_FAILURE) {
4518 }
4519
4520 if (deltaT > LAL_PI / creal(modeFreq)) {
4521 XLALPrintError("XLAL Error - %s: Ringdown frequency > Nyquist "
4522 "frequency!\nAt present this situation is not supported.\n",
4523 __func__);
4524
4526 }
4527 return XLAL_SUCCESS;
4528}
4529
4530/**
4531 * This function is the master function generating precessing spinning SEOBNRv4P
4532 * waveforms h+ and hx. Currently, only h2m harmonics will be generated.
4533 *
4534 * Input conventions:
4535 * Cartesian coordinate system: initial \f$\vec{L}_N\f$ is in the xz plane,
4536 * rotated away from the z-axis by an angle inc phiC : in radians deltaT
4537 * : in SI units (s) m1SI, m2SI : in SI units (kg) fMin : in SI units (Hz)
4538 * r : in SI units (m)
4539 * inc : in radians
4540 * INspin{1,2}: in dimensionless units of m{1,2}^2
4541 *
4542 * Evolution conventions:
4543 * values[0-2]: r vector in units of total mass
4544 * values[3-5]: pstar vector in units of reduced mass
4545 * values[6-8]: S1 vector in units of (total mass)^2
4546 * values[9-11]: S2 vector in units of (total mass)^2
4547 * values[12-13]: phases (carrier and precession (Thomas)) in rads
4548 *
4549 * Note that when the initial opening angles of the spins w.r.t. the initial
4550 * Newtonian angular momentum are small, the aligned-spin SEOBNRv4 dynamics is
4551 * used. However, the waveforms are then generated according to the SEOBNRv4P
4552 * model: for example, they include the (2,1) mode.
4553 *
4554 * STEP 0) Prepare parameters, including pre-computed coefficients for EOB
4555 * Hamiltonian, flux and waveform STEP 1) Solve for initial conditions STEP 2)
4556 * Evolve EOB trajectory with adaptive sampling (AdaS) STEP 3) Step back and
4557 * evolve EOB trajectory at high sampling rate (HiS) STEP 4) Get final
4558 * J/L/spins from HiS dynamics at peak of Omega, compute constant angles
4559 * EulerI2J STEP 5) Compute P-frame 22 mode amp/phase on HiS and compute NQC
4560 * coefficients STEP 6) Compute P-frame amp/phase for all modes on HiS, now
4561 * including NQC STEP 7) Attach RD to the P-frame waveform STEP 8) Build the
4562 * joined dynamics AdaS+HiS up to attachment, joined P-modes AdaS+HiS+RDpatch
4563 * STEP 9) Compute Euler angles J2P from AdaS and HiS dynamics up to attachment
4564 * STEP 10) Compute Euler angles J2P extension after attachment
4565 * STEP 11) Compute modes hJlm on the output time series by rotating and
4566 * interpolating the modes hPlm STEP 12) Rotate waveform from J-frame to the
4567 * output I-frame on timeseries-sampling (constant Wigner coeffs) STEP 13)
4568 * Compute hplus, hcross from I-frame waveform on timeseries sampling STEP -1)
4569 * Cleanup
4570 */
4573 *hplus, /**<< Main output: hplus GW polarization time series */
4575 *hcross, /**<< Main output: hcross GW polarization time series */
4576 SphHarmTimeSeries **hIlm, /**<< Spherical modes time series for the waveform
4577 in the initial inertial frame */
4578 SphHarmTimeSeries **hJlm, /**<< Spherical modes time series for the waveform
4579 in the final-J inertial frame */
4580 REAL8Vector **seobdynamicsAdaSVector, /**<< Vector for extended dynamics
4581 values, adaptive sampling part */
4582 REAL8Vector **seobdynamicsHiSVector, /**<< Vector for extended dynamics
4583 values, high sampling part */
4584 REAL8Vector **seobdynamicsAdaSHiSVector, /**<< Vector for extended joined
4585 dynamics values */
4586 REAL8Vector **tVecPmodes, /**<< Time vector for the P-modes */
4587 REAL8Vector **hP22_amp, /**<< Vector for the hP22 mode amplitude */
4588 REAL8Vector **hP22_phase, /**<< Vector for the hP22 mode phase */
4589 REAL8Vector **hP21_amp, /**<< Vector for the hP21 mode amplitude */
4590 REAL8Vector **hP21_phase, /**<< Vector for the hP21 mode phase */
4591 REAL8Vector **hP33_amp, /**<< Vector for the hP33 mode amplitude */
4592 REAL8Vector **hP33_phase, /**<< Vector for the hP33 mode phase */
4593 REAL8Vector **hP44_amp, /**<< Vector for the hP44 mode amplitude */
4594 REAL8Vector **hP44_phase, /**<< Vector for the hP44 mode phase */
4595 REAL8Vector **hP55_amp, /**<< Vector for the hP55 mode amplitude */
4596 REAL8Vector **hP55_phase, /**<< Vector for the hP55 mode phase */
4597 REAL8Vector **alphaJ2P, /**<< Vector for the Euler angle alphaJ2P */
4598 REAL8Vector **betaJ2P, /**<< Vector for the Euler angle betaJ2P */
4599 REAL8Vector **gammaJ2P, /**<< Vector for the Euler angle gammaJ2P */
4600 REAL8Vector **mergerParams, /**<< Parameters at merger */
4601 const REAL8 phi, /**<< Input: phase */
4602 const REAL8 INdeltaT, /**<< Input: sampling time step in SI */
4603 const REAL8 m1SI, /**<< Input: mass of first object in SI */
4604 const REAL8 m2SI, /**<< Input: mass of second object in SI */
4605 const REAL8 fMin, /**<< Input: fMin */
4606 const REAL8 r, /**<< Input: luminosity distance in SI */
4607 const REAL8 inc, /**<< Input: inclination */
4608 const REAL8 chi1x, /**<< Input: spin1 x-component, dimensionless (in units
4609 of m1^2) */
4610 const REAL8 chi1y, /**<< Input: spin1 y-component, dimensionless (in units
4611 of m1^2) */
4612 const REAL8 chi1z, /**<< Input: spin1 z-component, dimensionless (in units
4613 of m1^2) */
4614 const REAL8 chi2x, /**<< Input: spin2 x-component, dimensionless (in units
4615 of m2^2) */
4616 const REAL8 chi2y, /**<< Input: spin2 y-component, dimensionless (in units
4617 of m2^2) */
4618 const REAL8 chi2z, /**<< Input: spin2 z-component, dimensionless (in units
4619 of m2^2) */
4620 LALValue *modearray, /**<< Input: mode array for the hlm modes (m>0) to be
4621 generated in the P-frame -- has to include (2,2)
4622 mode -- modes with l larger than _SEOB_MODES_LMAX
4623 will be ignored */
4624 LALDict *seobflags /**<< Input: dictionary of SEOB flags */
4625) {
4626 /* Read waveform flags */
4627 if (!seobflags) {
4628 XLALPrintError("XLAL Error - %s: pointer to LALDict seobflags is NULL.\n",
4629 __func__);
4632 }
4633 /* Flag for the spin-aligned version of SEOB */
4634 /* Default v4 */
4635 INT4 SpinAlignedEOBversion = 0;
4636 if (!XLALDictContains(seobflags, "SEOBNRv4P_SpinAlignedEOBversion"))
4637 SpinAlignedEOBversion = 4;
4638 else
4639 SpinAlignedEOBversion =
4640 XLALDictLookupINT4Value(seobflags, "SEOBNRv4P_SpinAlignedEOBversion");
4641 /* Flag to generate P-frame modes m<0 with the symmetry hP_l-m ~ (-1)^l hP_lm*
4642 */
4643 /* Default True */
4644 INT4 flagSymmetrizehPlminusm = 0;
4645 if (!XLALDictContains(seobflags, "SEOBNRv4P_SymmetrizehPlminusm"))
4646 flagSymmetrizehPlminusm = 1;
4647 else
4648 flagSymmetrizehPlminusm =
4649 XLALDictLookupINT4Value(seobflags, "SEOBNRv4P_SymmetrizehPlminusm");
4650 /* Flag to use numerical or analytical derivatives of the Hamiltonian */
4651 /* Default numerical */
4652 // NOTE: analytical would be better if possible
4653 INT4 flagHamiltonianDerivative = 0;
4654 if (!XLALDictContains(seobflags, "SEOBNRv4P_HamiltonianDerivative"))
4655 flagHamiltonianDerivative = FLAG_SEOBNRv4P_HAMILTONIAN_DERIVATIVE_NUMERICAL;
4656 else
4657 flagHamiltonianDerivative =
4658 XLALDictLookupINT4Value(seobflags, "SEOBNRv4P_HamiltonianDerivative");
4659 /* Flag to choose the extension of Euler angles post-merger, constant or
4660 * simple precession around final J at a rate set by QNMs */
4661 /* Default QNM simple precession */
4662 INT4 flagEulerextension = 0;
4663 if (!XLALDictContains(seobflags, "SEOBNRv4P_euler_extension"))
4665 else
4666 flagEulerextension =
4667 XLALDictLookupINT4Value(seobflags, "SEOBNRv4P_euler_extension");
4668 /* Flag to choose the Z-axis of the radiation frame, L or LN */
4669 /* Default L */
4670 INT4 flagZframe = 0;
4671 if (!XLALDictContains(seobflags, "SEOBNRv4P_Zframe"))
4672 flagZframe = FLAG_SEOBNRv4P_ZFRAME_L;
4673 else
4674 flagZframe = XLALDictLookupINT4Value(seobflags, "SEOBNRv4P_Zframe");
4675 /* Flag to print debug information */
4676 /* Default False */
4677 INT4 debug = 0;
4678 if (!XLALDictContains(seobflags, "SEOBNRv4P_debug"))
4679 debug = 0;
4680 else
4681 debug = XLALDictLookupINT4Value(seobflags, "SEOBNRv4P_debug");
4682
4683 if (debug) {
4684 printf("************************************\n");
4685 printf("XLALSimIMRSpinPrecEOBWaveformAll\n");
4686 printf("************************************\n");
4687 printf("phi = %.16e\n", phi);
4688 printf("INdeltaT = %.16e\n", INdeltaT);
4689 printf("m1SI = %.16e\n", m1SI);
4690 printf("m2SI = %.16e\n", m2SI);
4691 printf("fMin = %.16e\n", fMin);
4692 printf("r = %.16e\n", r);
4693 printf("inc = %.16e\n", inc);
4694 printf("chi1x = %.16e\n", chi1x);
4695 printf("chi1y = %.16e\n", chi1y);
4696 printf("chi1z = %.16e\n", chi1z);
4697 printf("chi2x = %.16e\n", chi2x);
4698 printf("chi2y = %.16e\n", chi2y);
4699 printf("chi2z = %.16e\n", chi2z);
4700 printf("flagHamiltonianDerivative = %d\n", flagHamiltonianDerivative);
4701 printf("flagEulerextension = %d\n", flagEulerextension);
4702 printf("flagZframe = %d\n", flagZframe);
4703 }
4704
4705 /******************************************************************************************************************/
4706 /* STEP 0) Allocate memory, prepare parameters and pre-compute coeffs for EOB
4707 * Hamiltonian, flux and waveform */
4708 /******************************************************************************************************************/
4709
4710 if (debug)
4711 printf("STEP 0) Allocate memory, prepare parameters and pre-compute "
4712 "coeffs for EOB Hamiltonian, flux and waveform\n");
4713
4714 /* Check that SpinAlignedEOBversion has an authorized value: v2 or v4 */
4715 if (!((SpinAlignedEOBversion == 2) || (SpinAlignedEOBversion == 4))) {
4716 XLALPrintError("XLAL Error - %s: unauthorized SpinAlignedEOBversion %d.\n",
4717 __func__, SpinAlignedEOBversion);
4720 }
4721
4722 /* Initialize mass parameters */
4723 REAL8 m1 = m1SI / LAL_MSUN_SI;
4724 REAL8 m2 = m2SI / LAL_MSUN_SI;
4725 REAL8 mTotal = m1 + m2;
4726 REAL8 mTScaled = mTotal * LAL_MTSUN_SI;
4727 REAL8 eta = m1 * m2 / (mTotal * mTotal);
4728 if (eta > 0.25) {
4729 m2 = m1;
4730 eta = 0.25;
4731 }
4732
4733 /* Initialize amplitude factor */
4734 REAL8 amp0 = mTotal * LAL_MRSUN_SI / r;
4735
4736 /* Convert input dimensionless spin components to vectors */
4737 REAL8Vector INchi1;
4738 REAL8Vector INchi2;
4739 REAL8 INchi1data[3] = {chi1x, chi1y, chi1z};
4740 REAL8 INchi2data[3] = {chi2x, chi2y, chi2z};
4741 INchi1.length = INchi2.length = 3;
4742 INchi1.data = INchi1data;
4743 INchi2.data = INchi2data;
4744 INT4 ret = 0;
4745
4746 // Check Nyquist frequency
4747 UINT4 ellMaxForNyquistCheck = 2;
4748 UINT4 ell_max = SEOBGetLMaxInModeArray(modearray, _SEOB_MODES_LMAX);
4749
4750 // Set the max ell to use for Nyquist check
4751 // If the value is not set, simply use the largest L in the mode array
4752 // which will give 2 for SEOBNRv4P and 5 for SEOBNRv4PHM
4753 if(!XLALDictContains(seobflags,"ellMaxForNyquistCheck")){
4754 ellMaxForNyquistCheck = ell_max;
4755 }
4756 else{
4757 ellMaxForNyquistCheck = XLALDictLookupINT4Value(seobflags, "ellMaxForNyquistCheck");
4758 }
4759 if (ellMaxForNyquistCheck<2){
4760 XLALPrintError("Custom value of ell < 2 was passed to Nyquist check. This is not supported!");
4762 }
4763 if(ellMaxForNyquistCheck < ell_max){
4764 XLALPrintError("WARNING: Using ell=%d for Nyqusit check of srate, even though max ell of waveforms produced is %d\n",ellMaxForNyquistCheck,ell_max);
4765 }
4766 XLAL_TRY(XLALEOBCheckNyquistFrequency(m1, m2, INchi1.data, INchi2.data,
4767 ellMaxForNyquistCheck, SEOBNRv4P, INdeltaT),
4768 ret);
4769 if (ret != XLAL_SUCCESS) {
4771 }
4772 /* Sanity check various inputs */
4773 if (sqrt(chi1x * chi1x + chi1y * chi1y + chi1z * chi1z) > 1 ||
4774 sqrt(chi2x * chi2x + chi2y * chi2y + chi2z * chi2z) > 1) {
4775 XLALPrintError("XLAL Error - %s: Spins have magnitude > 1 !\n", __func__);
4778 }
4779 if (m1 < 0 || m2 < 0) {
4780 XLALPrintError("XLAL Error - %s: One of the masses is < 0 !\n", __func__);
4783 }
4784 if (m1 + m2 < 2) {
4785 printf("Warning: you are attempting to generate waveforms for BHs with "
4786 "total mass < 2 M_Sun. This may take a *very* long time\n");
4787 }
4788 if (m1 / m2 > 100 || m1 / m2 < 1 / 100) {
4789 XLALPrintError("XLAL Error - %s: Mass ratio is > 100 !\n", __func__);
4792 }
4793 /* If both dimensionless spin in-plane components are smaller than this
4794 * threshold, we fall back to aligned spins */
4795 REAL8 EPS_ALIGN = 1.0e-4;
4796
4797 /* Check if initial frequency is too high: we choose an initial minimum
4798 * separation of 10M as a compromise between reliability of initial conditions
4799 * and length of the waveform. We use Newtonian Kepler's law. Refuse to
4800 * generate waveforms shorter than that.
4801 */
4802 REAL8 freqMinRad = 0;
4803 /*Compute the highest initial frequency of the 22 mode */
4804 XLALEOBHighestInitialFreq(&freqMinRad, mTotal);
4805 if (fMin > freqMinRad) {
4806 XLALPrintError("XLAL Error - %s: Intitial frequency is too high, the limit "
4807 "is %4.10f \n",
4808 __func__, freqMinRad);
4810 }
4811 /* Accuracies of adaptive Runge-Kutta integrator */
4812 /* Note that this accuracies are lower than those used in SEOBNRv2: they allow
4813 * reasonable runtimes for precessing systems */
4814 /* These accuracies can be adjusted according to desired accuracy and runtime
4815 */
4816 REAL8 EPS_ABS = 1.0e-8;
4817 REAL8 EPS_REL = 1.0e-8;
4818 /* When using adaptice steps in the Runge-Kutta integrator, minimal step dt/M
4819 * In units of mTotal, introduced because steps go to 0 in some symmetric,
4820 * opposite-spin configurations */
4821 REAL8 deltaT_min = 8.0e-5;
4822
4823 /* Geometric output time step, in units of mTotal */
4824 REAL8 deltaT = INdeltaT / mTScaled;
4825
4826 /* Step-back target length of 150M from end of AdaS integration for HiS
4827 * integration */
4828 REAL8 tStepBack = 150.;
4829
4830 /* Flag to decide wether to include NQC corrections when computing the
4831 * waveform */
4832 UINT4 flagNQC = 0;
4833 /* Flag to decide wether to use adaptive sampling or constant sampling when
4834 * integrating the dynamics */
4835 UINT4 flagConstantSampling = 0;
4836
4837 /* Cast, in order of appearance */
4838 REAL8Vector *ICvalues = NULL;
4839 REAL8Array *dynamicsAdaS = NULL;
4840 SEOBdynamics *seobdynamicsAdaS = NULL;
4841 REAL8Vector *seobvalues_tstartHiS = NULL;
4842 REAL8Vector *ICvaluesHiS = NULL;
4843 REAL8Array *dynamicsHiS = NULL;
4844 SEOBdynamics *seobdynamicsHiS = NULL;
4845 REAL8Vector *seobvalues_tPeakOmega = NULL;
4846 REAL8Vector *seobvalues_test = NULL;
4847 REAL8Vector *Jfinal = NULL;
4848 REAL8Vector *Lhatfinal = NULL;
4849 REAL8Vector *chi1L_tPeakOmega = NULL;
4850 REAL8Vector *chi2L_tPeakOmega = NULL;
4851 SphHarmListEOBNonQCCoeffs *nqcCoeffsList = NULL;
4852 SphHarmListCAmpPhaseSequence *listhPlm_HiS = NULL;
4853 SphHarmListCAmpPhaseSequence *listhPlm_HiSRDpatch = NULL;
4854 SphHarmListCAmpPhaseSequence *listhPlm_AdaS = NULL;
4855 *tVecPmodes = NULL;
4856 SEOBdynamics *seobdynamicsAdaSHiS = NULL;
4857 SphHarmListCAmpPhaseSequence *listhPlm = NULL;
4858 *alphaJ2P = NULL;
4859 *betaJ2P = NULL;
4860 *gammaJ2P = NULL;
4861 *hJlm = NULL;
4862 *hIlm = NULL;
4863 REAL8TimeSeries *hplusTS = NULL;
4864 REAL8TimeSeries *hcrossTS = NULL;
4865 *mergerParams = NULL;
4866 *seobdynamicsAdaSVector = NULL;
4867 *seobdynamicsHiSVector = NULL;
4868 *seobdynamicsAdaSHiSVector = NULL;
4869
4870 /* Parameter structures */
4871 /* SpinEOBParams contains:
4872 EOBParams *eobParams; // see below
4873 SpinEOBHCoeffs *seobCoeffs; // see below
4874 EOBNonQCCoeffs *nqcCoeffs; // see below
4875 REAL8Vector *s1Vec; // spin1
4876 REAL8Vector *s2Vec; // spin2
4877 REAL8Vector *sigmaStar; // Eq. 5.3 of Barausse and Buonanno
4878 // PRD 81, 084024 (2010) [arXiv:0912.3517]
4879 REAL8Vector *sigmaKerr; // Eq. 5.2 of Barausse and Buonanno
4880 // PRD 81, 084024 (2010) [arXiv:0912.3517]
4881 REAL8 a; // |sigmaKerr|
4882 REAL8 chi1; // spin1.LNhat/m1^2
4883 REAL8 chi2; // spin2.LNhat/m2^2
4884 REAL8 prev_dr; // stores
4885 dr/dt for stopping condition purposes
4886 int alignedSpins; // flag to indicate whther the binary is precessing or not
4887 int tortoise; // flag to switch on/off tortoise coordinates when calling Hamiltonian
4888 int ignoreflux; // flag to switch off radiation reaction when calling the ODEs via
4889 XLALSpinPrecHcapNumericalDerivative or as XLALSpinPrecHcapExactDerivative
4890 */
4891 SpinEOBParams seobParams;
4892
4893 /* SpinEOBHCoeffs contains:
4894 double KK; // nonspinning calibration in Hamiltonian (for SEOBNRv2: 1.712
4895 − 1.804eta − 39:77eta^2 + 103.2eta^3)
4896 double k0; // Delta_i coefficients in the Delta_u potential Eq. 8 of
4897 // PRD 86, 024011 (2012) [arXiv:1202.0790] and https://dcc.ligo.org/T1400476
4898 double k1;
4899 double k2;
4900 double k3;
4901 double k4;
4902 double k5;
4903 double k5l;
4904 double b3; // omega_{fd}^1 frame dragging parameter in Hamiltonian
4905 (unused) double bb3; // omega_{fd}^2 frame dragging parameter in
4906 Hamiltonian (unused) double d1; // spin-orbit calibration in Hamiltonian
4907 for SEOBNRv1 double d1v2; // spin-orbit calibration in Hamiltonian for
4908 SEOBNRv1 double dheffSS; // spin-spin calibration in Hamiltonian for
4909 SEOBNRv1 double dheffSSv2; // spin-spin calibration in Hamiltonian for
4910 SEOBNRv1 UINT4 SpinAlignedEOBversion;
4911 int updateHCoeffs;
4912 */
4913 SpinEOBHCoeffs seobCoeffs;
4914
4915 /* Structure introduced for opt */
4916 SEOBHCoeffConstants seobCoeffConsts;
4917
4918 /* EOBParams contains parameters common to nonspin and spin EOBNR models,
4919 including mass ratio, masses, pre-computed coefficients for potential,
4920 flux and waveforms, NQC coefficients and Newtonian multiple prefixes */
4921 EOBParams eobParams;
4922
4923 /* Non-quasi-circular correction */
4924 EOBNonQCCoeffs nqcCoeffs;
4925
4926 /* FacWaveformCoeffscontaining the coefficients for calculating the factorized
4927 waveform. The coefficients are precomputed in the function
4928 XLALSimIMREOBCalcSpinPrecFacWaveformCoefficients */
4929 FacWaveformCoeffs hCoeffs;
4930
4931 /* NewtonMultipolePrefixes contains all the terms of the Newtonian multipole
4932 which are constant over the course of the evolution, and can therefore be
4933 pre-computed. They are stored in a two-dimensional array, which is
4934 indexed as values[l][m]. This is filled by
4935 XLALSimIMREOBComputeNewtonMultipolePrefixes */
4936 NewtonMultipolePrefixes prefixes;
4937
4938 memset(&seobParams, 0, sizeof(seobParams));
4939 memset(&seobCoeffs, 0, sizeof(seobCoeffs));
4940 memset(&seobCoeffConsts, 0, sizeof(seobCoeffConsts));
4941 memset(&eobParams, 0, sizeof(eobParams));
4942 memset(&nqcCoeffs, 0, sizeof(nqcCoeffs));
4943 memset(&hCoeffs, 0, sizeof(hCoeffs));
4944 memset(&prefixes, 0, sizeof(prefixes));
4945
4946 /* Recast the input ModeArray as a more convenient array of integers (l,m) */
4947 /* Also check that (2,2) is present in ModeArray */
4948 if (!(XLALSimInspiralModeArrayIsModeActive(modearray, 2, 2))) {
4949 FREE_ALL
4950 XLALPrintError("XLAL Error - %s: dominant harmonic (2,2) not present in "
4951 "input ModeArray -- will be needed internally.\n",
4952 __func__);
4955 }
4957 INT4 modes_lmax = SEOBGetLMaxInModeArray(modearray, _SEOB_MODES_LMAX);
4958 INT4 modes[nmodes][2];
4959 memset(modes, 0, 2 * nmodes * sizeof(INT4));
4961 if (debug) {
4962 printf("P-frame modes :\n");
4963 for (UINT4 nmode = 0; nmode < nmodes; nmode++)
4964 printf("(%d, %d)\n", modes[nmode][0], modes[nmode][1]);
4965 }
4966
4967 /* Workspace spin vectors */
4968 REAL8Vector s1Vec, s2Vec, sigmaStar, sigmaKerr;
4969 REAL8 s1Vecdata[3] = {0.};
4970 REAL8 s2Vecdata[3] = {0.};
4971 REAL8 sigmaStardata[3] = {0.};
4972 REAL8 sigmaKerrdata[3] = {0.};
4973 s1Vec.length = s2Vec.length = sigmaStar.length = sigmaKerr.length = 3;
4974 s1Vec.data = s1Vecdata;
4975 s2Vec.data = s2Vecdata;
4976 sigmaStar.data = sigmaStardata;
4977 sigmaKerr.data = sigmaKerrdata;
4978 /* s1Vec, s2Vec spins in units of mTotal square */
4979 for (UINT4 j = 0; j < 3; j++) {
4980 s1Vec.data[j] = INchi1.data[j] * m1 * m1 / mTotal / mTotal;
4981 s2Vec.data[j] = INchi2.data[j] * m2 * m2 / mTotal / mTotal;
4982 }
4983 /* Compute sigmaStar and sigmaKerr */
4984 SEOBCalculateSigmaStar(&sigmaStar, m1, m2, &s1Vec, &s2Vec);
4985 SEOBCalculateSigmaKerr(&sigmaKerr, &s1Vec, &s2Vec);
4986 /* Calculate the value of a, that is magnitude of Eq. 31 in PRD 86, 024011 [arXiv:1202.0790]
4987 * (2012) */
4988 REAL8 a = sqrt(inner_product(sigmaKerr.data, sigmaKerr.data));
4989
4990 /* Spin-EOB parameters */
4991 seobParams.a = a;
4992 seobParams.s1Vec = &s1Vec;
4993 seobParams.s2Vec = &s2Vec;
4994 seobParams.alignedSpins = 0;
4995 seobParams.tortoise = 1;
4996 seobParams.ignoreflux = 0;
4997 seobParams.sigmaStar = &sigmaStar;
4998 seobParams.sigmaKerr = &sigmaKerr;
4999 seobParams.seobCoeffs = &seobCoeffs;
5000 seobParams.seobCoeffConsts = &seobCoeffConsts;
5001 seobParams.eobParams = &eobParams;
5002 seobParams.nqcCoeffs = &nqcCoeffs;
5003 seobParams.seobApproximant = SEOBNRv4P;
5004 /* Here we store the initial LN-component of the dimensionless spins */
5005 seobParams.chi1 = chi1z;
5006 seobParams.chi2 = chi2z;
5007 /* Non-Spin-EOB parameters */
5008 eobParams.hCoeffs = &hCoeffs;
5009 eobParams.prefixes = &prefixes;
5010 seobCoeffs.SpinAlignedEOBversion = SpinAlignedEOBversion;
5011 eobParams.m1 = m1;
5012 eobParams.m2 = m2;
5013 eobParams.eta = eta;
5014
5015 TidalEOBParams tidal1, tidal2;
5016 tidal1.mByM = m1SI / (m1SI + m2SI);
5017 tidal1.lambda2Tidal = 0.;
5018 tidal1.omega02Tidal = 0.;
5019 tidal1.lambda3Tidal = 0.;
5020 tidal1.omega03Tidal = 0.;
5021 tidal1.quadparam = 1.0;
5022
5023 tidal2.mByM = m2SI / (m1SI + m2SI);
5024 tidal2.lambda2Tidal = 0.;
5025 tidal2.omega02Tidal = 0.;
5026 tidal2.lambda3Tidal = 0.;
5027 tidal2.omega03Tidal = 0.;
5028 tidal2.quadparam = 1.0;
5029
5030 seobCoeffs.tidal1 = &tidal1;
5031 seobCoeffs.tidal2 = &tidal2;
5032
5033 hCoeffs.tidal1 = &tidal1;
5034 hCoeffs.tidal2 = &tidal2;
5035
5036 /* From opt */
5037 seobCoeffConsts = XLALEOBSpinPrecCalcSEOBHCoeffConstants(eta);
5038
5039 REAL8 Lhat[3] = {0.0, 0.0, 1.0}; // Not quite true but should be very close
5040 REAL8 tempS1_p = inner_product(s1Vec.data, Lhat);
5041 REAL8 tempS2_p = inner_product(s2Vec.data, Lhat);
5042 REAL8 S1_perp[3] = {0, 0, 0};
5043 REAL8 S2_perp[3] = {0, 0, 0};
5044 for (UINT4 jj = 0; jj < 3; jj++) {
5045 S1_perp[jj] = s1Vec.data[jj] - tempS1_p * Lhat[jj];
5046 S2_perp[jj] = s2Vec.data[jj] - tempS2_p * Lhat[jj];
5047 }
5048 REAL8 sKerr_norm = sqrt(inner_product(sigmaKerr.data, sigmaKerr.data));
5049 REAL8 S_con = 0.0;
5050 if (sKerr_norm > 1e-6) {
5051 S_con = sigmaKerr.data[0] * Lhat[0] + sigmaKerr.data[1] * Lhat[1] +
5052 sigmaKerr.data[2] * Lhat[2];
5053 S_con /= (1 - 2 * eta);
5054 S_con += (inner_product(S1_perp, sigmaKerr.data) +
5055 inner_product(S2_perp, sigmaKerr.data)) /
5056 sKerr_norm / (1 - 2 * eta) / 2.;
5057 }
5058
5059 /* Pre-compute the Hamiltonian coefficients */
5061 &seobCoeffs, eta, a, S_con, SpinAlignedEOBversion) == XLAL_FAILURE) {
5062 FREE_ALL
5064 "XLAL Error: XLALSimIMRCalculateSpinPrecEOBHCoeffs_v2 failed.\n");
5067 }
5068
5069 /* Pre-compute the coefficients for the Newtonian factor of hLM
5070 Eq. A1 of PRD 86, 024011 (2012) [arXiv:1202.0790] */
5071 if (XLALSimIMREOBComputeNewtonMultipolePrefixes(&prefixes, m1, m2) ==
5072 XLAL_FAILURE) {
5073 FREE_ALL
5074 XLALPrintError("XLAL Error - %s: "
5075 "XLALSimIMREOBComputeNewtonMultipolePrefixes failed.\n",
5076 __func__);
5079 }
5080
5081 /******************************************************************************************************************/
5082 /* STEP 1) Solve for initial conditions */
5083 /******************************************************************************************************************/
5084
5085 if (debug)
5086 printf("STEP 1) Solve for initial conditions\n");
5087
5088 flagConstantSampling = 0; // Adaptive sampling by default
5089 /* Gemetric starting frequency */
5090 REAL8 MfMin = mTScaled * fMin;
5091 /* If the in-plane components of both spins are small, fall back to aligned
5092 * spins for the IC/dynamics/Euler angles */
5093 UINT4 SpinsAlmostAligned = 0;
5094 if ((sqrt(chi1x * chi1x + chi1y * chi1y) < EPS_ALIGN) &&
5095 (sqrt(chi2x * chi2x + chi2y * chi2y) < EPS_ALIGN)) {
5096 SpinsAlmostAligned = 1;
5097 INchi1.data[0] = 0.;
5098 INchi1.data[1] = 0.;
5099 INchi2.data[0] = 0.;
5100 INchi2.data[1] = 0.;
5101 // In the aligned-spin limit we now do exactly as v4
5102 EPS_REL = 1.0e-9;
5103 EPS_ABS = 1.0e-10;
5104 flagConstantSampling = 1;
5105 } else {
5106 SpinsAlmostAligned = 0;
5107 }
5108 seobParams.alignedSpins = SpinsAlmostAligned;
5109
5110 /* Compute vector for initial conditions at fMin */
5111 if (SEOBInitialConditions(&ICvalues, MfMin, m1, m2, &INchi1, &INchi2,
5112 &seobParams,
5113 flagHamiltonianDerivative) == XLAL_FAILURE) {
5114 FREE_ALL
5115 XLALPrintError("XLAL Error - %s: SEOBInitialConditions failed.\n",
5116 __func__);
5119 }
5120
5121 /******************************************************************************************************************/
5122 /* STEP 2) Evolve EOB trajectory with adaptive sampling (AdaS) */
5123 /******************************************************************************************************************/
5124
5125 if (debug)
5126 printf("STEP 2) Evolve EOB trajectory with adaptive sampling (AdaS)\n");
5127
5128 UINT4 retLenAdaS = 0;
5129 REAL8 tendAdaS =
5130 20. / mTScaled; /* This is 20s in geometric units, seems it should be
5131 ignored anyway because of integrator->stopontestonly */
5132 REAL8 tstartAdaS = 0.; /* t=0 will set at the starting time */
5133 /* Note: the timesampling step deltaT is used internally only to initialize
5134 * adaptive step */
5135 if (SEOBIntegrateDynamics(&dynamicsAdaS, &retLenAdaS, ICvalues, EPS_ABS,
5136 EPS_REL, deltaT, deltaT_min, tstartAdaS, tendAdaS,
5137 &seobParams, flagConstantSampling,
5138 flagHamiltonianDerivative) == XLAL_FAILURE) {
5139 FREE_ALL
5141 "XLAL Error - %s: SEOBIntegrateDynamicsAdaptiveSampling failed.\n",
5142 __func__);
5145 }
5146
5147 /* Compute derived quantities from the dynamics */
5149 &seobdynamicsAdaS, dynamicsAdaS, retLenAdaS, &seobParams,
5150 flagHamiltonianDerivative, flagZframe) == XLAL_FAILURE) {
5151 FREE_ALL
5152 XLALPrintError("XLAL Error - %s: SEOBComputeExtendedSEOBdynamics failed.\n",
5153 __func__);
5156 }
5157
5158 /******************************************************************************************************************/
5159 /* STEP 3) Step back and evolve EOB trajectory at high sampling rate (HiS) */
5160 /******************************************************************************************************************/
5161
5162 if (debug)
5163 printf("STEP 3) Step back and evolve EOB trajectory at high sampling rate "
5164 "(HiS)\n");
5165
5166 /* Error tolerances */
5167 if (!SpinsAlmostAligned){
5168 // For aligned spins we use the same tolerance for high-sampling
5169 // rate and for low sampling rate.
5170 EPS_ABS = 1e-8;
5171 EPS_REL = 1e-8;
5172 }
5173
5174 /* Time step for high-sampling part */
5175 REAL8 deltaTHiS = 1. / 50; /* Fixed at 1/50M */
5176
5177 /* Step-back time */
5178 /* Stepback by at least 150M wrt the ending time of the adaptive-sampling-rate
5179 * trajectory */
5180 // NOTE: this stepping back is discrete, and can jump by one sample of AdaS
5181 // when changing continuously parameters
5182 REAL8 tstartHiSTarget = seobdynamicsAdaS->tVec[retLenAdaS - 1] - tStepBack;
5183 INT4 indexstartHiS = retLenAdaS - 1; /* index for the AdaS dynamics */
5184 while ((indexstartHiS > 0) &&
5185 (seobdynamicsAdaS->tVec[indexstartHiS] > tstartHiSTarget))
5186 indexstartHiS--;
5187 REAL8 tstartHiS = seobdynamicsAdaS->tVec[indexstartHiS];
5188
5189 /* Compute the new initial conditions for high-sampling integration by
5190 * interpolating adaptive-sampling dynamics */
5191 /* Since we take values at a sample, no interpolation actually needed - but
5192 * this is flexible if we change the prescription for stepback */
5193 if (SEOBInterpolateDynamicsAtTime(&seobvalues_tstartHiS, tstartHiS,
5194 seobdynamicsAdaS) == XLAL_FAILURE) {
5195 FREE_ALL
5196 XLALPrintError("XLAL Error - %s: SEOBInterpolateDynamicsAtTime failed.\n",
5197 __func__);
5200 }
5201 if (!(ICvaluesHiS = XLALCreateREAL8Vector(14))) {
5203 "XLAL Error - %s: failed to allocate REAL8Vector ICvaluesHiS.\n",
5204 __func__);
5206 }
5207 memcpy(ICvaluesHiS->data, &(seobvalues_tstartHiS->data[1]),
5208 14 * sizeof(REAL8));
5209
5210 /* Integrate again the dynamics with a constant high sampling rate */
5211 seobParams.prev_dr = 0.; // This is used to check whether dr/dt is increasing
5212 // in the stopping condition
5213 seobParams.termination_reason =
5214 -999; // This is going to store the termination condition for the
5215 // high-sampling integration
5216 UINT4 retLenHiS = 0;
5217 REAL8 tendHiS =
5218 tstartHiS + tStepBack; /* Seems it should be ignored anyway because of
5219 integrator->stopontestonly */
5220 flagConstantSampling = 1;
5221 /* Note: here deltaT_min = 0. will simply be ignored, we use fixed steps */
5222 if (SEOBIntegrateDynamics(&dynamicsHiS, &retLenHiS, ICvaluesHiS, EPS_ABS,
5223 EPS_REL, deltaTHiS, 0., tstartHiS, tendHiS,
5224 &seobParams, flagConstantSampling,
5225 flagHamiltonianDerivative) == XLAL_FAILURE) {
5226 FREE_ALL
5228 "XLAL Error - %s: SEOBIntegrateDynamicsConstantSampling failed.\n",
5229 __func__);
5232 }
5233
5234 /* Compute derived quantities for the high-sampling dynamics */
5235 if (SEOBComputeExtendedSEOBdynamics(&seobdynamicsHiS, dynamicsHiS, retLenHiS,
5236 &seobParams, flagHamiltonianDerivative,
5237 flagZframe) == XLAL_FAILURE) {
5238 FREE_ALL
5239 XLALPrintError("XLAL Error - %s: SEOBComputeExtendedSEOBdynamics failed.\n",
5240 __func__);
5243 }
5244 /* Find time of peak of omega for the High-sampling dynamics */
5245 INT4 foundPeakOmega = 0;
5246 REAL8 tPeakOmega = 0.;
5247 SEOBLocateTimePeakOmega(&tPeakOmega, &foundPeakOmega, dynamicsHiS,
5248 seobdynamicsHiS, retLenHiS, &seobParams,
5249 flagHamiltonianDerivative);
5250
5251 /******************************************************************************************************************/
5252 /* STEP 4) Get final J/L/spins from HiS dynamics at peak of Omega, compute
5253 * constant angles EulerI2J */
5254 /******************************************************************************************************************/
5255
5256 if (debug)
5257 printf("STEP 4) Get final J/L/spins from HiS dynamics at peak of Omega, "
5258 "compute constant angles EulerI2J\n");
5259
5260 /* Compute final dynamics quantities, interpolating HiS dynamics at tPeakOmega
5261 */
5262
5263 if (SEOBInterpolateDynamicsAtTime(&seobvalues_tPeakOmega, tPeakOmega,
5264 seobdynamicsHiS) == XLAL_FAILURE) {
5265 FREE_ALL
5266 XLALPrintError("XLAL Error - %s: SEOBInterpolateDynamicsAtTime failed at "
5267 "tPeakOmega.\n",
5268 __func__);
5270 }
5271
5272 /* Interpolate the dynamics to r=10M. This is used as a
5273 feducial point to evaluate the final mass and spin fits */
5274 REAL8Vector timeVec;
5275 timeVec.length = retLenAdaS;
5276 timeVec.data = seobdynamicsAdaS->tVec;
5277 REAL8Vector* rVec = XLALCreateREAL8Vector(retLenAdaS);
5278 for (UINT4 jj = 0; jj < retLenAdaS; jj++) {
5279 rVec->data[jj] = -1* seobdynamicsAdaS->polarrVec[jj];
5280 }
5281 /*
5282 // Uncomment this to restore behaviour where we used the Keplerian frequency
5283 corresponding
5284 // to r=10M instead of r itself.
5285 UNUSED REAL8Vector omegaVec;
5286 omegaVec.length = retLenAdaS;
5287 omegaVec.data = seobdynamicsAdaS->omegaVec;
5288 UNUSED REAL8 omega_10M = pow(10.0, -1.5); // Keplerian value of omega at r=10M
5289 UINT4 index_10M = FindClosestIndex(&omegaVec, omega_10M);
5290 */
5291
5292 // FindClosestIndex requires an *increasing* function of time, so we use -r
5293 // instead of r
5294 UINT4 index_10M = FindClosestIndex(rVec, -10.0);
5295 REAL8 time_10M = timeVec.data[index_10M];
5296 if (SEOBInterpolateDynamicsAtTime(&seobvalues_test, time_10M,
5297 seobdynamicsAdaS) == XLAL_FAILURE) {
5298 FREE_ALL
5300 "XLAL Error - %s: SEOBInterpolateDynamicsAtTime failed at time_10M.\n",
5301 __func__);
5303 }
5304
5305 /* Compute the timeshift to get the attachment point */
5306 SEOBLFrameVectors(&chi1L_tPeakOmega, &chi2L_tPeakOmega, seobvalues_tPeakOmega,
5307 m1, m2, flagZframe);
5309 2, 2, m1, m2, chi1L_tPeakOmega->data[2], chi2L_tPeakOmega->data[2]);
5310
5311 /* Determine the time of attachment */
5312 REAL8 tAttach = tPeakOmega - Deltat22;
5313
5314 /* Compute final J from dynamics quantities */
5315 SEOBJfromDynamics(&Jfinal, seobvalues_tPeakOmega, &seobParams);
5316 /*Compute the L-hat vector. Note that it has unit norm */
5317 SEOBLhatfromDynamics(&Lhatfinal, seobvalues_tPeakOmega, &seobParams, flagZframe);
5318 REAL8 Jmag = sqrt(inner_product(Jfinal->data, Jfinal->data));
5319 /* Cosine of the angle between L-hat and J. Needed to determine
5320 * the correct sign of the final spin
5321 */
5322 REAL8 cos_angle = inner_product(Jfinal->data, Lhatfinal->data) / Jmag;
5323 /* Compute final-J-frame unit vectors e1J, e2J, e3J=Jfinalhat */
5324 /* Convention: if (ex, ey, ez) is the initial I-frame, e1J chosen such that ex
5325 * is in the plane (e1J, e3J) and ex.e1J>0 */
5326 REAL8Vector e1J, e2J, e3J;
5327 e1J.length = e2J.length = e3J.length = 3;
5328 REAL8 e1Jdata[3] = {0.};
5329 REAL8 e2Jdata[3] = {0.};
5330 REAL8 e3Jdata[3] = {0.};
5331 e1J.data = e1Jdata;
5332 e2J.data = e2Jdata;
5333 e3J.data = e3Jdata;
5334 SEOBBuildJframeVectors(&e1J, &e2J, &e3J, Jfinal);
5335
5336 /* Compute Euler angles from initial I-frame to final-J-frame */
5337 /* Note: if spins are aligned, the function SEOBEulerI2JFromJframeVectors */
5338 /* becomes ill-defined - just keep these Euler angles to zero then */
5339 REAL8 alphaI2J = 0., betaI2J = 0., gammaI2J = 0.;
5340 if (!SpinsAlmostAligned) {
5341 SEOBEulerI2JFromJframeVectors(&alphaI2J, &betaI2J, &gammaI2J, &e1J, &e2J,
5342 &e3J);
5343 }
5344
5345 /******************************************************************************************************************/
5346 /* STEP 5) Compute P-frame amp/phase for all modes on HiS and compute NQC
5347 * coefficients */
5348 /******************************************************************************************************************/
5349
5350 if (debug)
5351 printf("STEP 5) Compute P-frame of modes amp/phase on HiS and compute NQC "
5352 "coefficients\n");
5353
5355 &nqcCoeffsList, modes, nmodes, tPeakOmega, seobdynamicsHiS,
5356 &seobParams, chi1L_tPeakOmega, chi2L_tPeakOmega) == XLAL_FAILURE) {
5357 FREE_ALL
5358 XLALPrintError("XLAL Error - %s: NQC computation failed.\n", __func__);
5361 }
5362
5363 /******************************************************************************************************************/
5364 /* STEP 6) Compute P-frame amp/phase for all modes on HiS, now including NQC
5365 */
5366 /******************************************************************************************************************/
5367
5368 if (debug)
5369 printf("STEP 6) Compute P-frame amp/phase on HiS, now including NQC\n");
5370
5371 /* We now include the NQC when generating modes */
5372 flagNQC = 1;
5373
5374 /* Compute amplitude and phase of the P-frame modes hPlm on high sampling,
5375 * with NQC */
5376 SEOBCalculateSphHarmListhlmAmpPhase(&listhPlm_HiS, modes, nmodes,
5377 seobdynamicsHiS, nqcCoeffsList,
5378 &seobParams, flagNQC);
5379
5380 /******************************************************************************************************************/
5381 /* STEP 7) Attach RD to the P-frame waveform */
5382 /******************************************************************************************************************/
5383
5384 if (debug)
5385 printf("STEP 7) Attach RD to the P-frame waveform\n");
5386
5387 /* Compute the final mass and spins here and pass them on */
5388 REAL8 finalMass = 0., finalSpin = 0.;
5389 if (SEOBGetFinalSpinMass(&finalMass, &finalSpin, seobvalues_test, &seobParams,
5390 flagZframe)) {
5391 FREE_ALL
5392 XLALPrintError("XLAL Error - %s: SEOBGetFinalSpinMass failed.\n", __func__);
5395 }
5396
5397 /* The function above returns only the magnitude of the spin.
5398 * We pick the direction based on whether Lhat \cdot J is positive
5399 * or negative */
5400 if (cos_angle < 0)
5401 {
5402 finalSpin *= -1;
5403 }
5404 /* finalSpin interpolation is available only between -0.9996 and 0.9996 */
5405 /* Set finalSpin to +/- 0.9996 if it is out of this range */
5406 if (finalSpin < -0.9996)
5407 finalSpin = -0.9996;
5408 if (finalSpin > 0.9996)
5409 finalSpin = 0.9996;
5410
5411 if (debug) {
5412 XLAL_PRINT_INFO("final mass = %e, final spin = %e\n", finalMass, finalSpin);
5413 }
5414
5415 /* Estimate leading QNM , to determine how long the ringdown
5416 * patch will be */
5417 /* NOTE: XLALSimIMREOBGenerateQNMFreqV2Prec returns the complex frequency in
5418 * physical units... */
5419 COMPLEX16Vector sigmaQNM220estimatephysicalVec;
5420 COMPLEX16 sigmaQNM220estimatephysical = 0.;
5421 sigmaQNM220estimatephysicalVec.length = 1;
5422 sigmaQNM220estimatephysicalVec.data = &sigmaQNM220estimatephysical;
5423 if (XLALSimIMREOBGenerateQNMFreqV2FromFinalPrec(&sigmaQNM220estimatephysicalVec, m1,
5424 m2, finalMass, finalSpin, 2,
5425 2, 1) == XLAL_FAILURE) {
5426 FREE_ALL
5428 "XLAL Error - %s: failure in "
5429 "XLALSimIMREOBGenerateQNMFreqV2FromFinalPrec for mode (l,m) = (2,2).\n",
5430 __func__);
5432 }
5433 COMPLEX16 sigmaQNM220estimate = mTScaled * sigmaQNM220estimatephysical;
5434
5435 /* Length of RD patch, 40 e-folds of decay of the estimated QNM220 */
5436 UINT4 retLenRDPatch =
5437 (UINT4)ceil(40 / (cimag(sigmaQNM220estimate) * deltaTHiS));
5438
5439
5440 /* Attach RD to the P-frame modes */
5441 /* Vector holding the values of the 0-th overtone QNM complex frequencies for
5442 * the modes (l,m) */
5443 COMPLEX16Vector *sigmaQNMlm0 = NULL;
5444 // NOTE: the QNM complex frequencies are computed inside
5445 // SEOBAttachRDToSphHarmListhPlm
5447 &listhPlm_HiSRDpatch, &sigmaQNMlm0, modes, nmodes, finalMass, finalSpin,
5448 listhPlm_HiS, deltaTHiS, retLenHiS, retLenRDPatch, tAttach,
5449 seobvalues_tPeakOmega, seobdynamicsHiS, &seobParams, flagZframe, debug);
5450
5451 /******************************************************************************************************************/
5452 /* STEP 8) Build the joined dynamics AdaS+HiS up to attachment, joined P-modes
5453 * AdaS+HiS+RDpatch */
5454 /******************************************************************************************************************/
5455
5456 if (debug)
5457 printf("STEP 8) Build the joined dynamics AdaS+HiS up to attachment, "
5458 "joined P-modes AdaS+HiS+RDpatch\n");
5459
5460 /* Compute amplitude and phase of the P-frame modes hPlm on adaptive sampling,
5461 * with NQC */
5462 flagNQC = 1;
5463 SEOBCalculateSphHarmListhlmAmpPhase(&listhPlm_AdaS, modes, nmodes,
5464 seobdynamicsAdaS, nqcCoeffsList,
5465 &seobParams, flagNQC);
5466
5467 /* Vector of times for the P-modes, that will be used for interpolation:
5468 * joining AdaS and HiS+RDpatch */
5469 UINT4 retLenPmodes = 0;
5470 /* First junction at indexAdaSHiS, tAdaSHiS */
5471 UINT4 indexJoinHiS = 0;
5472 REAL8 tJoinHiS = 0.;
5473 /* Second junction at seobdynamicsAdaS, tJoinAttach */
5474 UINT4 indexJoinAttach = 0;
5475 REAL8 tJoinAttach = 0.;
5476 /* Construct the joined vector of times (AdaS+HiS+RDpatch) and keep the
5477 * jonction indices and times */
5478 SEOBJoinTimeVector(tVecPmodes, &retLenPmodes, &tJoinHiS, &indexJoinHiS,
5479 &tJoinAttach, &indexJoinAttach, retLenRDPatch, deltaTHiS,
5480 tstartHiS, tAttach, seobdynamicsAdaS, seobdynamicsHiS);
5481
5482 /* Copy dynamics from AdaS<HiS and HiS<tAttach to form joined dynamics, ending
5483 * at the last time sample <tAttach */
5484 // NOTE: we cut the dynamics at tAttach, as we will extend the Euler
5485 // angles for t>=tAttach -- but we could also choose to finish at tPeakOmega
5486 // which is used for final-J and for the final mass/spin fit
5487 SEOBJoinDynamics(&seobdynamicsAdaSHiS, seobdynamicsAdaS, seobdynamicsHiS,
5488 indexJoinHiS, indexJoinAttach);
5489
5490 /* Copy waveform modes from AdaS and HiS+RDpatch - adjusting 2pi-phase shift
5491 * at the junction point AdaS/HiS */
5492 SEOBJoinSphHarmListhlm(&listhPlm, listhPlm_AdaS, listhPlm_HiSRDpatch, modes,
5493 nmodes, indexstartHiS);
5494
5495 /* Get the time of the frame-invariant amplitude peak */
5496 REAL8 tPeak = 0;
5497 UINT4 indexPeak = 0;
5498 // NOTE: peak amplitude using l=2 only: h22 required, and h21 used if present
5499 SEOBAmplitudePeakFromAmp22Amp21(&tPeak, &indexPeak, listhPlm, modes, nmodes,
5500 *tVecPmodes);
5501
5502 /******************************************************************************************************************/
5503 /* STEP 9) Compute Euler angles J2P from AdaS and HiS dynamics up to
5504 * attachment */
5505 /******************************************************************************************************************/
5506
5507 if (debug)
5508 printf("STEP 9) Compute Euler angles J2P from AdaS and HiS dynamics up to "
5509 "attachment\n");
5510
5511 /* Compute Euler angles J2P from the dynamics before attachment point */
5512 /* If SpinsAlmostAligned, all Euler angles are set to 0 */
5513 if (SEOBEulerJ2PFromDynamics(alphaJ2P, betaJ2P, gammaJ2P, &e1J, &e2J, &e3J,
5514 retLenPmodes, indexJoinAttach,
5515 seobdynamicsAdaSHiS, &seobParams,
5516 flagZframe) == XLAL_FAILURE) {
5517 FREE_ALL
5518 XLALPrintError("XLAL Error - %s: SEOBEulerJ2PFromDynamics failed.\n",
5519 __func__);
5522 }
5523
5524 /******************************************************************************************************************/
5525 /* STEP 10) Compute Euler angles J2P extension after attachment */
5526 /******************************************************************************************************************/
5527
5528 if (debug)
5529 printf("STEP 10) Compute Euler angles J2P extension after attachment\n");
5530
5531 /* Compute Euler angles J2P according to the prescription flagEulerextension
5532 * after attachment point */
5533 /* If SpinsAlmostAligned, all Euler angles are set to 0 */
5534 /* NOTE: Regardless of the mode content of hPlm, the frame extension at the
5535 * moment is based on sigmaQNM22, sigmaQNM21 */
5536 COMPLEX16 sigmaQNM220 = 0., sigmaQNM210 = 0.;
5537 COMPLEX16Vector sigmaQNM220physicalVec, sigmaQNM210physicalVec;
5538 sigmaQNM220physicalVec.length = 1;
5539 sigmaQNM210physicalVec.length = 1;
5540 COMPLEX16 sigmaQNM220physicalval = 0.;
5541 COMPLEX16 sigmaQNM210physicalval = 0.;
5542 sigmaQNM220physicalVec.data = &sigmaQNM220physicalval;
5543 sigmaQNM210physicalVec.data = &sigmaQNM210physicalval;
5544 /* NOTE: XLALSimIMREOBGenerateQNMFreqV2Prec returns the complex frequency in
5545 * physical units... */
5546 if (XLALSimIMREOBGenerateQNMFreqV2FromFinalPrec(&sigmaQNM220physicalVec, m1,
5547 m2, finalMass, finalSpin, 2,
5548 2, 1) == XLAL_FAILURE) {
5549 FREE_ALL
5551 "XLAL Error - %s: failure in "
5552 "XLALSimIMREOBGenerateQNMFreqV2FromFinalPrec for mode (l,m) = (2,2).\n",
5553 __func__);
5555 }
5556 if (XLALSimIMREOBGenerateQNMFreqV2FromFinalPrec(&sigmaQNM210physicalVec, m1,
5557 m2, finalMass, finalSpin, 2,
5558 1, 1) == XLAL_FAILURE) {
5559 FREE_ALL
5561 "XLAL Error - %s: failure in "
5562 "XLALSimIMREOBGenerateQNMFreqV2FromFinalPrec for mode (l,m) = (2,1).\n",
5563 __func__);
5565 }
5566 sigmaQNM220 = mTScaled * sigmaQNM220physicalVec.data[0];
5567 sigmaQNM210 = mTScaled * sigmaQNM210physicalVec.data[0];
5568 INT4 flip = 1;
5569 if (cos_angle < 0){
5570 flip = -1;
5571 }
5573 *alphaJ2P, *betaJ2P, *gammaJ2P, sigmaQNM220, sigmaQNM210, *tVecPmodes,
5574 retLenPmodes, indexJoinAttach, &seobParams, flagEulerextension, flip);
5575
5576 /******************************************************************************************************************/
5577 /* STEP 11) Compute modes hJlm on the output time series by rotating and
5578 * interpolating the modes hPlm */
5579 /******************************************************************************************************************/
5580
5581 if (debug)
5582 printf("STEP 11) Compute modes hJlm on the output time series by rotating "
5583 "and interpolating the modes hPlm\n");
5584
5585 /* Determine the length of the fixed-sampling output time series */
5586 UINT4 retLenTS =
5587 floor(((*tVecPmodes)->data[retLenPmodes - 1] - (*tVecPmodes)->data[0]) /
5588 deltaT);
5589
5590 /* Rotate waveform from P-frame to J-frame */
5592 hJlm, modes, nmodes, modes_lmax, deltaT, retLenTS, *tVecPmodes,
5593 listhPlm, *alphaJ2P, *betaJ2P, *gammaJ2P,
5594 flagSymmetrizehPlminusm) == XLAL_FAILURE) {
5595 FREE_ALL
5597 "XLAL Error - %s: failure in "
5598 "SEOBRotateInterpolatehJlmReImFromSphHarmListhPlmAmpPhase.\n",
5599 __func__);
5601 }
5602
5603 /******************************************************************************************************************/
5604 /* STEP 12) Rotate waveform from J-frame to the output I-frame on
5605 * timeseries-sampling (constant Wigner coeffs) */
5606 /******************************************************************************************************************/
5607
5608 if (debug)
5609 printf("STEP 12) Rotate waveform from J-frame to the output I-frame on "
5610 "timeseries-sampling (constant Wigner coeffs)\n");
5611
5612 /* Rotate waveform from J-frame to I-frame */
5613 SEOBRotatehIlmFromhJlm(hIlm, *hJlm, modes_lmax, alphaI2J, betaI2J, gammaI2J,
5614 deltaT);
5615
5616 /******************************************************************************************************************/
5617 /* STEP 13) Compute hplus, hcross from I-frame waveform on timeseries sampling
5618 */
5619 /******************************************************************************************************************/
5620
5621 if (debug)
5622 printf("STEP 13) Compute hplus, hcross from I-frame waveform on timeseries "
5623 "sampling\n");
5624
5625 /* GPS time for output time series and modes */
5627 XLALGPSAdd(&tGPS, -mTScaled *
5628 tPeak); /* tPeak converted back to dimensionfull time */
5629
5630 /* Create output timeseries for hplus, hcross */
5631 /* Use the dimensionfull INdeltaT (s) as time step */
5632 hplusTS = XLALCreateREAL8TimeSeries("H_PLUS", &tGPS, 0.0, INdeltaT,
5633 &lalStrainUnit, retLenTS);
5634 hcrossTS = XLALCreateREAL8TimeSeries("H_CROSS", &tGPS, 0.0, INdeltaT,
5635 &lalStrainUnit, retLenTS);
5636
5637 /* Compute hplus, hcross from hIlm */
5638 // NOTE: azimuthal angle of the observer entering the -2Ylm is pi/2-phi
5639 // according to LAL conventions
5640 SEOBComputehplushcrossFromhIlm(hplusTS, hcrossTS, modes_lmax, *hIlm, amp0,
5641 inc, phi);
5642
5643 /******************************************************************************************************************/
5644 /* STEP -1) Output and cleanup */
5645 /******************************************************************************************************************/
5646
5647 if (debug)
5648 printf("STEP -1) Output and cleanup\n");
5649
5650 /* Output vector gathering quantities related to merger (similar to previous
5651 * AttachParams) */
5652 /* Format: tPeakOmega tAttach tPeak Jfinalx Jfinaly Jfinalz finalMassfit
5653 * finalSpinfit termination_reason [sigmaQNMlm0Re sigmaQNMlm0Im for lm in
5654 * modes] */
5655 /* NOTE: the size of this output vector depends on the number of modes, due to
5656 * the sigmaQNM */
5657 if (!((*mergerParams) = XLALCreateREAL8Vector(9 + 2 * nmodes))) {
5659 "XLAL Error - %s: failed to allocate REAL8Vector mergerParams.\n",
5660 __func__);
5662 }
5663 (*mergerParams)->data[0] = tPeakOmega;
5664 (*mergerParams)->data[1] = tAttach;
5665 (*mergerParams)->data[2] = tPeak;
5666 (*mergerParams)->data[3] = Jfinal->data[0];
5667 (*mergerParams)->data[4] = Jfinal->data[1];
5668 (*mergerParams)->data[5] = Jfinal->data[2];
5669 (*mergerParams)->data[6] = finalMass;
5670 (*mergerParams)->data[7] = finalSpin;
5671 (*mergerParams)->data[8] = seobParams.termination_reason;
5672 for (UINT4 nmode = 0; nmode < nmodes; nmode++) {
5673 (*mergerParams)->data[9 + 2 * nmode] = creal(sigmaQNMlm0->data[nmode]);
5674 (*mergerParams)->data[9 + 2 * nmode + 1] = cimag(sigmaQNMlm0->data[nmode]);
5675 }
5676
5677 /* Point the output pointers to the relevant time series and return */
5678 (*hplus) = hplusTS;
5679 (*hcross) = hcrossTS;
5680
5681 /* Additional outputs */
5682
5683 /* Dynamics */
5684 // NOTE: casting to REAL8Vector due to the SWIG wrapping
5685 *seobdynamicsAdaSVector =
5687 *seobdynamicsHiSVector =
5689 *seobdynamicsAdaSHiSVector =
5690 XLALCreateREAL8Vector(v4PdynamicsVariables * seobdynamicsAdaSHiS->length);
5691 memcpy((*seobdynamicsAdaSVector)->data, seobdynamicsAdaS->array->data,
5692 (v4PdynamicsVariables * seobdynamicsAdaS->length) * sizeof(REAL8));
5693 memcpy((*seobdynamicsHiSVector)->data, seobdynamicsHiS->array->data,
5694 (v4PdynamicsVariables * seobdynamicsHiS->length) * sizeof(REAL8));
5695 memcpy((*seobdynamicsAdaSHiSVector)->data, seobdynamicsAdaSHiS->array->data,
5696 (v4PdynamicsVariables * seobdynamicsAdaSHiS->length) * sizeof(REAL8));
5697
5698 /* Modes in the P-frame */
5699 // NOTE: casting to REAL8Vector due to the SWIG wrapping
5700 // NOTE: in the output, real amplitude instead of complex envelope
5701 /* (2,2) */
5702 *hP22_amp = XLALCreateREAL8Vector(retLenPmodes);
5703 *hP22_phase = XLALCreateREAL8Vector(retLenPmodes);
5706 if (hP22 == NULL) {
5707 memset((*hP22_amp)->data, 0, retLenPmodes * sizeof(REAL8));
5708 memset((*hP22_phase)->data, 0, retLenPmodes * sizeof(REAL8));
5709 } else {
5710 memcpy((*hP22_amp)->data, hP22->campphase->camp_real->data,
5711 retLenPmodes * sizeof(REAL8));
5712 memcpy((*hP22_phase)->data, hP22->campphase->phase->data,
5713 retLenPmodes * sizeof(REAL8));
5714 }
5715 /* (2,1) */
5716 *hP21_amp = XLALCreateREAL8Vector(retLenPmodes);
5717 *hP21_phase = XLALCreateREAL8Vector(retLenPmodes);
5720 if (hP21 == NULL) {
5721 memset((*hP21_amp)->data, 0, retLenPmodes * sizeof(REAL8));
5722 memset((*hP21_phase)->data, 0, retLenPmodes * sizeof(REAL8));
5723 } else {
5724 memcpy((*hP21_amp)->data, hP21->campphase->camp_real->data,
5725 retLenPmodes * sizeof(REAL8));
5726 memcpy((*hP21_phase)->data, hP21->campphase->phase->data,
5727 retLenPmodes * sizeof(REAL8));
5728 }
5729 /* (3,3) */
5730 *hP33_amp = XLALCreateREAL8Vector(retLenPmodes);
5731 *hP33_phase = XLALCreateREAL8Vector(retLenPmodes);
5734 if (hP33 == NULL) {
5735 memset((*hP33_amp)->data, 0, retLenPmodes * sizeof(REAL8));
5736 memset((*hP33_phase)->data, 0, retLenPmodes * sizeof(REAL8));
5737 } else {
5738 memcpy((*hP33_amp)->data, hP33->campphase->camp_real->data,
5739 retLenPmodes * sizeof(REAL8));
5740 memcpy((*hP33_phase)->data, hP33->campphase->phase->data,
5741 retLenPmodes * sizeof(REAL8));
5742 }
5743 /* (4,4) */
5744 *hP44_amp = XLALCreateREAL8Vector(retLenPmodes);
5745 *hP44_phase = XLALCreateREAL8Vector(retLenPmodes);
5748 if (hP44 == NULL) {
5749 memset((*hP44_amp)->data, 0, retLenPmodes * sizeof(REAL8));
5750 memset((*hP44_phase)->data, 0, retLenPmodes * sizeof(REAL8));
5751 } else {
5752 memcpy((*hP44_amp)->data, hP44->campphase->camp_real->data,
5753 retLenPmodes * sizeof(REAL8));
5754 memcpy((*hP44_phase)->data, hP44->campphase->phase->data,
5755 retLenPmodes * sizeof(REAL8));
5756 }
5757 /* (5,5) */
5758 *hP55_amp = XLALCreateREAL8Vector(retLenPmodes);
5759 *hP55_phase = XLALCreateREAL8Vector(retLenPmodes);
5762 if (hP55 == NULL) {
5763 memset((*hP55_amp)->data, 0, retLenPmodes * sizeof(REAL8));
5764 memset((*hP55_phase)->data, 0, retLenPmodes * sizeof(REAL8));
5765 } else {
5766 memcpy((*hP55_amp)->data, hP55->campphase->camp_real->data,
5767 retLenPmodes * sizeof(REAL8));
5768 memcpy((*hP55_phase)->data, hP55->campphase->phase->data,
5769 retLenPmodes * sizeof(REAL8));
5770 }
5771
5772 /* Cleanup */
5773 if (ICvalues != NULL)
5774 XLALDestroyREAL8Vector(ICvalues);
5775 if (dynamicsAdaS != NULL)
5776 XLALDestroyREAL8Array(dynamicsAdaS);
5777 if (rVec != NULL)
5779 if (seobdynamicsAdaS != NULL)
5780 SEOBdynamics_Destroy(seobdynamicsAdaS);
5781 if (seobvalues_tstartHiS != NULL)
5782 XLALDestroyREAL8Vector(seobvalues_tstartHiS);
5783 if (ICvaluesHiS != NULL)
5784 XLALDestroyREAL8Vector(ICvaluesHiS);
5785 if (dynamicsHiS != NULL)
5786 XLALDestroyREAL8Array(dynamicsHiS);
5787 if (seobdynamicsHiS != NULL)
5788 SEOBdynamics_Destroy(seobdynamicsHiS);
5789 if (seobvalues_tPeakOmega != NULL)
5790 XLALDestroyREAL8Vector(seobvalues_tPeakOmega);
5791 if (seobvalues_test != NULL)
5792 XLALDestroyREAL8Vector(seobvalues_test);
5793 if (Jfinal != NULL)
5794 XLALDestroyREAL8Vector(Jfinal);
5795 if (Lhatfinal != NULL)
5796 XLALDestroyREAL8Vector(Lhatfinal);
5797 if (nqcCoeffsList != NULL)
5798 SphHarmListEOBNonQCCoeffs_Destroy(nqcCoeffsList);
5799 if (listhPlm_HiS != NULL)
5801 if (listhPlm_HiSRDpatch != NULL)
5802 SphHarmListCAmpPhaseSequence_Destroy(listhPlm_HiSRDpatch);
5803 if (listhPlm_AdaS != NULL)
5805 if (seobdynamicsAdaSHiS != NULL)
5806 SEOBdynamics_Destroy(seobdynamicsAdaSHiS);
5807 if (listhPlm != NULL)
5809 if (chi1L_tPeakOmega != NULL)
5810 XLALDestroyREAL8Vector(chi1L_tPeakOmega);
5811 if (chi2L_tPeakOmega != NULL)
5812 XLALDestroyREAL8Vector(chi2L_tPeakOmega);
5813 if (sigmaQNMlm0 != NULL)
5814 XLALDestroyCOMPLEX16Vector(sigmaQNMlm0);
5815 return XLAL_SUCCESS;
5816}
5817#undef FREE_ALL
5818#undef PRINT_ALL_PARAMS
5819
5820#endif
int XLALDictContains(const LALDict *dict, const char *key)
void XLALDestroyDict(LALDict *dict)
LALDict * XLALCreateDict(void)
int XLALDictInsertINT4Value(LALDict *dict, const char *key, INT4 value)
INT4 XLALDictLookupINT4Value(const LALDict *dict, const char *key)
INT4 XLALSimIMREOBFinalMassSpinPrec(REAL8 *finalMass, REAL8 *finalSpin, const REAL8 mass1, const REAL8 mass2, const REAL8 spin1[3], const REAL8 spin2[3], Approximant approximant)
Computes the final mass and spin of the black hole resulting from merger.
INT4 XLALSimIMREOBGenerateQNMFreqV2FromFinalPrec(COMPLEX16Vector *modefreqs, const REAL8 mass1, const REAL8 mass2, const REAL8 finalMass, REAL8 finalSpin, UINT4 l, INT4 m, UINT4 nmodes)
This function generates the quasinormal mode frequencies for a black hole ringdown.
INT4 XLALSimIMREOBGenerateQNMFreqV2Prec(COMPLEX16Vector *modefreqs, const REAL8 mass1, const REAL8 mass2, const REAL8 spin1[3], const REAL8 spin2[3], UINT4 l, INT4 m, UINT4 nmodes, Approximant approximant)
This function generates the quasinormal mode frequencies for a black hole ringdown.
double XLALSimLocateMaxAmplTime(REAL8Vector *timeHi, COMPLEX16Vector *hP22, int *found)
flagSEOBNRv4P_euler_extension
Definition: LALSimIMR.h:277
@ FLAG_SEOBNRv4P_EULEREXT_QNM_SIMPLE_PRECESSION
QNM-based simple precession prescription post-merger.
Definition: LALSimIMR.h:278
@ FLAG_SEOBNRv4P_EULEREXT_CONSTANT
Euler angles set to constants post-merger.
Definition: LALSimIMR.h:279
flagSEOBNRv4P_Zframe
Definition: LALSimIMR.h:282
@ FLAG_SEOBNRv4P_ZFRAME_L
set Z axis of the P-frame along L
Definition: LALSimIMR.h:283
@ FLAG_SEOBNRv4P_ZFRAME_LN
set Z axis of the P-frame along LN
Definition: LALSimIMR.h:284
flagSEOBNRv4P_hamiltonian_derivative
Definition: LALSimIMR.h:272
@ FLAG_SEOBNRv4P_HAMILTONIAN_DERIVATIVE_ANALYTICAL
use analytical derivatives (opt)
Definition: LALSimIMR.h:273
@ FLAG_SEOBNRv4P_HAMILTONIAN_DERIVATIVE_NUMERICAL
use numerical derivatives (pre-opt)
Definition: LALSimIMR.h:274
static int XLALSimIMRCalculateSpinPrecEOBHCoeffs_v2(SpinEOBHCoeffs *coeffs, const REAL8 eta, REAL8 a, REAL8 chi, const UINT4 SpinAlignedEOBversion)
This function is used to calculate some coefficients which will be used in the spinning EOB Hamiltoni...
static UNUSED INT4 XLALSimIMREOBAttachFitRingdown(REAL8Vector *signal1, REAL8Vector *signal2, const INT4 l, const INT4 m, const REAL8 dt, const REAL8 mass1, const REAL8 mass2, const REAL8 spin1x, const REAL8 spin1y, const REAL8 spin1z, const REAL8 spin2x, const REAL8 spin2y, const REAL8 spin2z, const REAL8 domega220, const REAL8 dtau220, const REAL8 domega210, const REAL8 dtau210, const REAL8 domega330, const REAL8 dtau330, const REAL8 domega440, const REAL8 dtau440, const REAL8 domega550, const REAL8 dtau550, const UINT2 TGRflag, REAL8Vector *timeVec, REAL8Vector *matchrange, Approximant approximant, UINT4 *indAmpMax)
The main function for performing the ringdown attachment for SEOBNRv4 (and beyond) This is the functi...
static UNUSED int XLALSimIMRSpinEOBNonQCCorrection(COMPLEX16 *restrict nqc, REAL8Vector *restrict values, const REAL8 omega, EOBNonQCCoeffs *restrict coeffs)
This function calculates the non-quasicircular correction to apply to the waveform.
static UNUSED REAL8 XLALSimIMREOBGetNRSpinPeakDeltaTv4(INT4 UNUSED l, INT4 UNUSED m, REAL8 UNUSED m1, REAL8 UNUSED m2, REAL8 UNUSED chi1, REAL8 UNUSED chi2)
The time difference between the orbital peak and the peak amplitude of the mode in question (currentl...
static UNUSED int XLALSimIMRSpinEOBCalculateNQCCoefficientsV4(REAL8Vector *restrict amplitude, REAL8Vector *restrict phase, REAL8Vector *restrict rVec, REAL8Vector *restrict prVec, REAL8Vector *restrict orbOmegaVec, INT4 modeL, INT4 modeM, REAL8 timePeak, REAL8 deltaT, REAL8 m1, REAL8 m2, REAL8 chiA, REAL8 chiS, EOBNonQCCoeffs *restrict coeffs)
This function computes the coefficients a3s, a4, etc.
static UNUSED int XLALSimIMREOBComputeNewtonMultipolePrefixes(NewtonMultipolePrefixes *prefix, const REAL8 m1, const REAL8 m2)
Function which computes the various coefficients in the Newtonian multipole.
#define nModes
static double f_alphadotcosi(double x, void *inparams)
const UINT4 lmModes[NMODES][2]
static int XLALSpinAlignedHcapDerivative(double t, const REAL8 values[], REAL8 dvalues[], void *funcParams)
SEOBHCoeffConstants XLALEOBSpinPrecCalcSEOBHCoeffConstants(REAL8 eta)
Optimized routine for calculating coefficients for the v3 Hamiltonian.
static int XLALSimIMREOBCalcSpinFacWaveformCoefficients(FacWaveformCoeffs *const coeffs, SpinEOBParams *restrict params, const REAL8 m1, const REAL8 m2, const REAL8 eta, const REAL8 a, const REAL8 chiS, const REAL8 chiA, const UINT4 SpinAlignedEOBversion)
Spin Factors.
static int XLALSimIMREOBCalcSpinPrecFacWaveformCoefficients(FacWaveformCoeffs *const coeffs, const REAL8 m1, const REAL8 m2, const REAL8 eta, const REAL8 a, const REAL8 chiS, const REAL8 chiA, const UINT4 SpinAlignedEOBversion)
Waveform expressions are given by Taracchini et al.
static INT4 XLALSimIMRSpinEOBGetPrecSpinFactorizedWaveform(COMPLEX16 *restrict hlm, REAL8Vector *restrict values, REAL8Vector *restrict cartvalues, const REAL8 v, const REAL8 Hreal, const INT4 l, const INT4 m, SpinEOBParams *restrict params)
This function calculates hlm mode factorized-resummed waveform for given dynamical variables.
static UNUSED INT4 XLALSimIMREOBCalcCalibCoefficientHigherModesPrec(SpinEOBParams *restrict UNUSED params, const UINT4 modeL, const UINT4 modeM, SEOBdynamics *seobdynamics, const REAL8 timeorb, const REAL8 m1, const REAL8 m2, const REAL8 UNUSED deltaT)
This function calculate the calibration parameter for the amplitude of the factorized-resummed wavefo...
static REAL8 XLALSimIMRSpinPrecEOBHamiltonian(const REAL8 eta, REAL8Vector *restrict x, REAL8Vector *restrict p, REAL8Vector *restrict s1Vec, REAL8Vector *restrict s2Vec, REAL8Vector *restrict sigmaKerr, REAL8Vector *restrict sigmaStar, int tortoise, SpinEOBHCoeffs *coeffs)
static REAL8 * cross_product(const REAL8 values1[], const REAL8 values2[], REAL8 result[])
static int XLALSpinPrecHcapRvecDerivative(double UNUSED t, const REAL8 values[], REAL8 dvalues[], void *funcParams)
Function to calculate numerical derivatives of the spin EOB Hamiltonian, to get , as decribed in Eqs.
static REAL8 inner_product(const REAL8 values1[], const REAL8 values2[])
Functions to compute the inner product and cross products between vectors.
static int XLALSpinPrecHcapExactDerivative(double UNUSED t, const REAL8 values[], REAL8 dvalues[], void *funcParams)
Function to calculate numerical derivatives of the spin EOB Hamiltonian, which correspond to time der...
static int XLALSpinPrecHcapNumericalDerivative(double UNUSED t, const REAL8 values[], REAL8 dvalues[], void *funcParams)
Function to calculate numerical derivatives of the spin EOB Hamiltonian, which correspond to time der...
static int XLALSimIMRSpinEOBInitialConditions(REAL8Vector *initConds, const REAL8 mass1, const REAL8 mass2, const REAL8 fMin, const REAL8 inc, const REAL8 spin1[], const REAL8 spin2[], SpinEOBParams *params, INT4 use_optimized_v2)
Main function for calculating the spinning EOB initial conditions, following the quasi-spherical init...
static int XLALSimIMRSpinEOBInitialConditionsPrec(REAL8Vector *initConds, const REAL8 mass1, const REAL8 mass2, const REAL8 fMin, const REAL8 inc, const REAL8 spin1[], const REAL8 spin2[], SpinEOBParams *params, INT4 use_optimized)
Main function for calculating the spinning EOB initial conditions, following the quasi-spherical init...
static UNUSED int RotationMatrixActiveFromBasisVectors(REAL8Array *R, const REAL8 e1p[], const REAL8 e2p[], const REAL8 e3p[])
Active rotation matrix from orthonormal basis (e1, e2, e3) to (e1', e2', e3') Input are e1',...
static UNUSED int EulerAnglesZYZFromRotationMatrixActive(REAL8 *alpha, REAL8 *beta, REAL8 *gamma, REAL8Array *R)
Computes the Euler angles in the (z,y,z) convention corresponding to a given 3*3 active rotation matr...
static int SEOBConvertSpinAlignedDynamicsToGenericSpins(REAL8Array **dynamics, REAL8Array *dynamics_spinaligned, UINT4 retLen, REAL8 chi1, REAL8 chi2, SpinEOBParams *seobParams)
This function converts a spin-aligned dynamics as output by the Runge-Kutta integrator to a generic-s...
static int SEOBLhatfromDynamics(REAL8Vector **L, REAL8Vector *seobvalues, SpinEOBParams *seobParams, const flagSEOBNRv4P_Zframe flagZframe)
This function computes the L-hat vector.
static UNUSED int XLALEOBFindRobustPeak(REAL8 *tPeakQuant, REAL8Vector *tVec, REAL8Vector *quantVec, UINT4 window_width)
static SphHarmListEOBNonQCCoeffs * SphHarmListEOBNonQCCoeffs_GetMode(SphHarmListEOBNonQCCoeffs *list, UINT4 l, INT4 m)
static int SEOBGetLMaxInModeArray(LALValue *modearray, int lmax)
This function returns the maximum ell in the mode array.
static UINT4 FindClosestIndex(REAL8Vector *vec, REAL8 value)
This function finds the index in a vector such that the value at the index closest to an input value.
static REAL8 SEOBWignerDPhase(INT4 m, INT4 mp, REAL8 alpha, REAL8 gamma)
static int SEOBInterpolateDynamicsAtTime(REAL8Vector **seobdynamics_values, REAL8 t, SEOBdynamics *seobdynamics)
This function computes all extended dynamics values at a given time by interpolating the dynamics arr...
static int SEOBLocateTimePeakOmega(REAL8 *tPeakOmega, INT4 *foundPeakOmega, UNUSED REAL8Array *dynamics, SEOBdynamics *seobdynamics, UINT4 retLen, UNUSED SpinEOBParams *seobParams, UNUSED flagSEOBNRv4P_hamiltonian_derivative flagHamiltonianDerivative)
This function finds the peak of omega.
static UNUSED int SEOBLocateTimePeakModeAmp(REAL8 *tPeakAmp, INT4 *foundPeakAmp, CAmpPhaseSequence *hlm, SEOBdynamics *seobdynamics, UINT4 retLen)
This function looks for the peak of a mode amplitude.
static int SEOBdynamics_Destroy(SEOBdynamics *seobdynamics)
static int SEOBIntegrateDynamics(REAL8Array **dynamics, UINT4 *retLenOut, REAL8Vector *ICvalues, REAL8 EPS_ABS, REAL8 EPS_REL, REAL8 deltaT, REAL8 deltaT_min, REAL8 tstart, REAL8 tend, SpinEOBParams *seobParams, UINT4 flagConstantSampling, flagSEOBNRv4P_hamiltonian_derivative flagHamiltonianDerivative)
This function integrates the SEOBNRv4P dynamics.
static int CAmpPhaseSequence_Destroy(CAmpPhaseSequence *campphase)
static INT4 XLALCheck_EOB_mode_array_structure(LALValue *ModeArray, UINT4 PrecEOBversion)
ModeArray is a structure which allows to select the the co-precessing frame modes to include in the w...
static INT4 XLALSetup_EOB__std_mode_array_structure(LALValue *ModeArray, UINT4 PrecEOBversion)
ModeArray is a structure which allows to select the the co-precessing frame modes to include in the w...
static int SEOBJoinTimeVector(REAL8Vector **tVecPmodes, UINT4 *retLenPmodes, REAL8 *tJoinHiS, UINT4 *indexJoinHiS, REAL8 *tJoinAttach, UINT4 *indexJoinAttach, UINT4 retLenHiSRDpatch, REAL8 deltaTHiS, REAL8 tstartHiS, REAL8 tAttach, SEOBdynamics *seobdynamicsAdaS, SEOBdynamics *seobdynamicsHiS)
This function constructs the joined vector of times (AdaS+HiS+RDpatch) and keeps the jonction indices...
#define _SEOB_MODES_LMAX
static int SEOBAttachRDToSphHarmListhPlm(SphHarmListCAmpPhaseSequence **listhPlm_RDattached, COMPLEX16Vector **sigmaQNMlm0, INT4 modes[][2], UINT4 nmodes, REAL8 finalMass, REAL8 finalSpin, SphHarmListCAmpPhaseSequence *listhPlm, REAL8 deltaT, UINT4 retLen, UINT4 retLenRDPatch, REAL8 tAttach, REAL8Vector *seobvalues, SEOBdynamics *seobdynamics, SpinEOBParams *seobParams, const flagSEOBNRv4P_Zframe flagZframe, const INT4 debug)
This function attaches the ringdown to the P-frame modes hlm.
static REAL8 SEOBCalculateChiA(REAL8 chi1dotZ, REAL8 chi2dotZ)
#define v4Pwave
static UNUSED REAL8Vector * get_slice(REAL8Vector *vec, UINT4 lo, UINT4 hi)
static int SEOBRotatehIlmFromhJlm(SphHarmTimeSeries **hIlm, SphHarmTimeSeries *hJlm, INT4 modes_lmax, REAL8 alphaI2J, REAL8 betaI2J, REAL8 gammaI2J, REAL8 deltaT)
This function computes the hIlm Re/Im timeseries (fixed sampling) from hJlm Re/Im timeseries (same sa...
static REAL8 SEOBWignerDAmp(UINT4 l, INT4 m, INT4 mp, REAL8 beta)
These functions compute the amplitude and phase of a Wigner coefficient Dlmmp, given Euler angles of ...
static int SEOBJoinDynamics(SEOBdynamics **seobdynamicsJoined, SEOBdynamics *seobdynamics1, SEOBdynamics *seobdynamics2, UINT4 indexJoin12, UINT4 indexEnd2)
This function copies dynamics from AdaS<HiS and HiS<tAttach to form joined dynamics,...
static REAL8 SEOBCalculateChiS(REAL8 chi1dotZ, REAL8 chi2dotZ)
Functions to calculate symmetrized and antisymmetrized combinations of the dimensionless spins projec...
static int SphHarmListCAmpPhaseSequence_AddMode(SphHarmListCAmpPhaseSequence **list_prepended, CAmpPhaseSequence *campphase, UINT4 l, INT4 m)
int XLALSimIMRSpinPrecEOBWaveform(REAL8TimeSeries **hplus, REAL8TimeSeries **hcross, const REAL8 phiC, const REAL8 deltaT, const REAL8 m1SI, const REAL8 m2SI, const REAL8 fMin, const REAL8 r, const REAL8 inc, const REAL8 INspin1[], const REAL8 INspin2[], UNUSED const UINT4 PrecEOBversion, LALDict *LALParams)
Standard interface for SEOBNRv4P waveform generator: calls XLALSimIMRSpinPrecEOBWaveformAll.
static UNUSED int XLALEOBSpinPrecStopConditionBasedOnPR(double UNUSED t, const double values[], double dvalues[], void UNUSED *funcParams)
Stopping conditions for dynamics integration for SEOBNRv4P.
static int XLALSpinPrecAlignedHiSRStopCondition(double UNUSED t, const double UNUSED values[], double dvalues[], void UNUSED *funcParams)
Stopping condition for the high resolution SEOBNRv4.
static int XLALEOBSpinPrecAlignedStopCondition(double UNUSED t, const double values[], double dvalues[], void *funcParams)
Stopping condition for the regular resolution SEOBNRv1/2 orbital evolution – stop when reaching max o...
static int SEOBJfromDynamics(REAL8Vector **J, REAL8Vector *seobvalues, SpinEOBParams *seobParams)
This function computes the J vector.
static int SEOBRotateInterpolatehJlmReImFromSphHarmListhPlmAmpPhase(SphHarmTimeSeries **hJlm, INT4 modes[][2], UINT4 nmodes, INT4 modes_lmax, REAL8 deltaT, UINT4 retLenTS, REAL8Vector *tVecPmodes, SphHarmListCAmpPhaseSequence *listhPlm, REAL8Vector *alphaJ2P, REAL8Vector *betaJ2P, REAL8Vector *gammaJ2P, UINT4 flagSymmetrizehPlminusm)
This function computes the hJlm Re/Im timeseries (fixed sampling) from hPlm amp/phase modes and Euler...
static int SEOBCalculateSigmaStar(REAL8Vector *sigmaStar, REAL8 mass1, REAL8 mass2, REAL8Vector *s1, REAL8Vector *s2)
Function to calculate normalized spin of the test particle in SEOBNRv1.
static int SEOBEulerJ2PFromDynamics(REAL8Vector **alphaJ2P, REAL8Vector **betaJ2P, REAL8Vector **gammaJ2P, REAL8Vector *e1J, REAL8Vector *e2J, REAL8Vector *e3J, UINT4 retLen, UINT4 indexStop, SEOBdynamics *seobdynamics, SpinEOBParams *seobParams, flagSEOBNRv4P_Zframe flagZframe)
This function computes the Euler angles from J-frame to P-frame given the dynamics and basis vectors ...
#define FREE_ALL
static UNUSED UINT4 argmax(REAL8Vector *vec)
static UINT4 SEOBGetNumberOfModesInModeArray(LALValue *modearray, int lmax)
This function gets the number of modes present in a mode array, ignoring modes l>lmax (and l<2) Note ...
static int CAmpPhaseSequence_Init(CAmpPhaseSequence **campphase, int size)
static UNUSED int XLALEOBSpinPrecStopCondition_v4(double UNUSED t, const double values[], double dvalues[], void UNUSED *funcParams)
static int SEOBCalculateSphHarmListNQCCoefficientsV4(SphHarmListEOBNonQCCoeffs **nqcCoeffsList, INT4 modes[][2], UINT4 nmodes, REAL8 tPeakOmega, SEOBdynamics *seobdynamics, SpinEOBParams *seobParams, REAL8Vector *chi1_omegaPeak, REAL8Vector *chi2_omegaPeak)
This function computes the NQC coefficients for a list of mode contributions.
#define PRINT_ALL_PARAMS
static int SEOBLFrameVectors(REAL8Vector **S1, REAL8Vector **S2, REAL8Vector *seobvalues, REAL8 m1, REAL8 m2, const flagSEOBNRv4P_Zframe flagZframe)
static int SphHarmListEOBNonQCCoeffs_AddMode(SphHarmListEOBNonQCCoeffs **list_prepended, EOBNonQCCoeffs *nqcCoeffs, UINT4 l, INT4 m)
static int SEOBComputehplushcrossFromhIlm(REAL8TimeSeries *hplusTS, REAL8TimeSeries *hcrossTS, INT4 modes_lmax, SphHarmTimeSeries *hIlm, REAL8 amp0, REAL8 inc, REAL8 phi)
This function combines the modes hIlm timeseries with the sYlm to produce the polarizations hplus,...
static REAL8 FindClosestValueInIncreasingVector(REAL8Vector *vec, REAL8 value)
This function finds the value in a vector that is closest to an input value.
static int SEOBJoinSphHarmListhlm(SphHarmListCAmpPhaseSequence **listhlm_joined, SphHarmListCAmpPhaseSequence *listhlm_1, SphHarmListCAmpPhaseSequence *listhlm_2, INT4 modes[][2], UINT4 nmodes, UINT4 indexJoin12)
This function copies dynamics from AdaS<HiS and HiS<tAttach to form joined dynamics,...
static int SEOBCalculateSphHarmListhlmAmpPhase(SphHarmListCAmpPhaseSequence **listhlm, INT4 modes[][2], UINT4 nmodes, SEOBdynamics *seobdynamics, SphHarmListEOBNonQCCoeffs *listnqcCoeffs, SpinEOBParams *seobParams, UINT4 flagNQC)
This function generates all waveform modes as a list for a given SEOB dynamics.
#define v4PdynamicsVariables
int XLALSimIMRSpinPrecEOBWaveformAll(REAL8TimeSeries **hplus, REAL8TimeSeries **hcross, SphHarmTimeSeries **hIlm, SphHarmTimeSeries **hJlm, REAL8Vector **seobdynamicsAdaSVector, REAL8Vector **seobdynamicsHiSVector, REAL8Vector **seobdynamicsAdaSHiSVector, REAL8Vector **tVecPmodes, REAL8Vector **hP22_amp, REAL8Vector **hP22_phase, REAL8Vector **hP21_amp, REAL8Vector **hP21_phase, REAL8Vector **hP33_amp, REAL8Vector **hP33_phase, REAL8Vector **hP44_amp, REAL8Vector **hP44_phase, REAL8Vector **hP55_amp, REAL8Vector **hP55_phase, REAL8Vector **alphaJ2P, REAL8Vector **betaJ2P, REAL8Vector **gammaJ2P, REAL8Vector **mergerParams, const REAL8 phi, const REAL8 INdeltaT, const REAL8 m1SI, const REAL8 m2SI, const REAL8 fMin, const REAL8 r, const REAL8 inc, const REAL8 chi1x, const REAL8 chi1y, const REAL8 chi1z, const REAL8 chi2x, const REAL8 chi2y, const REAL8 chi2z, LALValue *modearray, LALDict *seobflags)
This function is the master function generating precessing spinning SEOBNRv4P waveforms h+ and hx.
int XLALEOBHighestInitialFreq(REAL8 *freqMinRad, REAL8 mTotal)
static int SEOBGetModeNumbersFromModeArray(INT4 modes[][2], LALValue *modearray, int lmax)
This function populates a dynamically allocated INT4 array to with the modes active in a ModeArray Po...
static int SEOBBuildJframeVectors(REAL8Vector *e1J, REAL8Vector *e2J, REAL8Vector *e3J, REAL8Vector *JVec)
This function computes the Jframe unit vectors, with e3J along Jhat.
int XLALEOBCheckNyquistFrequency(REAL8 m1, REAL8 m2, REAL8 spin1[3], REAL8 spin2[3], UINT4 ell_max, Approximant approx, REAL8 deltaT)
static int SEOBCalculateSigmaKerr(REAL8Vector *sigmaKerr, REAL8Vector *s1, REAL8Vector *s2)
Function to calculate normalized spin of the deformed-Kerr background in SEOBNRv1.
static int SEOBCalculatehlmAmpPhase(CAmpPhaseSequence **hlm, INT4 l, INT4 m, SEOBdynamics *seobdynamics, EOBNonQCCoeffs *nqcCoeffs, SpinEOBParams *seobParams, UINT4 includeNQC)
This function generates a waveform mode for a given SEOB dynamics.
static int SphHarmListEOBNonQCCoeffs_Destroy(SphHarmListEOBNonQCCoeffs *list)
struct approximant v4P
struct approximant v4PHM
static SphHarmListCAmpPhaseSequence * SphHarmListCAmpPhaseSequence_GetMode(SphHarmListCAmpPhaseSequence *list, UINT4 l, INT4 m)
static int SEOBEulerI2JFromJframeVectors(REAL8 *alphaI2J, REAL8 *betaI2J, REAL8 *gammaI2J, REAL8Vector *e1J, REAL8Vector *e2J, REAL8Vector *e3J)
This function computes Euler angles I2J given the unit vectors of the Jframe.
static REAL8 SEOBCalculatetplspin(REAL8 m1, REAL8 m2, REAL8 eta, REAL8 chi1dotZ, REAL8 chi2dotZ, INT4 SpinAlignedEOBversion)
Function to calculate tplspin See discussion below Eq.
static int SEOBComputeExtendedSEOBdynamics(SEOBdynamics **seobdynamics, REAL8Array *dynamics, UINT4 retLen, SpinEOBParams *seobParams, flagSEOBNRv4P_hamiltonian_derivative flagHamiltonianDerivative, flagSEOBNRv4P_Zframe flagZframe)
This function computes quantities (polardynamics, omega, s1dotZ, s2dotZ, hamiltonian) derived from th...
static int SEOBGetFinalSpinMass(REAL8 *finalMass, REAL8 *finalSpin, REAL8Vector *seobvalues, SpinEOBParams *seobParams, const flagSEOBNRv4P_Zframe flagZframe)
static int SphHarmListCAmpPhaseSequence_Destroy(SphHarmListCAmpPhaseSequence *list)
static int SEOBEulerJ2PPostMergerExtension(REAL8Vector *alphaJ2P, REAL8Vector *betaJ2P, REAL8Vector *gammaJ2P, COMPLEX16 sigmaQNM220, COMPLEX16 sigmaQNM210, REAL8Vector *tVec, UINT4 retLen, UINT4 indexStart, SpinEOBParams *seobParams, flagSEOBNRv4P_euler_extension flagEulerextension, INT4 flip)
This function extends Euler angles J2P according to the prescription flagEulerextension after attachm...
static int SEOBAmplitudePeakFromAmp22Amp21(REAL8 *tPeak, UINT4 *indexPeak, SphHarmListCAmpPhaseSequence *listhPlm, INT4 modes[][2], UINT4 nmodes, REAL8Vector *tVec)
This function finds the (first) index and time with the largest sum-of-squares amplitude - discrete,...
static int SEOBdynamics_Init(SEOBdynamics **seobdynamics, UINT4 retLen)
SphHarmTimeSeries * XLALSimIMRSpinPrecEOBModes(const REAL8 deltaT, const REAL8 m1SI, const REAL8 m2SI, const REAL8 fMin, const REAL8 r, const REAL8 INspin1[], const REAL8 INspin2[], UNUSED const UINT4 PrecEOBversion, LALDict *LALParams)
Standard interface for SEOBNRv4P modes generator: calls XLALSimIMRSpinPrecEOBWaveformAll.
static int SEOBInitialConditions(REAL8Vector **ICvalues, REAL8 MfMin, REAL8 m1, REAL8 m2, REAL8Vector *chi1, REAL8Vector *chi2, SpinEOBParams *seobParams, flagSEOBNRv4P_hamiltonian_derivative flagHamiltonianDerivative)
This function computes initial conditions for SEOBNRv4P.
static double beta(const double a, const double b, const sysq *system)
Internal function that computes the spin-orbit couplings.
LALValue * XLALSimInspiralWaveformParamsLookupModeArray(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupEOBChooseNumOrAnalHamDer(LALDict *params)
INT4 XLALSimInspiralWaveformParamsLookupEOBEllMaxForNyquistCheck(LALDict *params)
Module containing the energy and flux functions for waveform generation.
static int XLALSpinPrecHcapRvecDerivative_exact(double UNUSED t, const REAL8 values[], REAL8 dvalues[], void *funcParams)
Function to calculate exact derivatives of the spin EOB Hamiltonian, to get , as decribed in Eqs.
void XLALDestroyValue(LALValue *value)
int l
Definition: bh_qnmode.c:135
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
LIGOTimeGPS tstart
const double pr
const double r2
sigmaKerr data[0]
void XLALDestroyREAL8Array(REAL8Array *)
REAL8Array * XLALCreateREAL8ArrayL(UINT4,...)
int XLALAdaptiveRungeKutta4(LALAdaptiveRungeKuttaIntegrator *integrator, void *params, REAL8 *yinit, REAL8 tinit, REAL8 tend, REAL8 deltat, REAL8Array **yout)
void XLALAdaptiveRungeKuttaFree(LALAdaptiveRungeKuttaIntegrator *integrator)
int XLALAdaptiveRungeKutta4NoInterpolate(LALAdaptiveRungeKuttaIntegrator *integrator, void *params, REAL8 *yinit, REAL8 tinit, REAL8 tend, REAL8 deltat_or_h0, REAL8 min_deltat_or_h0, REAL8Array **t_and_yout, INT4 EOBversion)
LALAdaptiveRungeKuttaIntegrator * XLALAdaptiveRungeKutta4Init(int dim, int(*dydt)(double t, const double y[], double dydt[], void *params), int(*stop)(double t, const double y[], double dydt[], void *params), double eps_abs, double eps_rel)
#define LAL_MSUN_SI
#define LAL_PI
#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)
Approximant
Enum that specifies the PN approximant to be used in computing the waveform.
@ SEOBNRv4P
Spin precessing EOBNR model based on SEOBNRv4.
int XLALSimInspiralModeArrayIsModeActive(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralModeArrayActivateMode(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralCreateModeArray(void)
Create a LALValue pointer to store the mode array.
COMPLEX16TimeSeries * XLALSphHarmTimeSeriesGetMode(SphHarmTimeSeries *ts, UINT4 l, INT4 m)
Get the time series of a waveform's (l,m) spherical harmonic mode from a SphHarmTimeSeries linked lis...
SphHarmTimeSeries * XLALSphHarmTimeSeriesAddMode(SphHarmTimeSeries *appended, const COMPLEX16TimeSeries *inmode, UINT4 l, INT4 m)
Prepend a node to a linked list of SphHarmTimeSeries, or create a new head.
void XLALSphHarmTimeSeriesSetTData(SphHarmTimeSeries *ts, REAL8Sequence *tdata)
Set the tdata member for all nodes in the list.
void XLALDestroySphHarmTimeSeries(SphHarmTimeSeries *ts)
Delete list from current pointer to the end of the list.
static const INT4 r
static const INT4 m
static const INT4 q
static const INT4 a
double XLALWignerdMatrix(int l, int mp, int m, double beta)
COMPLEX16 XLALSpinWeightedSphericalHarmonic(REAL8 theta, REAL8 phi, int s, int l, int m)
void XLALDestroyCOMPLEX16TimeSeries(COMPLEX16TimeSeries *series)
COMPLEX16TimeSeries * XLALCreateCOMPLEX16TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
const LALUnit lalStrainUnit
REAL8Vector * XLALCreateREAL8Vector(UINT4 length)
COMPLEX16Vector * XLALCreateCOMPLEX16Vector(UINT4 length)
void XLALDestroyREAL8Vector(REAL8Vector *vector)
void XLALDestroyCOMPLEX16Vector(COMPLEX16Vector *vector)
int XLALREAL8VectorUnwrapAngle(REAL8Vector *out, const REAL8Vector *in)
#define XLAL_PRINT_INFO(...)
#define XLAL_ERROR_NULL(...)
#define XLAL_ERROR(...)
#define XLAL_PRINT_WARNING(...)
#define XLAL_TRY(statement, errnum)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_PRINT_ERROR(...)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFUNC
XLAL_EDOM
XLAL_EINVAL
XLAL_FAILURE
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
p
double alpha
Definition: sgwb.c:106
Structure to represent a data piece (e.g.
REAL8Vector * xdata
Sequence of times or frequencies on which data is given.
REAL8Vector * phase
Sequence for the phase.
REAL8Vector * camp_imag
Sequence for the imag part of the complex amplitude (enveloppe).
REAL8Vector * camp_real
Sequence for the real part of the complex amplitude (enveloppe).
COMPLEX16Sequence * data
COMPLEX16 * data
The coefficients which are used in calculating the non-quasicircular correction to the EOBNRv2 model.
Structure containing all the parameters needed for the EOB waveform.
FacWaveformCoeffs * hCoeffs
NewtonMultipolePrefixes * prefixes
Structure containing the coefficients for calculating the factorized waveform.
TidalEOBParams * tidal2
TidalEOBParams * tidal1
Structure containing all the terms of the Newtonian multipole which are constant over the course of t...
gsl_interp_accel * alpha_acc
gsl_interp_accel * beta_acc
REAL8 * data
REAL8Sequence * data
REAL8 * data
Structure the EOB dynamics for precessing waveforms.
REAL8Array * array
REAL8 * polarpphiVec
Structure to represent linked list of modes with complex amplitude (enveloppe) and phase.
struct tagSphHarmListCAmpPhaseSequence * next
Pointer to next element in the list.
CAmpPhaseSequence * campphase
Data for this mode.
Structure to represent linked list of modes with complex amplitude (enveloppe) and phase.
EOBNonQCCoeffs * nqcCoeffs
NQC coefficients for this mode.
struct tagSphHarmListEOBNonQCCoeffs * next
Pointer to next element in the list.
Structure to carry a collection of spherical harmonic modes in COMPLEX16 time series.
REAL8Sequence * tdata
Timestamp values.
Parameters for the spinning EOB model, used in calculating the Hamiltonian.
UINT4 SpinAlignedEOBversion
TidalEOBParams * tidal1
TidalEOBParams * tidal2
Parameters for the spinning EOB model.
SpinEOBHCoeffs * seobCoeffs
REAL8Vector * sigmaStar
EOBNonQCCoeffs * nqcCoeffs
REAL8Vector * s1Vec
EOBParams * eobParams
REAL8Vector * sigmaKerr
SEOBHCoeffConstants * seobCoeffConsts
Approximant seobApproximant
REAL8Vector * s2Vec
Tidal parameters for EOB model of NS: mByM - dimensionless ratio m_{NS}/M lambda2Tidal - dimensionles...
Definition: burst.c:245
double deltaT
Definition: unicorn.c:24