LALSimulation  5.4.0.1-fe68b98
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"
49 #include "LALSimInspiralPrecess.h"
51 #include "LALSimFindAttachTime.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  */
97 struct approximant {
98  const char *name;
100 };
101 struct approximant v4P = {.name = "SEOBNRv4P", .number = 401};
102 struct 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 */
198 UNUSED 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,
211 i.e. parroting Python behaviour */
212 UNUSED 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
222 samples. The idea is the scan the samples with a window [w_1,w_2] and find the
223 local max in each window, where local max has to not lie at the boundaries of
224 the window. One then keeps track of all the local maxes and picks the largest
225 one. Finally, one compares this value to the global argmax. If there is a clean
226 critical point which is also a global max then these 2 values have to agree */
227 UNUSED 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
306 Will set the termination reason to 1 if terminates normally(i.e. 5 steps
307 after peak of omega found). Will set it to -1 if something has become nan.
308 */
309 UNUSED 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  */
350 UNUSED static int
351 XLALEOBSpinPrecStopConditionBasedOnPR(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*/
672  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 2);
673  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 1);
674  XLALSimInspiralModeArrayActivateMode(ModeArray, 3, 3);
675  XLALSimInspiralModeArrayActivateMode(ModeArray, 4, 4);
676  XLALSimInspiralModeArrayActivateMode(ModeArray, 5, 5);
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*/
682  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 2);
683  XLALSimInspiralModeArrayActivateMode(ModeArray, 2, 1);
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  */
694 static INT4 XLALCheck_EOB_mode_array_structure(LALValue *ModeArray,
695  UINT4 PrecEOBversion) {
696  INT4 flagTrue = 0;
697  UINT4 modeL;
698  UINT4 modeM;
699  UINT4 nModes;
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)
909  XLALDestroyREAL8Vector(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  */
957 static int
958 SEOBGetLMaxInModeArray(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)
1322  XLALDestroyREAL8Vector(campphase->camp_real);
1323  if (campphase->camp_imag)
1324  XLALDestroyREAL8Vector(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 {
1461  list = XLALMalloc(sizeof(SphHarmListCAmpPhaseSequence));
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 
1499 static 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  */
1577 static REAL8 SEOBCalculateChiS(REAL8 chi1dotZ, REAL8 chi2dotZ) {
1578  return 0.5 * (chi1dotZ + chi2dotZ);
1579 }
1580 static 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  */
1588 static 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 =
2451  SphHarmListEOBNonQCCoeffs_GetMode(listnqcCoeffs, l, m)->nqcCoeffs;
2452 
2453  CAmpPhaseSequence *hlm = NULL;
2454  SEOBCalculatehlmAmpPhase(&hlm, l, m, seobdynamics, nqcCoeffs, seobParams,
2455  includeNQC);
2456 
2457  SphHarmListCAmpPhaseSequence_AddMode(listhlm, hlm, l, m);
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  */
2532 UNUSED static int SEOBLocateTimePeakModeAmp(
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 */
2650  const flagSEOBNRv4P_Zframe
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 */,
2776  const flagSEOBNRv4P_Zframe
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  }
2940  RotationMatrixActiveFromBasisVectors(R, e1J->data, e2J->data, e3J->data);
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 */
3155  const flagSEOBNRv4P_Zframe
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 */
3202  COMPLEX16Vector *
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 */
3455  SEOBdynamics
3456  *seobdynamicsAdaS, /**<< Input: SEOB dynamics with adaptive-sampling */
3457  SEOBdynamics
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
3512 static int SEOBJoinDynamics(
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
3913  REAL8Vector
3914  *alphaJ2P, /**<< Output: vector for alpha J2P, already allocated */
3915  REAL8Vector
3916  *betaJ2P, /**<< Output: vector for beta J2P, already allocated */
3917  REAL8Vector
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 */
4346  XLALSphHarmTimeSeriesSetTData(*hIlm, tI);
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  */
4572  REAL8TimeSeries *
4573  *hplus, /**<< Main output: hplus GW polarization time series */
4574  REAL8TimeSeries *
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"))
4664  flagEulerextension = FLAG_SEOBNRv4P_EULEREXT_QNM_SIMPLE_PRECESSION;
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 =
5686  XLALCreateREAL8Vector(v4PdynamicsVariables * seobdynamicsAdaS->length);
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);
5705  SphHarmListCAmpPhaseSequence_GetMode(listhPlm, 2, 2);
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);
5719  SphHarmListCAmpPhaseSequence_GetMode(listhPlm, 2, 1);
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);
5733  SphHarmListCAmpPhaseSequence_GetMode(listhPlm, 3, 3);
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);
5747  SphHarmListCAmpPhaseSequence_GetMode(listhPlm, 4, 4);
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);
5761  SphHarmListCAmpPhaseSequence_GetMode(listhPlm, 5, 5);
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)
5778  XLALDestroyREAL8Vector(rVec);
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)
5804  SphHarmListCAmpPhaseSequence_Destroy(listhPlm_AdaS);
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)
INT4 XLALDictLookupINT4Value(LALDict *dict, const char *key)
int XLALDictInsertINT4Value(LALDict *dict, const char *key, INT4 value)
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:275
@ FLAG_SEOBNRv4P_EULEREXT_QNM_SIMPLE_PRECESSION
QNM-based simple precession prescription post-merger.
Definition: LALSimIMR.h:276
@ FLAG_SEOBNRv4P_EULEREXT_CONSTANT
Euler angles set to constants post-merger.
Definition: LALSimIMR.h:277
flagSEOBNRv4P_Zframe
Definition: LALSimIMR.h:280
@ FLAG_SEOBNRv4P_ZFRAME_L
set Z axis of the P-frame along L
Definition: LALSimIMR.h:281
@ FLAG_SEOBNRv4P_ZFRAME_LN
set Z axis of the P-frame along LN
Definition: LALSimIMR.h:282
flagSEOBNRv4P_hamiltonian_derivative
Definition: LALSimIMR.h:270
@ FLAG_SEOBNRv4P_HAMILTONIAN_DERIVATIVE_ANALYTICAL
use analytical derivatives (opt)
Definition: LALSimIMR.h:271
@ FLAG_SEOBNRv4P_HAMILTONIAN_DERIVATIVE_NUMERICAL
use numerical derivatives (pre-opt)
Definition: LALSimIMR.h:272
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 int SEOBGetLMaxInModeArray(LALValue *modearray, int lmax)
This function returns the maximum ell in the mode array.
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 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 SphHarmListCAmpPhaseSequence * SphHarmListCAmpPhaseSequence_GetMode(SphHarmListCAmpPhaseSequence *list, UINT4 l, INT4 m)
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)
static SphHarmListEOBNonQCCoeffs * SphHarmListEOBNonQCCoeffs_GetMode(SphHarmListEOBNonQCCoeffs *list, UINT4 l, INT4 m)
static UNUSED REAL8Vector * get_slice(REAL8Vector *vec, UINT4 lo, UINT4 hi)
#define v4Pwave
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 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)
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]
REAL8Array * XLALCreateREAL8ArrayL(UINT4,...)
void XLALDestroyREAL8Array(REAL8Array *)
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.
LALValue * XLALSimInspiralCreateModeArray(void)
Create a LALValue pointer to store the mode array.
int XLALSimInspiralModeArrayIsModeActive(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralModeArrayActivateMode(LALValue *modes, unsigned l, int m)
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.
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...
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)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
void XLALDestroyCOMPLEX16TimeSeries(COMPLEX16TimeSeries *series)
COMPLEX16TimeSeries * XLALCreateCOMPLEX16TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
const LALUnit lalStrainUnit
COMPLEX16Vector * XLALCreateCOMPLEX16Vector(UINT4 length)
REAL8Vector * XLALCreateREAL8Vector(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)
list 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