LALSimulation  5.4.0.1-fe68b98
LALSimIMRSEOBNRv4ROM.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014, 2015, 2016 Michael Puerrer, John Veitch
3  * Reduced Order Model for SEOBNR
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 
21 #ifdef __GNUC__
22 #define UNUSED __attribute__ ((unused))
23 #else
24 #define UNUSED
25 #endif
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <math.h>
30 #include <complex.h>
31 #include <time.h>
32 #include <stdbool.h>
33 #include <alloca.h>
34 #include <string.h>
35 #include <libgen.h>
36 
37 #include <gsl/gsl_errno.h>
38 #include <gsl/gsl_bspline.h>
39 #include <gsl/gsl_blas.h>
40 #include <gsl/gsl_min.h>
41 #include <gsl/gsl_spline.h>
42 #include <gsl/gsl_poly.h>
43 #include <lal/Units.h>
44 #include <lal/SeqFactories.h>
45 #include <lal/LALConstants.h>
46 #include <lal/XLALError.h>
47 #include <lal/FrequencySeries.h>
48 #include <lal/Date.h>
49 #include <lal/StringInput.h>
50 #include <lal/Sequence.h>
51 #include <lal/LALStdio.h>
52 #include <lal/FileIO.h>
53 
54 
55 #ifdef LAL_HDF5_ENABLED
56 #include <lal/H5FileIO.h>
57 static const char ROMDataHDF5[] = "SEOBNRv4ROM_v2.0.hdf5";
58 static const INT4 ROMDataHDF5_VERSION_MAJOR = 2;
59 static const INT4 ROMDataHDF5_VERSION_MINOR = 0;
60 static const INT4 ROMDataHDF5_VERSION_MICRO = 0;
61 #endif
62 
63 #include <lal/LALSimInspiral.h>
64 #include <lal/LALSimIMR.h>
65 
67 
68 #include <lal/LALConfig.h>
69 #ifdef LAL_PTHREAD_LOCK
70 #include <pthread.h>
71 #endif
72 
73 
74 
75 #ifdef LAL_PTHREAD_LOCK
76 static pthread_once_t SEOBNRv4ROM_is_initialized = PTHREAD_ONCE_INIT;
77 #endif
78 
79 /*************** type definitions ******************/
80 
81 typedef struct tagSEOBNRROMdataDS_coeff
82 {
83  gsl_vector* c_amp;
84  gsl_vector* c_phi;
86 
88 {
89  gsl_vector* cvec_amp; // Flattened amplitude projection coefficients
90  gsl_vector* cvec_phi; // Flattened phase projection coefficients
91  gsl_matrix *Bamp; // Reduced SVD basis for amplitude
92  gsl_matrix *Bphi; // Reduced SVD basis for phase
93  int nk_amp; // Number frequency points for amplitude
94  int nk_phi; // Number of frequency points for phase
95  gsl_vector *gA; // Sparse frequency points for amplitude
96  gsl_vector *gPhi; // Sparse frequency points for phase
97  gsl_vector *etavec; // B-spline knots in eta
98  gsl_vector *chi1vec; // B-spline knots in chi1
99  gsl_vector *chi2vec; // B-spline knots in chi2
100  int ncx, ncy, ncz; // Number of points in eta, chi1, chi2
101  double eta_bounds[2]; // [eta_min, eta_max]
102  double chi1_bounds[2]; // [chi1_min, chi1_max]
103  double chi2_bounds[2]; // [chi2_min, chi2_max]
104 };
105 typedef struct tagSEOBNRROMdataDS_submodel SEOBNRROMdataDS_submodel;
106 
107 struct tagSEOBNRROMdataDS
108 {
109  UINT4 setup;
110  SEOBNRROMdataDS_submodel* sub1;
111  SEOBNRROMdataDS_submodel* sub2;
112  SEOBNRROMdataDS_submodel* sub3;
113 };
114 typedef struct tagSEOBNRROMdataDS SEOBNRROMdataDS;
115 
116 static SEOBNRROMdataDS __lalsim_SEOBNRv4ROMDS_data;
117 
118 typedef int (*load_dataPtr)(const char*, gsl_vector *, gsl_vector *, gsl_matrix *, gsl_matrix *, gsl_vector *);
119 
120 typedef struct tagSplineData
121 {
122  gsl_bspline_workspace *bwx;
123  gsl_bspline_workspace *bwy;
124  gsl_bspline_workspace *bwz;
125 } SplineData;
126 
127 /**************** Internal functions **********************/
128 
129 UNUSED static void SEOBNRv4ROM_Init_LALDATA(void);
130 UNUSED static int SEOBNRv4ROM_Init(const char dir[]);
131 UNUSED static bool SEOBNRv4ROM_IsSetup(void);
132 
133 UNUSED static int SEOBNRROMdataDS_Init(SEOBNRROMdataDS *romdata, const char dir[]);
134 UNUSED static void SEOBNRROMdataDS_Cleanup(SEOBNRROMdataDS *romdata);
135 
136 static int TP_Spline_interpolation_3d(
137  REAL8 eta, // Input: eta-value for which projection coefficients should be evaluated
138  REAL8 chi1, // Input: chi1-value for which projection coefficients should be evaluated
139  REAL8 chi2, // Input: chi2-value for which projection coefficients should be evaluated
140  gsl_vector *cvec_amp, // Input: data for spline coefficients for amplitude
141  gsl_vector *cvec_phi, // Input: data for spline coefficients for phase
142 // gsl_vector *cvec_amp_pre, // Input: data for spline coefficients for amplitude prefactor
143  int nk_amp, // number of SVD-modes == number of basis functions for amplitude
144  int nk_phi, // number of SVD-modes == number of basis functions for phase
145  int nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
146  int ncx, // Number of points in eta + 2
147  int ncy, // Number of points in chi1 + 2
148  int ncz, // Number of points in chi2 + 2
149  const double *etavec, // B-spline knots in eta
150  const double *chi1vec, // B-spline knots in chi1
151  const double *chi2vec, // B-spline knots in chi2
152  gsl_vector *c_amp, // Output: interpolated projection coefficients for amplitude
153  gsl_vector *c_phi // Output: interpolated projection coefficients for phase
154 // REAL8 *amp_pre // Output: interpolated amplitude prefactor
155 );
156 
158  UNUSED SEOBNRROMdataDS_submodel **submodel,
159  UNUSED const char dir[],
160  UNUSED const char grp_name[]
161 );
162 
163 UNUSED static void SEOBNRROMdataDS_Cleanup_submodel(SEOBNRROMdataDS_submodel *submodel);
164 
165 /**
166  * Core function for computing the ROM waveform.
167  * Interpolate projection coefficient data and evaluate coefficients at desired (q, chi).
168  * Construct 1D splines for amplitude and phase.
169  * Compute strain waveform from amplitude and phase.
170 */
171 UNUSED static int SEOBNRv4ROMCore(
172  COMPLEX16FrequencySeries **hptilde, /**< Output: Frequency-domain waveform h+ */
173  COMPLEX16FrequencySeries **hctilde, /**< Output: Frequency-domain waveform hx */
174  double phiRef, /**< Orbital phase (rad) */
175  double fRef, /**< Reference frequency */
176  double distance, /**< Distance of source (m) */
177  double inclination, /**< Inclination angle of source (rad) */
178  double Mtot_sec, /**< Total source mass in seconds */
179  double eta, /**< Symmetric mass ratio */
180  double chi1, /**< Dimensionless aligned spin on companion 1 */
181  double chi2, /**< Dimensionless aligned spin on companion 2 */
182  const REAL8Sequence *freqs, /* Frequency points at which to evaluate the waveform (Hz) */
183  double deltaF, /**< Sampling frequency (Hz).
184  * If deltaF > 0, the frequency points given in freqs are uniformly spaced with
185  * spacing deltaF. Otherwise, the frequency points are spaced non-uniformly.
186  * Then we will use deltaF = 0 to create the frequency series we return. */
187  int nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
188  LALDict *LALparams, /**< LAL dictionary containing accessory parameters */
189  NRTidal_version_type NRTidal_version /**< NRTidal version; either NRTidal_V or NRTidalv2_V or NoNRT_V in case of BBH baseline */
190 );
191 
192 UNUSED static void SEOBNRROMdataDS_coeff_Init(SEOBNRROMdataDS_coeff **romdatacoeff, int nk_amp, int nk_phi);
193 UNUSED static void SEOBNRROMdataDS_coeff_Cleanup(SEOBNRROMdataDS_coeff *romdatacoeff);
194 
195 static size_t NextPow2(const size_t n);
196 UNUSED static void SplineData_Destroy(SplineData *splinedata);
197 UNUSED static void SplineData_Init(
198  SplineData **splinedata,
199  int ncx, // Number of points in eta + 2
200  int ncy, // Number of points in chi1 + 2
201  int ncz, // Number of points in chi2 + 2
202  const double *etavec, // B-spline knots in eta
203  const double *chi1vec, // B-spline knots in chi1
204  const double *chi2vec // B-spline knots in chi2
205 );
206 
207 UNUSED static int SEOBNRv4ROMTimeFrequencySetup(
208  gsl_spline **spline_phi, // phase spline
209  gsl_interp_accel **acc_phi, // phase spline accelerator
210  REAL8 *Mf_final, // ringdown frequency in Mf
211  REAL8 *Mtot_sec, // total mass in seconds
212  REAL8 m1SI, // Mass of companion 1 (kg)
213  REAL8 m2SI, // Mass of companion 2 (kg)
214  REAL8 chi1, // Aligned spin of companion 1
215  REAL8 chi2, // Aligned spin of companion 2
216  REAL8 *Mf_ROM_min, // Lowest geometric frequency for ROM
217  REAL8 *Mf_ROM_max // Highest geometric frequency for ROM
218 );
219 
221  gsl_vector *v,
222  REAL8 eta,
223  REAL8 chi,
224  int ncx,
225  int ncy,
226  gsl_bspline_workspace *bwx,
227  gsl_bspline_workspace *bwy
228 );
229 
230 UNUSED static void GlueAmplitude(
231  // INPUTS
232  SEOBNRROMdataDS_submodel *submodel_lo,
233  SEOBNRROMdataDS_submodel *submodel_hi,
234  gsl_vector* amp_f_lo,
235  gsl_vector* amp_f_hi,
236  double amp_pre_lo,
237  double amp_pre_hi,
238  const double Mfm,
239  // OUTPUTS
240  gsl_interp_accel **acc_amp,
241  gsl_spline **spline_amp
242 );
243 
244 UNUSED static void GluePhasing(
245  // INPUTS
246  SEOBNRROMdataDS_submodel *submodel_lo,
247  SEOBNRROMdataDS_submodel *submodel_hi,
248  gsl_vector* phi_f_lo,
249  gsl_vector* phi_f_hi,
250  const double Mfm,
251  // OUTPUTS
252  gsl_interp_accel **acc_phi_out,
253  gsl_spline **spline_phi_out
254 );
255 
256 
257 /********************* Definitions begin here ********************/
258 
259 /** Setup SEOBNRv4ROM model using data files installed in dir
260  */
261 static int SEOBNRv4ROM_Init(const char dir[]) {
262  if(__lalsim_SEOBNRv4ROMDS_data.setup) {
263  XLALPrintError("Error: SEOBNRv4ROM data was already set up!");
265  }
266 
268 
269  if(__lalsim_SEOBNRv4ROMDS_data.setup) {
270  return(XLAL_SUCCESS);
271  }
272  else {
273  return(XLAL_EFAILED);
274  }
275 }
276 
277 /** Helper function to check if the SEOBNRv4ROM model has been initialised */
278 static bool SEOBNRv4ROM_IsSetup(void) {
280  return true;
281  else
282  return false;
283 }
284 
285 // Setup B-spline basis functions for given points
286 static void SplineData_Init(
287  SplineData **splinedata,
288  int ncx, // Number of points in eta + 2
289  int ncy, // Number of points in chi1 + 2
290  int ncz, // Number of points in chi2 + 2
291  const double *etavec, // B-spline knots in eta
292  const double *chi1vec, // B-spline knots in chi1
293  const double *chi2vec // B-spline knots in chi2
294 )
295 {
296  if(!splinedata) exit(1);
297  if(*splinedata) SplineData_Destroy(*splinedata);
298 
299  (*splinedata)=XLALCalloc(1,sizeof(SplineData));
300 
301  // Set up B-spline basis for desired knots
302  const size_t nbreak_x = ncx-2; // must have nbreak = n-2 for cubic splines
303  const size_t nbreak_y = ncy-2; // must have nbreak = n-2 for cubic splines
304  const size_t nbreak_z = ncz-2; // must have nbreak = n-2 for cubic splines
305 
306  // Allocate a cubic bspline workspace (k = 4)
307  gsl_bspline_workspace *bwx = gsl_bspline_alloc(4, nbreak_x);
308  gsl_bspline_workspace *bwy = gsl_bspline_alloc(4, nbreak_y);
309  gsl_bspline_workspace *bwz = gsl_bspline_alloc(4, nbreak_z);
310 
311  // Set breakpoints (and thus knots by hand)
312  gsl_vector *breakpts_x = gsl_vector_alloc(nbreak_x);
313  gsl_vector *breakpts_y = gsl_vector_alloc(nbreak_y);
314  gsl_vector *breakpts_z = gsl_vector_alloc(nbreak_z);
315  for (UINT4 i=0; i<nbreak_x; i++)
316  gsl_vector_set(breakpts_x, i, etavec[i]);
317  for (UINT4 j=0; j<nbreak_y; j++)
318  gsl_vector_set(breakpts_y, j, chi1vec[j]);
319  for (UINT4 k=0; k<nbreak_z; k++)
320  gsl_vector_set(breakpts_z, k, chi2vec[k]);
321 
322  gsl_bspline_knots(breakpts_x, bwx);
323  gsl_bspline_knots(breakpts_y, bwy);
324  gsl_bspline_knots(breakpts_z, bwz);
325 
326  gsl_vector_free(breakpts_x);
327  gsl_vector_free(breakpts_y);
328  gsl_vector_free(breakpts_z);
329 
330  (*splinedata)->bwx=bwx;
331  (*splinedata)->bwy=bwy;
332  (*splinedata)->bwz=bwz;
333 }
334 
335 static void SplineData_Destroy(SplineData *splinedata)
336 {
337  if(!splinedata) return;
338  if(splinedata->bwx) gsl_bspline_free(splinedata->bwx);
339  if(splinedata->bwy) gsl_bspline_free(splinedata->bwy);
340  if(splinedata->bwz) gsl_bspline_free(splinedata->bwz);
341  XLALFree(splinedata);
342 }
343 
344 // Interpolate projection coefficients for amplitude and phase over the parameter space (q, chi).
345 // The multi-dimensional interpolation is carried out via a tensor product decomposition.
347  REAL8 eta, // Input: eta-value for which projection coefficients should be evaluated
348  REAL8 chi1, // Input: chi1-value for which projection coefficients should be evaluated
349  REAL8 chi2, // Input: chi2-value for which projection coefficients should be evaluated
350  gsl_vector *cvec_amp, // Input: data for spline coefficients for amplitude
351  gsl_vector *cvec_phi, // Input: data for spline coefficients for phase
352  int nk_amp, // number of SVD-modes == number of basis functions for amplitude
353  int nk_phi, // number of SVD-modes == number of basis functions for phase
354  int nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
355  int ncx, // Number of points in eta + 2
356  int ncy, // Number of points in chi1 + 2
357  int ncz, // Number of points in chi2 + 2
358  const double *etavec, // B-spline knots in eta
359  const double *chi1vec, // B-spline knots in chi1
360  const double *chi2vec, // B-spline knots in chi2
361  gsl_vector *c_amp, // Output: interpolated projection coefficients for amplitude
362  gsl_vector *c_phi // Output: interpolated projection coefficients for phase
363  ) {
364  if (nk_max != -1) {
365  if (nk_max > nk_amp || nk_max > nk_phi)
366  XLAL_ERROR(XLAL_EDOM, "Truncation parameter nk_max %d must be smaller or equal to nk_amp %d and nk_phi %d", nk_max, nk_amp, nk_phi);
367  else { // truncate SVD modes
368  nk_amp = nk_max;
369  nk_phi = nk_max;
370  }
371  }
372 
373  SplineData *splinedata=NULL;
374  SplineData_Init(&splinedata, ncx, ncy, ncz, etavec, chi1vec, chi2vec);
375 
376  gsl_bspline_workspace *bwx=splinedata->bwx;
377  gsl_bspline_workspace *bwy=splinedata->bwy;
378  gsl_bspline_workspace *bwz=splinedata->bwz;
379 
380  int N = ncx*ncy*ncz; // Size of the data matrix for one SVD-mode
381  // Evaluate the TP spline for all SVD modes - amplitude
382  for (int k=0; k<nk_amp; k++) { // For each SVD mode
383  gsl_vector v = gsl_vector_subvector(cvec_amp, k*N, N).vector; // Pick out the coefficient matrix corresponding to the k-th SVD mode.
384  REAL8 csum = Interpolate_Coefficent_Tensor(&v, eta, chi1, chi2, ncy, ncz, bwx, bwy, bwz);
385  gsl_vector_set(c_amp, k, csum);
386  }
387 
388  // Evaluate the TP spline for all SVD modes - phase
389  for (int k=0; k<nk_phi; k++) { // For each SVD mode
390  gsl_vector v = gsl_vector_subvector(cvec_phi, k*N, N).vector; // Pick out the coefficient matrix corresponding to the k-th SVD mode.
391  REAL8 csum = Interpolate_Coefficent_Tensor(&v, eta, chi1, chi2, ncy, ncz, bwx, bwy, bwz);
392  gsl_vector_set(c_phi, k, csum);
393  }
394 
395  SplineData_Destroy(splinedata);
396 
397  return(0);
398 }
399 
400 /* Set up a new ROM submodel, using data contained in dir */
402  SEOBNRROMdataDS_submodel **submodel,
403  UNUSED const char dir[],
404  UNUSED const char grp_name[]
405 ) {
406  int ret = XLAL_FAILURE;
407 
408  if(!submodel) exit(1);
409  /* Create storage for submodel structures */
410  if (!*submodel)
411  *submodel = XLALCalloc(1,sizeof(SEOBNRROMdataDS_submodel));
412  else
414 
415 #ifdef LAL_HDF5_ENABLED
416  size_t size = strlen(dir) + strlen(ROMDataHDF5) + 2;
417  char *path = XLALMalloc(size);
418  snprintf(path, size, "%s/%s", dir, ROMDataHDF5);
419 
421  LALH5File *sub = XLALH5GroupOpen(file, grp_name);
422 
423  // Read ROM coefficients
424  ReadHDF5RealVectorDataset(sub, "Amp_ciall", & (*submodel)->cvec_amp);
425  ReadHDF5RealVectorDataset(sub, "Phase_ciall", & (*submodel)->cvec_phi);
426 
427  // Read ROM basis functions
428  ReadHDF5RealMatrixDataset(sub, "Bamp", & (*submodel)->Bamp);
429  ReadHDF5RealMatrixDataset(sub, "Bphase", & (*submodel)->Bphi);
430 
431  // Read sparse frequency points
432  ReadHDF5RealVectorDataset(sub, "Mf_grid_Amp", & (*submodel)->gA);
433  ReadHDF5RealVectorDataset(sub, "Mf_grid_Phi", & (*submodel)->gPhi);
434 
435  // Read parameter space nodes
436  ReadHDF5RealVectorDataset(sub, "etavec", & (*submodel)->etavec);
437  ReadHDF5RealVectorDataset(sub, "chi1vec", & (*submodel)->chi1vec);
438  ReadHDF5RealVectorDataset(sub, "chi2vec", & (*submodel)->chi2vec);
439 
440  // Initialize other members
441  (*submodel)->nk_amp = (*submodel)->gA->size;
442  (*submodel)->nk_phi = (*submodel)->gPhi->size;
443  (*submodel)->ncx = (*submodel)->etavec->size + 2;
444  (*submodel)->ncy = (*submodel)->chi1vec->size + 2;
445  (*submodel)->ncz = (*submodel)->chi2vec->size + 2;
446 
447  // Domain of definition of submodel
448  (*submodel)->eta_bounds[0] = gsl_vector_get((*submodel)->etavec, 0);
449  (*submodel)->eta_bounds[1] = gsl_vector_get((*submodel)->etavec, (*submodel)->etavec->size - 1);
450  (*submodel)->chi1_bounds[0] = gsl_vector_get((*submodel)->chi1vec, 0);
451  (*submodel)->chi1_bounds[1] = gsl_vector_get((*submodel)->chi1vec, (*submodel)->chi1vec->size - 1);
452  (*submodel)->chi2_bounds[0] = gsl_vector_get((*submodel)->chi2vec, 0);
453  (*submodel)->chi2_bounds[1] = gsl_vector_get((*submodel)->chi2vec, (*submodel)->chi2vec->size - 1);
454 
455  XLALFree(path);
457  ret = XLAL_SUCCESS;
458 #else
459  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
460 #endif
461 
462  return ret;
463 }
464 
465 /* Deallocate contents of the given SEOBNRROMdataDS_submodel structure */
466 static void SEOBNRROMdataDS_Cleanup_submodel(SEOBNRROMdataDS_submodel *submodel) {
467  if(submodel->cvec_amp) gsl_vector_free(submodel->cvec_amp);
468  if(submodel->cvec_phi) gsl_vector_free(submodel->cvec_phi);
469  if(submodel->Bamp) gsl_matrix_free(submodel->Bamp);
470  if(submodel->Bphi) gsl_matrix_free(submodel->Bphi);
471  if(submodel->gA) gsl_vector_free(submodel->gA);
472  if(submodel->gPhi) gsl_vector_free(submodel->gPhi);
473  if(submodel->etavec) gsl_vector_free(submodel->etavec);
474  if(submodel->chi1vec) gsl_vector_free(submodel->chi1vec);
475  if(submodel->chi2vec) gsl_vector_free(submodel->chi2vec);
476 }
477 
478 /* Set up a new ROM model, using data contained in dir */
480  UNUSED SEOBNRROMdataDS *romdata,
481  UNUSED const char dir[])
482 {
483  int ret = XLAL_FAILURE;
484 
485  /* Create storage for structures */
486  if(romdata->setup) {
487  XLALPrintError("WARNING: You tried to setup the SEOBNRv4ROM model that was already initialised. Ignoring\n");
488  return (XLAL_FAILURE);
489  }
490 
491 #ifdef LAL_HDF5_ENABLED
492  // First, check we got the correct version number
493  size_t size = strlen(dir) + strlen(ROMDataHDF5) + 2;
494  char *path = XLALMalloc(size);
495  snprintf(path, size, "%s/%s", dir, ROMDataHDF5);
497 
498  XLALPrintInfo("ROM metadata\n============\n");
499  PrintInfoStringAttribute(file, "Email");
500  PrintInfoStringAttribute(file, "Description");
501  ret = ROM_check_version_number(file, ROMDataHDF5_VERSION_MAJOR,
502  ROMDataHDF5_VERSION_MINOR,
503  ROMDataHDF5_VERSION_MICRO);
504 
505  XLALFree(path);
507 
508  ret |= SEOBNRROMdataDS_Init_submodel(&(romdata)->sub1, dir, "sub1");
509  if (ret==XLAL_SUCCESS) XLALPrintInfo("%s : submodel 1 loaded successfully.\n", __func__);
510 
511  ret |= SEOBNRROMdataDS_Init_submodel(&(romdata)->sub2, dir, "sub2");
512  if (ret==XLAL_SUCCESS) XLALPrintInfo("%s : submodel 2 loaded successfully.\n", __func__);
513 
514  ret |= SEOBNRROMdataDS_Init_submodel(&(romdata)->sub3, dir, "sub3");
515  if (ret==XLAL_SUCCESS) XLALPrintInfo("%s : submodel 3 loaded successfully.\n", __func__);
516 
517  if(XLAL_SUCCESS==ret)
518  romdata->setup=1;
519  else
520  SEOBNRROMdataDS_Cleanup(romdata);
521 #else
522  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
523 #endif
524 
525  return (ret);
526 }
527 
528 /* Deallocate contents of the given SEOBNRROMdataDS structure */
529 static void SEOBNRROMdataDS_Cleanup(SEOBNRROMdataDS *romdata) {
530  SEOBNRROMdataDS_Cleanup_submodel((romdata)->sub1);
531  XLALFree((romdata)->sub1);
532  (romdata)->sub1 = NULL;
533  SEOBNRROMdataDS_Cleanup_submodel((romdata)->sub2);
534  XLALFree((romdata)->sub2);
535  (romdata)->sub2 = NULL;
536  SEOBNRROMdataDS_Cleanup_submodel((romdata)->sub3);
537  XLALFree((romdata)->sub3);
538  (romdata)->sub3 = NULL;
539  romdata->setup=0;
540 }
541 
542 /* Structure for internal use */
543 static void SEOBNRROMdataDS_coeff_Init(SEOBNRROMdataDS_coeff **romdatacoeff, int nk_amp, int nk_phi) {
544  if(!romdatacoeff) exit(1);
545  /* Create storage for structures */
546  if(!*romdatacoeff)
547  *romdatacoeff=XLALCalloc(1,sizeof(SEOBNRROMdataDS_coeff));
548  else
549  SEOBNRROMdataDS_coeff_Cleanup(*romdatacoeff);
550 
551  (*romdatacoeff)->c_amp = gsl_vector_alloc(nk_amp);
552  (*romdatacoeff)->c_phi = gsl_vector_alloc(nk_phi);
553 }
554 
555 /* Deallocate contents of the given SEOBNRROMdataDS_coeff structure */
557  if(romdatacoeff->c_amp) gsl_vector_free(romdatacoeff->c_amp);
558  if(romdatacoeff->c_phi) gsl_vector_free(romdatacoeff->c_phi);
559  XLALFree(romdatacoeff);
560 }
561 
562 /* Return the closest higher power of 2 */
563 // Note: NextPow(2^k) = 2^k for integer values k.
564 static size_t NextPow2(const size_t n) {
565  return 1 << (size_t) ceil(log2(n));
566 }
567 
568 static void GlueAmplitude(
569  // INPUTS
570  SEOBNRROMdataDS_submodel *submodel_lo,
571  SEOBNRROMdataDS_submodel *submodel_hi,
572  gsl_vector* amp_f_lo,
573  gsl_vector* amp_f_hi,
574  // amp_pre_* can be set to 1 if not using amplitude prefactors
575  double amp_pre_lo,
576  double amp_pre_hi,
577  const double Mfm,
578  // OUTPUTS
579  gsl_interp_accel **acc_amp,
580  gsl_spline **spline_amp
581 ) {
582  // First need to find overlaping frequency interval
583  int jA_lo;
584  // Find index so that Mf < Mfm
585  for (jA_lo=0; jA_lo < submodel_lo->nk_amp; jA_lo++) {
586  if (gsl_vector_get(submodel_lo->gA, jA_lo) > Mfm) {
587  jA_lo--;
588  break;
589  }
590  }
591 
592  int jA_hi;
593  // Find index so that Mf > Mfm
594  for (jA_hi=0; jA_hi < submodel_hi->nk_amp; jA_hi++)
595  if (gsl_vector_get(submodel_hi->gA, jA_hi) > Mfm)
596  break;
597 
598  int nA = 1 + jA_lo + (submodel_hi->nk_amp - jA_hi); // length of the union of frequency points of the low and high frequency models glued at MfM
599 
600  gsl_vector *gAU = gsl_vector_alloc(nA); // glued frequency grid
601  gsl_vector *amp_f = gsl_vector_alloc(nA); // amplitude on glued frequency grid
602  // Note: We don't interpolate the amplitude, but this may already be smooth enough for practical purposes.
603  // To improve this we would evaluate both amplitue splines times the prefactor at the matching frequency and correct with the ratio, so we are C^0.
604  for (int i=0; i<=jA_lo; i++) {
605  gsl_vector_set(gAU, i, gsl_vector_get(submodel_lo->gA, i));
606  double A = amp_pre_lo * gsl_vector_get(amp_f_lo, i);
607  gsl_vector_set(amp_f, i, A);
608  }
609 
610  for (int i=jA_lo+1; i<nA; i++) {
611  int k = jA_hi - (jA_lo+1) + i;
612  gsl_vector_set(gAU, i, gsl_vector_get(submodel_hi->gA, k));
613  double A = amp_pre_hi * gsl_vector_get(amp_f_hi, k);
614  gsl_vector_set(amp_f, i, A);
615  }
616 
617  // Setup 1d splines in frequency from glued amplitude grids & data
618  *acc_amp = gsl_interp_accel_alloc();
619  *spline_amp = gsl_spline_alloc(gsl_interp_cspline, nA);
620  //gsl_spline_init(spline_amp, gAU->data, gsl_vector_const_ptr(amp_f,0), nA);
621  gsl_spline_init(*spline_amp, gsl_vector_const_ptr(gAU,0),
622  gsl_vector_const_ptr(amp_f,0), nA);
623 
624  gsl_vector_free(gAU);
625  gsl_vector_free(amp_f);
626  gsl_vector_free(amp_f_lo);
627  gsl_vector_free(amp_f_hi);
628 }
629 
630 // Glue phasing in frequency to C^1 smoothness
631 static void GluePhasing(
632  // INPUTS
633  SEOBNRROMdataDS_submodel *submodel_lo,
634  SEOBNRROMdataDS_submodel *submodel_hi,
635  gsl_vector* phi_f_lo,
636  gsl_vector* phi_f_hi,
637  const double Mfm,
638  // OUTPUTS
639  gsl_interp_accel **acc_phi,
640  gsl_spline **spline_phi
641 ) {
642  // First need to find overlaping frequency interval
643  int jP_lo;
644  // Find index so that Mf < Mfm
645  for (jP_lo=0; jP_lo < submodel_lo->nk_phi; jP_lo++) {
646  if (gsl_vector_get(submodel_lo->gPhi, jP_lo) > Mfm) {
647  jP_lo--;
648  break;
649  }
650  }
651 
652  int jP_hi;
653  // Find index so that Mf > Mfm
654  for (jP_hi=0; jP_hi < submodel_hi->nk_phi; jP_hi++)
655  if (gsl_vector_get(submodel_hi->gPhi, jP_hi) > Mfm)
656  break;
657 
658  int nP = 1 + jP_lo + (submodel_hi->nk_phi - jP_hi); // length of the union of frequency points of the low and high frequency models glued at MfM
659  gsl_vector *gPU = gsl_vector_alloc(nP); // glued frequency grid
660  gsl_vector *phi_f = gsl_vector_alloc(nP); // phase on glued frequency grid
661  // We need to do a bit more work to glue the phase with C^1 smoothness
662  for (int i=0; i<=jP_lo; i++) {
663  gsl_vector_set(gPU, i, gsl_vector_get(submodel_lo->gPhi, i));
664  double P = gsl_vector_get(phi_f_lo, i);
665  gsl_vector_set(phi_f, i, P);
666  }
667 
668  for (int i=jP_lo+1; i<nP; i++) {
669  int k = jP_hi - (jP_lo+1) + i;
670  gsl_vector_set(gPU, i, gsl_vector_get(submodel_hi->gPhi, k));
671  double P = gsl_vector_get(phi_f_hi, k);
672  gsl_vector_set(phi_f, i, P);
673  }
674 
675  // Set up phase data across the gluing frequency Mfm
676  // We need to set up a spline for the low frequency model and evaluate at the designated points for the high frequency model so that we work with the *same* frequeny interval!
677 
678  // We could optimize this further by not constructing the whole spline for
679  // submodel_lo, but this may be insignificant since the number of points is small anyway.
680  gsl_interp_accel *acc_phi_lo = gsl_interp_accel_alloc();
681  gsl_spline *spline_phi_lo = gsl_spline_alloc(gsl_interp_cspline, submodel_lo->nk_phi);
682  gsl_spline_init(spline_phi_lo, gsl_vector_const_ptr(submodel_lo->gPhi,0),
683  gsl_vector_const_ptr(phi_f_lo,0), submodel_lo->nk_phi);
684 
685  const int nn = 15;
686  gsl_vector_const_view gP_hi_data = gsl_vector_const_subvector(submodel_hi->gPhi, jP_hi - nn, 2*nn+1);
687  gsl_vector_const_view P_hi_data = gsl_vector_const_subvector(phi_f_hi, jP_hi - nn, 2*nn+1);
688  gsl_vector *P_lo_data = gsl_vector_alloc(2*nn+1);
689  for (int i=0; i<2*nn+1; i++) {
690  double P = gsl_spline_eval(spline_phi_lo, gsl_vector_get(&gP_hi_data.vector, i), acc_phi_lo);
691  gsl_vector_set(P_lo_data, i, P);
692  }
693 
694  // Fit phase data to cubic polynomial in frequency
695  gsl_vector *cP_lo = Fit_cubic(&gP_hi_data.vector, P_lo_data);
696  gsl_vector *cP_hi = Fit_cubic(&gP_hi_data.vector, &P_hi_data.vector);
697 
698  double P_lo_derivs[2];
699  double P_hi_derivs[2];
700  gsl_poly_eval_derivs(cP_lo->data, 4, Mfm, P_lo_derivs, 2);
701  gsl_poly_eval_derivs(cP_hi->data, 4, Mfm, P_hi_derivs, 2);
702 
703  double delta_omega = P_hi_derivs[1] - P_lo_derivs[1];
704  double delta_phi = P_hi_derivs[0] - P_lo_derivs[0] - delta_omega * Mfm;
705 
706  for (int i=jP_lo+1; i<nP; i++) {
707  int k = jP_hi - (jP_lo+1) + i;
708  double f = gsl_vector_get(submodel_hi->gPhi, k);
709  gsl_vector_set(gPU, i, f);
710  double P = gsl_vector_get(phi_f_hi, k) - delta_omega * f - delta_phi; // Now correct phase of high frequency submodel
711  gsl_vector_set(phi_f, i, P);
712  }
713 
714  // free some vectors
715  gsl_vector_free(P_lo_data);
716  gsl_vector_free(cP_lo);
717  gsl_vector_free(cP_hi);
718  gsl_vector_free(phi_f_lo);
719  gsl_vector_free(phi_f_hi);
720 
721  // Setup 1d splines in frequency from glued phase grids & data
722  *acc_phi = gsl_interp_accel_alloc();
723  *spline_phi = gsl_spline_alloc(gsl_interp_cspline, nP);
724  //gsl_spline_init(spline_phi, gPU->data, gsl_vector_const_ptr(phi_f,0), nP);
725  gsl_spline_init(*spline_phi, gsl_vector_const_ptr(gPU,0),
726  gsl_vector_const_ptr(phi_f,0), nP);
727 
728  /**** Finished gluing ****/
729 
730  gsl_vector_free(phi_f);
731  gsl_vector_free(gPU);
732  gsl_spline_free(spline_phi_lo);
733  gsl_interp_accel_free(acc_phi_lo);
734 }
735 
736 
737 
738 /**
739  * Core function for computing the ROM waveform.
740  * Interpolate projection coefficient data and evaluate coefficients at desired (q, chi1, chi2).
741  * Construct 1D splines for amplitude and phase.
742  * Compute strain waveform from amplitude and phase.
743 */
744 static int SEOBNRv4ROMCore(
745  COMPLEX16FrequencySeries **hptilde, /**< [out] Frequency-domain waveform h+ */
746  COMPLEX16FrequencySeries **hctilde, /**< [out] Frequency-domain waveform hx */
747  double phiRef, /**< orbital reference phase */
748  double fRef, /**< Reference frequency */
749  double distance, /**< Distance of source (m) */
750  double inclination, /**< Inclination of source */
751  double Mtot_sec, /**< Total mass of source in seconds */
752  double eta, /**< Symmetric mass ratio */
753  double chi1, /**< Dimensionless spin aligned spin on companion 1 */
754  double chi2, /**< Dimensionless aligned spin on companion 2 */
755  const REAL8Sequence *freqs_in, /**< Frequency points at which to evaluate the waveform (Hz) */
756  double deltaF, /**< Sampling frequency (Hz).
757  * If deltaF > 0, the frequency points given in freqs are uniformly spaced with
758  * spacing deltaF. Otherwise, the frequency points are spaced non-uniformly.
759  * Then we will use deltaF = 0 to create the frequency series we return. */
760  int nk_max, /**< truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
761  LALDict *LALparams, /**< LAL dictionary containing accessory parameters */
762  NRTidal_version_type NRTidal_version /**< NRTidal version; either NRTidal_V or NRTidalv2_V or NoNRT_V in case of BBH baseline */
763  )
764 {
765 
766  /* Check output arrays */
767  if(!hptilde || !hctilde)
769 
770  SEOBNRROMdataDS *romdata=&__lalsim_SEOBNRv4ROMDS_data;
771  if (!SEOBNRv4ROM_IsSetup()) {
773  "Error setting up SEOBNRv4ROM data - check your $LAL_DATA_PATH\n");
774  }
775 
776  if(*hptilde || *hctilde) {
777  XLALPrintError("(*hptilde) and (*hctilde) are supposed to be NULL, but got %p and %p",
778  (*hptilde), (*hctilde));
780  }
781  int retcode=0;
782 
783  // 'Nudge' parameter values to allowed boundary values if close by
784  if (eta > 0.25) nudge(&eta, 0.25, 1e-6);
785  if (eta < 0.01) nudge(&eta, 0.01, 1e-6);
786 
787  if ( chi1 < -1.0 || chi2 < -1.0 || chi1 > 1.0 || chi2 > 1.0) {
788  XLALPrintError("XLAL Error - %s: chi1 or chi2 smaller than -1.0 or larger than 1.0!\n"
789  "SEOBNRv4ROM is only available for spins in the range -1 <= a/M <= 1.0.\n",
790  __func__);
792  }
793 
794  if (eta<0.01 || eta > 0.25) {
795  XLALPrintError("XLAL Error - %s: eta (%f) smaller than 0.01 or unphysical!\n"
796  "SEOBNRv4ROM is only available for eta in the range 0.01 <= eta <= 0.25.\n",
797  __func__, eta);
799  }
800 
801  /* We always need to glue two submodels together for this ROM */
802  SEOBNRROMdataDS_submodel *submodel_hi; // high frequency ROM
803  SEOBNRROMdataDS_submodel *submodel_lo; // low frequency ROM
804  submodel_lo = romdata->sub1;
805 
806  /* Select high frequency ROM submodel */
807  if (chi1 < romdata->sub3->chi1_bounds[0] || eta > romdata->sub3->eta_bounds[1]) // only check the two conditions that apply for this ROM; could be more general, but slower
808  submodel_hi = romdata->sub2;
809  else
810  submodel_hi = romdata->sub3;
811 
812 
813  /* Find frequency bounds */
814  if (!freqs_in) XLAL_ERROR(XLAL_EFAULT);
815  double fLow = freqs_in->data[0];
816  double fHigh = freqs_in->data[freqs_in->length - 1];
817 
818  if(fRef==0.0)
819  fRef=fLow;
820 
821  /* Convert to geometric units for frequency */
822  // lowest allowed geometric frequency for ROM
823  double Mf_ROM_min = fmax(gsl_vector_get(submodel_lo->gA, 0),
824  gsl_vector_get(submodel_lo->gPhi,0));
825  // highest allowed geometric frequency for ROM
826  double Mf_ROM_max = fmin(gsl_vector_get(submodel_hi->gA, submodel_hi->nk_amp-1),
827  gsl_vector_get(submodel_hi->gPhi, submodel_hi->nk_phi-1));
828  double fLow_geom = fLow * Mtot_sec;
829  double fHigh_geom = fHigh * Mtot_sec;
830  double fRef_geom = fRef * Mtot_sec;
831  double deltaF_geom = deltaF * Mtot_sec;
832 
833  // Enforce allowed geometric frequency range
834  if (fLow_geom < Mf_ROM_min)
835  XLAL_ERROR(XLAL_EDOM, "Starting frequency Mflow=%g is smaller than lowest frequency in ROM Mf=%g.\n", fLow_geom, Mf_ROM_min);
836  if (fHigh_geom == 0 || fHigh_geom > Mf_ROM_max)
837  fHigh_geom = Mf_ROM_max;
838  else if (fHigh_geom < Mf_ROM_min)
839  XLAL_ERROR(XLAL_EDOM, "End frequency %g is smaller than ROM starting frequency %g!\n", fHigh_geom, Mf_ROM_min);
840  if (fHigh_geom <= fLow_geom)
841  XLAL_ERROR(XLAL_EDOM, "End frequency %g is smaller than (or equal to) starting frequency %g!\n", fHigh_geom, fLow_geom);
842  if (fRef_geom > Mf_ROM_max) {
843  XLALPrintWarning("Reference frequency Mf_ref=%g is greater than maximal frequency in ROM Mf=%g. Starting at maximal frequency in ROM.\n", fRef_geom, Mf_ROM_max);
844  fRef_geom = Mf_ROM_max; // If fref > fhigh we reset fref to default value of cutoff frequency.
845  }
846  if (fRef_geom < Mf_ROM_min) {
847  XLALPrintWarning("Reference frequency Mf_ref=%g is smaller than lowest frequency in ROM Mf=%g. Starting at lowest frequency in ROM.\n", fLow_geom, Mf_ROM_min);
848  fRef_geom = Mf_ROM_min;
849  }
850 
851  if (Mtot_sec/LAL_MTSUN_SI > 500.0)
852  XLALPrintWarning("Total mass=%gMsun > 500Msun. SEOBNRv4ROM disagrees with SEOBNRv4 for high total masses.\n", Mtot_sec/LAL_MTSUN_SI);
853 
854  /* Internal storage for waveform coefficiencts */
855  SEOBNRROMdataDS_coeff *romdata_coeff_lo=NULL;
856  SEOBNRROMdataDS_coeff *romdata_coeff_hi=NULL;
857  SEOBNRROMdataDS_coeff_Init(&romdata_coeff_lo, submodel_lo->nk_amp, submodel_lo->nk_phi);
858  SEOBNRROMdataDS_coeff_Init(&romdata_coeff_hi, submodel_hi->nk_amp, submodel_hi->nk_phi);
859  REAL8 amp_pre_lo = 1.0; // unused here
860  REAL8 amp_pre_hi = 1.0;
861 
862  /* Interpolate projection coefficients and evaluate them at (eta,chi1,chi2) */
864  eta, // Input: eta-value for which projection coefficients should be evaluated
865  chi1, // Input: chi1-value for which projection coefficients should be evaluated
866  chi2, // Input: chi2-value for which projection coefficients should be evaluated
867  submodel_lo->cvec_amp, // Input: data for spline coefficients for amplitude
868  submodel_lo->cvec_phi, // Input: data for spline coefficients for phase
869  submodel_lo->nk_amp, // number of SVD-modes == number of basis functions for amplitude
870  submodel_lo->nk_phi, // number of SVD-modes == number of basis functions for phase
871  nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
872  submodel_lo->ncx, // Number of points in eta + 2
873  submodel_lo->ncy, // Number of points in chi1 + 2
874  submodel_lo->ncz, // Number of points in chi2 + 2
875  gsl_vector_const_ptr(submodel_lo->etavec, 0), // B-spline knots in eta
876  gsl_vector_const_ptr(submodel_lo->chi1vec, 0), // B-spline knots in chi1
877  gsl_vector_const_ptr(submodel_lo->chi2vec, 0), // B-spline knots in chi2
878  romdata_coeff_lo->c_amp, // Output: interpolated projection coefficients for amplitude
879  romdata_coeff_lo->c_phi // Output: interpolated projection coefficients for phase
880  );
881 
882  if(retcode!=0) {
883  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
884  XLAL_ERROR(retcode);
885  }
886 
887  /* Interpolate projection coefficients and evaluate them at (eta,chi1,chi2) */
889  eta, // Input: eta-value for which projection coefficients should be evaluated
890  chi1, // Input: chi1-value for which projection coefficients should be evaluated
891  chi2, // Input: chi2-value for which projection coefficients should be evaluated
892  submodel_hi->cvec_amp, // Input: data for spline coefficients for amplitude
893  submodel_hi->cvec_phi, // Input: data for spline coefficients for phase
894  submodel_hi->nk_amp, // number of SVD-modes == number of basis functions for amplitude
895  submodel_hi->nk_phi, // number of SVD-modes == number of basis functions for phase
896  nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
897  submodel_hi->ncx, // Number of points in eta + 2
898  submodel_hi->ncy, // Number of points in chi1 + 2
899  submodel_hi->ncz, // Number of points in chi2 + 2
900  gsl_vector_const_ptr(submodel_hi->etavec, 0), // B-spline knots in eta
901  gsl_vector_const_ptr(submodel_hi->chi1vec, 0), // B-spline knots in chi1
902  gsl_vector_const_ptr(submodel_hi->chi2vec, 0), // B-spline knots in chi2
903  romdata_coeff_hi->c_amp, // Output: interpolated projection coefficients for amplitude
904  romdata_coeff_hi->c_phi // Output: interpolated projection coefficients for phase
905  );
906 
907  if(retcode!=0) {
908  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
909  XLAL_ERROR(retcode);
910  }
911 
912 
913  // Compute function values of amplitude an phase on sparse frequency points by evaluating matrix vector products
914  // amp_pts = B_A^T . c_A
915  // phi_pts = B_phi^T . c_phi
916  gsl_vector* amp_f_lo = gsl_vector_alloc(submodel_lo->nk_amp);
917  gsl_vector* phi_f_lo = gsl_vector_alloc(submodel_lo->nk_phi);
918  gsl_blas_dgemv(CblasTrans, 1.0, submodel_lo->Bamp, romdata_coeff_lo->c_amp, 0.0, amp_f_lo);
919  gsl_blas_dgemv(CblasTrans, 1.0, submodel_lo->Bphi, romdata_coeff_lo->c_phi, 0.0, phi_f_lo);
920 
921  gsl_vector* amp_f_hi = gsl_vector_alloc(submodel_hi->nk_amp);
922  gsl_vector* phi_f_hi = gsl_vector_alloc(submodel_hi->nk_phi);
923  gsl_blas_dgemv(CblasTrans, 1.0, submodel_hi->Bamp, romdata_coeff_hi->c_amp, 0.0, amp_f_hi);
924  gsl_blas_dgemv(CblasTrans, 1.0, submodel_hi->Bphi, romdata_coeff_hi->c_phi, 0.0, phi_f_hi);
925 
926  const double Mfm = 0.01; // Gluing frequency: the low and high frequency ROMs overlap here; this is used both for amplitude and phase.
927 
928  // Glue amplitude
929  gsl_interp_accel *acc_amp;
930  gsl_spline *spline_amp;
931  GlueAmplitude(submodel_lo, submodel_hi, amp_f_lo, amp_f_hi, amp_pre_lo, amp_pre_hi, Mfm,
932  &acc_amp, &spline_amp
933  );
934 
935  // Glue phasing in frequency to C^1 smoothness
936  gsl_interp_accel *acc_phi;
937  gsl_spline *spline_phi;
938  GluePhasing(submodel_lo, submodel_hi, phi_f_lo, phi_f_hi, Mfm,
939  &acc_phi, &spline_phi
940  );
941 
942  size_t npts = 0;
943  LIGOTimeGPS tC = {0, 0};
944  UINT4 offset = 0; // Index shift between freqs and the frequency series
945  REAL8Sequence *freqs = NULL;
946  REAL8Sequence *amp_tidal = NULL; /* Tidal amplitude series; required only for SEOBNRv4_ROM_NRTidalv2 */
947  if (deltaF > 0) { // freqs contains uniform frequency grid with spacing deltaF; we start at frequency 0
948  /* Set up output array with size closest power of 2 */
949  npts = NextPow2(fHigh_geom / deltaF_geom) + 1;
950  if (fHigh_geom < fHigh * Mtot_sec) /* Resize waveform if user wants f_max larger than cutoff frequency */
951  npts = NextPow2(fHigh * Mtot_sec / deltaF_geom) + 1;
952 
953  XLALGPSAdd(&tC, -1. / deltaF); /* coalesce at t=0 */
954  *hptilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &tC, 0.0, deltaF, &lalStrainUnit, npts);
955  *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &tC, 0.0, deltaF, &lalStrainUnit, npts);
956 
957  // Recreate freqs using only the lower and upper bounds
958  // Use fLow, fHigh and deltaF rather than geometric frequencies for numerical accuracy
959  double fHigh_temp = fHigh_geom / Mtot_sec;
960  UINT4 iStart = (UINT4) ceil(fLow / deltaF);
961  UINT4 iStop = (UINT4) ceil(fHigh_temp / deltaF);
962  freqs = XLALCreateREAL8Sequence(iStop - iStart);
963  if (!freqs) {
964  XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
965  }
966  for (UINT4 i=iStart; i<iStop; i++)
967  freqs->data[i-iStart] = i*deltaF_geom;
968 
969  offset = iStart;
970  } else { // freqs contains frequencies with non-uniform spacing; we start at lowest given frequency
971  npts = freqs_in->length;
972  *hptilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &tC, fLow, 0, &lalStrainUnit, npts);
973  *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &tC, fLow, 0, &lalStrainUnit, npts);
974  offset = 0;
975 
976  freqs = XLALCreateREAL8Sequence(freqs_in->length);
977  if (!freqs) {
978  XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
979  }
980  for (UINT4 i=0; i<freqs_in->length; i++)
981  freqs->data[i] = freqs_in->data[i] * Mtot_sec;
982  }
983 
984  if (!(*hptilde) || !(*hctilde)) {
986  gsl_spline_free(spline_amp);
987  gsl_spline_free(spline_phi);
988  gsl_interp_accel_free(acc_amp);
989  gsl_interp_accel_free(acc_phi);
990  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
991  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
993  }
994  memset((*hptilde)->data->data, 0, npts * sizeof(COMPLEX16));
995  memset((*hctilde)->data->data, 0, npts * sizeof(COMPLEX16));
996 
997  XLALUnitMultiply(&(*hptilde)->sampleUnits, &(*hptilde)->sampleUnits, &lalSecondUnit);
998  XLALUnitMultiply(&(*hctilde)->sampleUnits, &(*hctilde)->sampleUnits, &lalSecondUnit);
999 
1000  COMPLEX16 *pdata=(*hptilde)->data->data;
1001  COMPLEX16 *cdata=(*hctilde)->data->data;
1002 
1003  REAL8 cosi = cos(inclination);
1004  REAL8 pcoef = 0.5*(1.0 + cosi*cosi);
1005  REAL8 ccoef = cosi;
1006 
1007  REAL8 s = 0.5; // Scale polarization amplitude so that strain agrees with FFT of SEOBNRv4
1008  double Mtot = Mtot_sec / LAL_MTSUN_SI;
1009  double amp0 = Mtot * Mtot_sec * LAL_MRSUN_SI / (distance); // Correct overall amplitude to undo mass-dependent scaling used in ROM
1010 
1011  // Evaluate reference phase for setting phiRef correctly
1012  double phase_change = gsl_spline_eval(spline_phi, fRef_geom, acc_phi) - 2*phiRef;
1013 
1014  int ret = XLAL_SUCCESS;
1015  // Assemble waveform from aplitude and phase
1016  if (NRTidal_version == NRTidalv2_V) {
1017  /* get component masses (in solar masses) from mtotal and eta! */
1018  const REAL8 factor = sqrt(1. - 4.*eta);
1019  const REAL8 m1 = 0.5*Mtot*(1.+ factor);
1020  const REAL8 m2 = 0.5*Mtot*(1.- factor);
1021  /* Generate the tidal amplitude (Eq. 24 of arxiv: 1905.06011) to add to BBH baseline; only for NRTidalv2 */
1022  amp_tidal = XLALCreateREAL8Sequence(freqs->length);
1025 
1026  ret = XLALSimNRTunedTidesFDTidalAmplitudeFrequencySeries(amp_tidal, freqs, m1, m2, l1, l2);
1027  XLAL_CHECK(XLAL_SUCCESS == ret, ret, "Failed to generate tidal amplitude series to construct SEOBNRv4_ROM_NRTidalv2 waveform.");
1028  /* Generated tidal amplitude corrections */
1029  for (UINT4 i=0; i<freqs->length; i++) { // loop over frequency points in sequence
1030  double f = freqs->data[i];
1031  double ampT = amp_tidal->data[i];
1032 
1033  if (f > Mf_ROM_max) continue; // We're beyond the highest allowed frequency; since freqs may not be ordered, we'll just skip the current frequency and leave zero in the buffer
1034  int j = i + offset; // shift index for frequency series if needed
1035  double A = gsl_spline_eval(spline_amp, f, acc_amp);
1036  double phase = gsl_spline_eval(spline_phi, f, acc_phi) - phase_change;
1037  COMPLEX16 htilde = s*amp0*(A+ampT) * (cos(phase) + I*sin(phase)); //cexp(I*phase);
1038  pdata[j] = pcoef * htilde;
1039  cdata[j] = -I * ccoef * htilde;
1040  }
1041  } else {
1042  for (UINT4 i=0; i<freqs->length; i++) { // loop over frequency points in sequence
1043  double f = freqs->data[i];
1044  if (f > Mf_ROM_max) continue; // We're beyond the highest allowed frequency; since freqs may not be ordered, we'll just skip the current frequency and leave zero in the buffer
1045  int j = i + offset; // shift index for frequency series if needed
1046  double A = gsl_spline_eval(spline_amp, f, acc_amp);
1047  double phase = gsl_spline_eval(spline_phi, f, acc_phi) - phase_change;
1048  COMPLEX16 htilde = s*amp0*A * (cos(phase) + I*sin(phase));//cexp(I*phase);
1049 
1050  pdata[j] = pcoef * htilde;
1051  cdata[j] = -I * ccoef * htilde;
1052  }
1053  }
1054 
1055  /* Correct phasing so we coalesce at t=0 (with the definition of the epoch=-1/deltaF above) */
1056 
1057  // Get SEOBNRv4 ringdown frequency for 22 mode
1058  double Mf_final = SEOBNRROM_Ringdown_Mf_From_Mtot_Eta(Mtot_sec, eta, chi1,
1059  chi2, SEOBNRv4);
1060 
1061  // prevent gsl interpolation errors
1062  // The ringdown frequency Mf_final is only used to evaluate the spline_phi
1063  // derivative below and spline_phi has domain [Mf_ROM_min, Mf_ROM_max].
1064  // Mf_final should always be inside this interval, but we'll check anyway.
1065  if (Mf_final > Mf_ROM_max)
1066  Mf_final = Mf_ROM_max;
1067  if (Mf_final < Mf_ROM_min) {
1068  XLALDestroyREAL8Sequence(freqs);
1069  gsl_spline_free(spline_amp);
1070  gsl_spline_free(spline_phi);
1071  gsl_interp_accel_free(acc_amp);
1072  gsl_interp_accel_free(acc_phi);
1073  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1074  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1075  XLAL_ERROR(XLAL_EDOM, "f_ringdown < f_min");
1076  }
1077 
1078  // Time correction is t(f_final) = 1/(2pi) dphi/df (f_final)
1079  // We compute the dimensionless time correction t/M since we use geometric units.
1080  REAL8 t_corr = gsl_spline_eval_deriv(spline_phi, Mf_final, acc_phi) / (2*LAL_PI);
1081 
1082  // Now correct phase
1083  for (UINT4 i=0; i<freqs->length; i++) { // loop over frequency points in sequence
1084  double f = freqs->data[i] - fRef_geom;
1085  int j = i + offset; // shift index for frequency series if needed
1086  double phase_factor = -2*LAL_PI * f * t_corr;
1087  COMPLEX16 t_factor = (cos(phase_factor) + I*sin(phase_factor));//cexp(I*phase_factor);
1088  pdata[j] *= t_factor;
1089  cdata[j] *= t_factor;
1090  }
1091 
1092  XLALDestroyREAL8Sequence(freqs);
1093  XLALDestroyREAL8Sequence(amp_tidal);
1094 
1095  gsl_spline_free(spline_amp);
1096  gsl_spline_free(spline_phi);
1097  gsl_interp_accel_free(acc_amp);
1098  gsl_interp_accel_free(acc_phi);
1099  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1100  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1101 
1102  return(XLAL_SUCCESS);
1103 }
1104 
1105 /**
1106  * @addtogroup LALSimIMRSEOBNRROM_c
1107  *
1108  * \author Michael Puerrer
1109  *
1110  * \brief C code for SEOBNRv4 reduced order model
1111  * See CQG 31 195010, 2014, arXiv:1402.4146 for the basic approach.
1112  * Further details in PRD 93, 064041, 2016, arXiv:1512.02248.
1113  *
1114  * This is a frequency domain model that approximates the time domain SEOBNRv4 model.
1115  *
1116  * The binary data HDF5 file (SEOBNRv4ROM_DS_HI_v1.0.hdf5)
1117  * will be available at on LIGO clusters in /home/cbc/.
1118  * Make sure the files are in your LAL_DATA_PATH.
1119  *
1120  * @note Note that due to its construction the iFFT of the ROM has a small (~ 20 M) offset
1121  * in the peak time that scales with total mass as compared to the time-domain SEOBNRv4 model.
1122  *
1123  * @note Parameter ranges:
1124  * * 0.01 <= eta <= 0.25
1125  * * -1 <= chi_i <= 1.0
1126  * * 2Msun (@ flow=20Hz) <= Mtot < 500Msun
1127  *
1128  * Aligned component spins chi1, chi2.
1129  * Symmetric mass-ratio eta = m1*m2/(m1+m2)^2.
1130  * Total mass Mtot.
1131  *
1132  * This ROM consists of three submodels and glues together one low-mass and 2 high-mass models
1133  * These submodels and their boundaries are not explicit in the source, just in the HDF5 data file.
1134  *
1135  * @{
1136  */
1137 
1138 
1139 /**
1140  * Compute waveform in LAL format at specified frequencies for the SEOBNRv4_ROM model.
1141  *
1142  * XLALSimIMRSEOBNRv4ROM() returns the plus and cross polarizations as a complex
1143  * frequency series with equal spacing deltaF and contains zeros from zero frequency
1144  * to the starting frequency and zeros beyond the cutoff frequency in the ringdown.
1145  *
1146  * In contrast, XLALSimIMRSEOBNRv4ROMFrequencySequence() returns a
1147  * complex frequency series with entries exactly at the frequencies specified in
1148  * the sequence freqs (which can be unequally spaced). No zeros are added.
1149  *
1150  * If XLALSimIMRSEOBNRv4ROMFrequencySequence() is called with frequencies that
1151  * are beyond the maxium allowed geometric frequency for the ROM, zero strain is returned.
1152  * It is not assumed that the frequency sequence is ordered.
1153  *
1154  * This function is designed as an entry point for reduced order quadratures.
1155  */
1157  struct tagCOMPLEX16FrequencySeries **hptilde, /**< Output: Frequency-domain waveform h+ */
1158  struct tagCOMPLEX16FrequencySeries **hctilde, /**< Output: Frequency-domain waveform hx */
1159  const REAL8Sequence *freqs, /**< Frequency points at which to evaluate the waveform (Hz) */
1160  REAL8 phiRef, /**< Orbital phase at reference time */
1161  REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
1162  REAL8 distance, /**< Distance of source (m) */
1163  REAL8 inclination, /**< Inclination of source (rad) */
1164  REAL8 m1SI, /**< Mass of companion 1 (kg) */
1165  REAL8 m2SI, /**< Mass of companion 2 (kg) */
1166  REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1167  REAL8 chi2, /**< Dimensionless aligned component spin 2 */
1168  INT4 nk_max, /**< Truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
1169  LALDict *LALparams, /**< LAL dictionary containing accessory parameters */
1170  NRTidal_version_type NRTidal_version /**< NRTidal version; either NRTidal_V or NRTidalv2_V or NoNRT_V in case of BBH baseline */
1171 )
1172 {
1173  /* Internally we need m1 > m2, so change around if this is not the case */
1174  if (m1SI < m2SI) {
1175  // Swap m1 and m2
1176  double m1temp = m1SI;
1177  double chi1temp = chi1;
1178  m1SI = m2SI;
1179  chi1 = chi2;
1180  m2SI = m1temp;
1181  chi2 = chi1temp;
1182  }
1183 
1184  /* Get masses in terms of solar mass */
1185  double mass1 = m1SI / LAL_MSUN_SI;
1186  double mass2 = m2SI / LAL_MSUN_SI;
1187  double Mtot = mass1+mass2;
1188  double eta = mass1 * mass2 / (Mtot*Mtot); /* Symmetric mass-ratio */
1189  double Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
1190 
1191  if (!freqs) XLAL_ERROR(XLAL_EFAULT);
1192 
1193  // Load ROM data if not loaded already
1194 #ifdef LAL_PTHREAD_LOCK
1195  (void) pthread_once(&SEOBNRv4ROM_is_initialized, SEOBNRv4ROM_Init_LALDATA);
1196 #else
1198 #endif
1199 
1200  if(!SEOBNRv4ROM_IsSetup()) {
1202  "Error setting up SEOBNRv4ROM data - check your $LAL_DATA_PATH\n");
1203  }
1204 
1205  // Call the internal core function with deltaF = 0 to indicate that freqs is non-uniformly
1206  // spaced and we want the strain only at these frequencies
1207  int retcode = SEOBNRv4ROMCore(hptilde, hctilde, phiRef, fRef, distance,
1208  inclination, Mtot_sec, eta, chi1, chi2, freqs,
1209  0, nk_max, LALparams, NRTidal_version);
1210 
1211  return(retcode);
1212 }
1213 
1214 /**
1215  * Compute waveform in LAL format for the SEOBNRv4_ROM model.
1216  *
1217  * Returns the plus and cross polarizations as a complex frequency series with
1218  * equal spacing deltaF and contains zeros from zero frequency to the starting
1219  * frequency fLow and zeros beyond the cutoff frequency in the ringdown.
1220  */
1222  struct tagCOMPLEX16FrequencySeries **hptilde, /**< Output: Frequency-domain waveform h+ */
1223  struct tagCOMPLEX16FrequencySeries **hctilde, /**< Output: Frequency-domain waveform hx */
1224  REAL8 phiRef, /**< Phase at reference time */
1225  REAL8 deltaF, /**< Sampling frequency (Hz) */
1226  REAL8 fLow, /**< Starting GW frequency (Hz) */
1227  REAL8 fHigh, /**< End frequency; 0 defaults to Mf=0.14 */
1228  REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
1229  REAL8 distance, /**< Distance of source (m) */
1230  REAL8 inclination, /**< Inclination of source (rad) */
1231  REAL8 m1SI, /**< Mass of companion 1 (kg) */
1232  REAL8 m2SI, /**< Mass of companion 2 (kg) */
1233  REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1234  REAL8 chi2, /**< Dimensionless aligned component spin 2 */
1235  INT4 nk_max, /**< Truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
1236  LALDict *LALparams, /**< LAL dictionary containing accessory parameters */
1237  NRTidal_version_type NRTidal_version /**< NRTidal version; either NRTidal_V or NRTidalv2_V or NoNRT_V in case of BBH baseline */
1238 )
1239 {
1240  /* Internally we need m1 > m2, so change around if this is not the case */
1241  if (m1SI < m2SI) {
1242  // Swap m1 and m2
1243  double m1temp = m1SI;
1244  double chi1temp = chi1;
1245  m1SI = m2SI;
1246  chi1 = chi2;
1247  m2SI = m1temp;
1248  chi2 = chi1temp;
1249  }
1250 
1251  /* Get masses in terms of solar mass */
1252  double mass1 = m1SI / LAL_MSUN_SI;
1253  double mass2 = m2SI / LAL_MSUN_SI;
1254  double Mtot = mass1+mass2;
1255  double eta = mass1 * mass2 / (Mtot*Mtot); /* Symmetric mass-ratio */
1256  double Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
1257 
1258  if(fRef==0.0)
1259  fRef=fLow;
1260 
1261  // Load ROM data if not loaded already
1262 #ifdef LAL_PTHREAD_LOCK
1263  (void) pthread_once(&SEOBNRv4ROM_is_initialized, SEOBNRv4ROM_Init_LALDATA);
1264 #else
1266 #endif
1267 
1268  // Use fLow, fHigh, deltaF to compute freqs sequence
1269  // Instead of building a full sequency we only transfer the boundaries and let
1270  // the internal core function do the rest (and properly take care of corner cases).
1272  freqs->data[0] = fLow;
1273  freqs->data[1] = fHigh;
1274 
1275  int retcode = SEOBNRv4ROMCore(hptilde, hctilde, phiRef, fRef, distance,
1276  inclination, Mtot_sec, eta, chi1, chi2, freqs,
1277  deltaF, nk_max, LALparams, NRTidal_version);
1278 
1279  XLALDestroyREAL8Sequence(freqs);
1280 
1281  return(retcode);
1282 }
1283 
1284 /** @} */
1285 
1286 // Auxiliary function to perform setup of phase spline for t(f) and f(t) functions
1288  gsl_spline **spline_phi, // phase spline
1289  gsl_interp_accel **acc_phi, // phase spline accelerator
1290  REAL8 *Mf_final, // ringdown frequency in Mf
1291  REAL8 *Mtot_sec, // total mass in seconds
1292  REAL8 m1SI, // Mass of companion 1 (kg)
1293  REAL8 m2SI, // Mass of companion 2 (kg)
1294  REAL8 chi1, // Aligned spin of companion 1
1295  REAL8 chi2, // Aligned spin of companion 2
1296  REAL8 *Mf_ROM_min, // Lowest geometric frequency for ROM
1297  REAL8 *Mf_ROM_max // Highest geometric frequency for ROM
1298 )
1299 {
1300  /* Get masses in terms of solar mass */
1301  double mass1 = m1SI / LAL_MSUN_SI;
1302  double mass2 = m2SI / LAL_MSUN_SI;
1303  double Mtot = mass1 + mass2;
1304  double eta = mass1 * mass2 / (Mtot*Mtot); /* Symmetric mass-ratio */
1305  *Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
1306 
1307  // 'Nudge' parameter values to allowed boundary values if close by
1308  if (eta > 0.25) nudge(&eta, 0.25, 1e-6);
1309  if (eta < 0.01) nudge(&eta, 0.01, 1e-6);
1310 
1311  if ( chi1 < -1.0 || chi2 < -1.0 || chi1 > 1.0 || chi2 > 1.0) {
1312  XLALPrintError( "XLAL Error - %s: chi1 or chi2 smaller than -1.0 or larger than 1.0!\nSEOBNRv4ROM is only available for spins in the range -1 <= a/M <= 1.0.\n", __func__);
1313  XLAL_ERROR( XLAL_EDOM );
1314  }
1315 
1316  if (eta < 0.01 || eta > 0.25) {
1317  XLALPrintError( "XLAL Error - %s: eta (%f) smaller than 0.01 or unphysical!\nSEOBNRv4ROM is only available for symmetric mass-ratios in the range 0.01 <= eta <= 0.25.\n", __func__,eta);
1318  XLAL_ERROR( XLAL_EDOM );
1319  }
1320 
1321  // Load ROM data if not loaded already
1322 #ifdef LAL_PTHREAD_LOCK
1323  (void) pthread_once(&SEOBNRv4ROM_is_initialized, SEOBNRv4ROM_Init_LALDATA);
1324 #else
1326 #endif
1327 
1328  SEOBNRROMdataDS *romdata=&__lalsim_SEOBNRv4ROMDS_data;
1329  if (!SEOBNRv4ROM_IsSetup()) {
1331  "Error setting up SEOBNRv4ROM data - check your $LAL_DATA_PATH\n");
1332  }
1333 
1334  /* We always need to glue two submodels together for this ROM */
1335  SEOBNRROMdataDS_submodel *submodel_hi; // high frequency ROM
1336  SEOBNRROMdataDS_submodel *submodel_lo; // low frequency ROM
1337  submodel_lo = romdata->sub1;
1338 
1339  /* Select high frequency ROM submodel */
1340  if (chi1 < romdata->sub3->chi1_bounds[0] || eta > romdata->sub3->eta_bounds[1]) // only check the two conditions that apply for this ROM; could be more general, but slower
1341  submodel_hi = romdata->sub2;
1342  else
1343  submodel_hi = romdata->sub3;
1344 
1345  /* Internal storage for waveform coefficiencts */
1346  SEOBNRROMdataDS_coeff *romdata_coeff_lo=NULL;
1347  SEOBNRROMdataDS_coeff *romdata_coeff_hi=NULL;
1348  SEOBNRROMdataDS_coeff_Init(&romdata_coeff_lo, submodel_lo->nk_amp,
1349  submodel_lo->nk_phi);
1350  SEOBNRROMdataDS_coeff_Init(&romdata_coeff_hi, submodel_hi->nk_amp,
1351  submodel_hi->nk_phi);
1352 
1353  // lowest allowed geometric frequency for ROM
1354  *Mf_ROM_min = fmax(gsl_vector_get(submodel_lo->gA, 0),
1355  gsl_vector_get(submodel_lo->gPhi,0));
1356  // highest allowed geometric frequency for ROM
1357  *Mf_ROM_max = fmin(gsl_vector_get(submodel_hi->gA, submodel_hi->nk_amp-1),
1358  gsl_vector_get(submodel_hi->gPhi, submodel_hi->nk_phi-1));
1359 
1360 
1361  /* Interpolate projection coefficients and evaluate them at (eta,chi1,chi2) */
1362  int nk_max = -1; // adjust truncation parameter if speed is an issue
1363  int retcode=TP_Spline_interpolation_3d(
1364  eta, // Input: eta-value for which projection coefficients should be evaluated
1365  chi1, // Input: chi1-value for which projection coefficients should be evaluated
1366  chi2, // Input: chi2-value for which projection coefficients should be evaluated
1367  submodel_lo->cvec_amp, // Input: data for spline coefficients for amplitude
1368  submodel_lo->cvec_phi, // Input: data for spline coefficients for phase
1369  submodel_lo->nk_amp, // number of SVD-modes == number of basis functions for amplitude
1370  submodel_lo->nk_phi, // number of SVD-modes == number of basis functions for phase
1371  nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1372  submodel_lo->ncx, // Number of points in eta + 2
1373  submodel_lo->ncy, // Number of points in chi1 + 2
1374  submodel_lo->ncz, // Number of points in chi2 + 2
1375  gsl_vector_const_ptr(submodel_lo->etavec, 0), // B-spline knots in eta
1376  gsl_vector_const_ptr(submodel_lo->chi1vec, 0), // B-spline knots in chi1
1377  gsl_vector_const_ptr(submodel_lo->chi2vec, 0), // B-spline knots in chi2
1378  romdata_coeff_lo->c_amp, // Output: interpolated projection coefficients for amplitude
1379  romdata_coeff_lo->c_phi // Output: interpolated projection coefficients for phase
1380  );
1381 
1382  if(retcode!=0) {
1383  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1384  XLAL_ERROR(retcode);
1385  }
1386 
1387  /* Interpolate projection coefficients and evaluate them at (eta,chi1,chi2) */
1389  eta, // Input: eta-value for which projection coefficients should be evaluated
1390  chi1, // Input: chi1-value for which projection coefficients should be evaluated
1391  chi2, // Input: chi2-value for which projection coefficients should be evaluated
1392  submodel_hi->cvec_amp, // Input: data for spline coefficients for amplitude
1393  submodel_hi->cvec_phi, // Input: data for spline coefficients for phase
1394  submodel_hi->nk_amp, // number of SVD-modes == number of basis functions for amplitude
1395  submodel_hi->nk_phi, // number of SVD-modes == number of basis functions for phase
1396  nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1397  submodel_hi->ncx, // Number of points in eta + 2
1398  submodel_hi->ncy, // Number of points in chi1 + 2
1399  submodel_hi->ncz, // Number of points in chi2 + 2
1400  gsl_vector_const_ptr(submodel_hi->etavec, 0), // B-spline knots in eta
1401  gsl_vector_const_ptr(submodel_hi->chi1vec, 0), // B-spline knots in chi1
1402  gsl_vector_const_ptr(submodel_hi->chi2vec, 0), // B-spline knots in chi2
1403  romdata_coeff_hi->c_amp, // Output: interpolated projection coefficients for amplitude
1404  romdata_coeff_hi->c_phi // Output: interpolated projection coefficients for phase
1405  );
1406 
1407  if(retcode!=0) {
1408  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1409  XLAL_ERROR(retcode);
1410  }
1411 
1412  // Compute function values of amplitude an phase on sparse frequency points by evaluating matrix vector products
1413  // phi_pts = B_phi^T . c_phi
1414  gsl_vector* phi_f_lo = gsl_vector_alloc(submodel_lo->nk_phi);
1415  gsl_blas_dgemv(CblasTrans, 1.0, submodel_lo->Bphi, romdata_coeff_lo->c_phi,
1416  0.0, phi_f_lo);
1417 
1418  gsl_vector* phi_f_hi = gsl_vector_alloc(submodel_hi->nk_phi);
1419  gsl_blas_dgemv(CblasTrans, 1.0, submodel_hi->Bphi, romdata_coeff_hi->c_phi,
1420  0.0, phi_f_hi);
1421 
1422  const double Mfm = 0.01; // Gluing frequency: the low and high frequency ROMs overlap here; this is used both for amplitude and phase.
1423 
1424  // Glue phasing in frequency to C^1 smoothness
1425  GluePhasing(submodel_lo, submodel_hi, phi_f_lo, phi_f_hi, Mfm,
1426  acc_phi, spline_phi
1427  );
1428 
1429  // Get SEOBNRv4 ringdown frequency for 22 mode
1430  *Mf_final = SEOBNRROM_Ringdown_Mf_From_Mtot_Eta(*Mtot_sec, eta, chi1, chi2,
1431  SEOBNRv4);
1432 
1433  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1434  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1435 
1436  return(XLAL_SUCCESS);
1437 }
1438 
1439 /**
1440  * Compute the 'time' elapsed in the ROM waveform from a given starting frequency until the ringdown.
1441  *
1442  * The notion of elapsed 'time' (in seconds) is defined here as the difference of the
1443  * frequency derivative of the frequency domain phase between the ringdown frequency
1444  * and the starting frequency ('frequency' argument). This notion of time is similar to the
1445  * chirp time, but it includes both the inspiral and the merger ringdown part of SEOBNRv4.
1446  *
1447  * The allowed frequency range for the starting frequency in geometric frequency is [0.00053, 0.135].
1448  * The SEOBNRv4 ringdown frequency can be obtained by calling XLALSimInspiralGetFinalFreq().
1449  *
1450  * See XLALSimIMRSEOBNRv4ROMFrequencyOfTime() for the inverse function.
1451  */
1453  REAL8 *t, /**< Output: time (s) elapsed from starting frequency to ringdown */
1454  REAL8 frequency, /**< Starting frequency (Hz) */
1455  REAL8 m1SI, /**< Mass of companion 1 (kg) */
1456  REAL8 m2SI, /**< Mass of companion 2 (kg) */
1457  REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1458  REAL8 chi2 /**< Dimensionless aligned component spin 2 */
1459 )
1460 {
1461  /* Internally we need m1 > m2, so change around if this is not the case */
1462  if (m1SI < m2SI) {
1463  // Swap m1 and m2
1464  double m1temp = m1SI;
1465  double chi1temp = chi1;
1466  m1SI = m2SI;
1467  chi1 = chi2;
1468  m2SI = m1temp;
1469  chi2 = chi1temp;
1470  }
1471 
1472  // Set up phase spline
1473  gsl_spline *spline_phi = NULL;
1474  gsl_interp_accel *acc_phi = NULL;
1475  double Mf_final = NAN, Mtot_sec;
1476  double Mf_ROM_min = NAN, Mf_ROM_max = NAN;
1477  int ret = SEOBNRv4ROMTimeFrequencySetup(&spline_phi, &acc_phi, &Mf_final,
1478  &Mtot_sec, m1SI, m2SI, chi1, chi2,
1479  &Mf_ROM_min, &Mf_ROM_max);
1480  if(ret != 0)
1481  XLAL_ERROR(ret);
1482 
1483  if (spline_phi == NULL) {
1484  XLALPrintError( "XLAL Error - %s: `spline_phi` is not initialized\n", __func__);
1486  }
1487 
1488  if (acc_phi == NULL) {
1489  XLALPrintError( "XLAL Error - %s: `acc_phi` is not initialized\n", __func__);
1491  }
1492 
1493  if (isnan(Mf_final)) {
1494  XLALPrintError( "XLAL Error - %s: `Mf_final` is not initialized\n", __func__);
1496  }
1497 
1498  if (isnan(Mf_ROM_min)) {
1499  XLALPrintError( "XLAL Error - %s: `Mf_ROM_min` is not initialized\n", __func__);
1501  }
1502 
1503  if (isnan(Mf_ROM_max)) {
1504  XLALPrintError( "XLAL Error - %s: `Mf_ROM_max` is not initialized\n", __func__);
1506  }
1507 
1508  // Time correction is t(f_final) = 1/(2pi) dphi/df (f_final)
1509  double t_corr = gsl_spline_eval_deriv(spline_phi, Mf_final, acc_phi) / (2*LAL_PI); // t_corr / M
1510  //XLAL_PRINT_INFO("t_corr[s] = %g\n", t_corr * Mtot_sec);
1511 
1512  double Mf = frequency * Mtot_sec;
1513  if (Mf < Mf_ROM_min || Mf > Mf_ROM_max || Mf > Mf_final) {
1514  gsl_spline_free(spline_phi);
1515  gsl_interp_accel_free(acc_phi);
1516  XLAL_ERROR(XLAL_EDOM, "Frequency %g Hz (Mf=%g) is outside allowed range.\n"
1517  "Min / max / final Mf values are %g, %g, %g\n", frequency, Mf, Mf_ROM_min, Mf_ROM_max, Mf_final);
1518  }
1519 
1520  // Compute time relative to origin at merger
1521  double time_M = gsl_spline_eval_deriv(spline_phi, frequency * Mtot_sec, acc_phi) / (2*LAL_PI) - t_corr;
1522  *t = time_M * Mtot_sec;
1523 
1524  gsl_spline_free(spline_phi);
1525  gsl_interp_accel_free(acc_phi);
1526 
1527  return(XLAL_SUCCESS);
1528 }
1529 
1530 /**
1531  * Compute the starting frequency so that the given amount of 'time' elapses in the ROM waveform
1532  * from the starting frequency until the ringdown.
1533  *
1534  * The notion of elapsed 'time' (in seconds) is defined here as the difference of the
1535  * frequency derivative of the frequency domain phase between the ringdown frequency
1536  * and the starting frequency ('frequency' argument). This notion of time is similar to the
1537  * chirp time, but it includes both the inspiral and the merger ringdown part of SEOBNRv4.
1538  *
1539  * If the frequency that corresponds to the specified elapsed time is lower than the
1540  * ROM starting frequency or above half of the SEOBNRv4 ringdown frequency an error is thrown.
1541  * The SEOBNRv4 ringdown frequency can be obtained by calling XLALSimInspiralGetFinalFreq().
1542  *
1543  * See XLALSimIMRSEOBNRv4ROMTimeOfFrequency() for the inverse function.
1544  */
1546  REAL8 *frequency, /**< Output: Frequency (Hz) */
1547  REAL8 t, /**< Time (s) at frequency */
1548  REAL8 m1SI, /**< Mass of companion 1 (kg) */
1549  REAL8 m2SI, /**< Mass of companion 2 (kg) */
1550  REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1551  REAL8 chi2 /**< Dimensionless aligned component spin 2 */
1552 )
1553 {
1554  /* Internally we need m1 > m2, so change around if this is not the case */
1555  if (m1SI < m2SI) {
1556  // Swap m1 and m2
1557  double m1temp = m1SI;
1558  double chi1temp = chi1;
1559  m1SI = m2SI;
1560  chi1 = chi2;
1561  m2SI = m1temp;
1562  chi2 = chi1temp;
1563  }
1564 
1565  // Set up phase spline
1566  gsl_spline *spline_phi = NULL;
1567  gsl_interp_accel *acc_phi = NULL;
1568  double Mf_final = NAN, Mtot_sec;
1569  double Mf_ROM_min = NAN, Mf_ROM_max = NAN;
1570  int ret = SEOBNRv4ROMTimeFrequencySetup(&spline_phi, &acc_phi, &Mf_final,
1571  &Mtot_sec, m1SI, m2SI, chi1, chi2,
1572  &Mf_ROM_min, &Mf_ROM_max);
1573  if(ret != 0)
1574  XLAL_ERROR(ret);
1575 
1576  if (spline_phi == NULL) {
1577  XLALPrintError( "XLAL Error - %s: `spline_phi` is not initialized\n", __func__);
1579  }
1580 
1581  if (acc_phi == NULL) {
1582  XLALPrintError( "XLAL Error - %s: `acc_phi` is not initialized\n", __func__);
1584  }
1585 
1586  if (isnan(Mf_final)) {
1587  XLALPrintError( "XLAL Error - %s: `Mf_final` is not initialized\n", __func__);
1589  }
1590 
1591  if (isnan(Mf_ROM_min)) {
1592  XLALPrintError( "XLAL Error - %s: `Mf_ROM_min` is not initialized\n", __func__);
1594  }
1595 
1596  if (isnan(Mf_ROM_max)) {
1597  XLALPrintError( "XLAL Error - %s: `Mf_ROM_max` is not initialized\n", __func__);
1599  }
1600 
1601  // Time correction is t(f_final) = 1/(2pi) dphi/df (f_final)
1602  double t_corr = gsl_spline_eval_deriv(spline_phi, Mf_final, acc_phi) / (2*LAL_PI); // t_corr / M
1603  //XLAL_PRINT_INFO("t_corr[s] = %g\n", t_corr * Mtot_sec);
1604 
1605  // Assume for now that we only care about f(t) *before* merger so that f(t) - f_ringdown >= 0.
1606  // Assume that we only need to cover the frequency range [f_min, f_ringdown/2].
1607  int N = 20;
1608  double log_f_pts[N];
1609  double log_t_pts[N];
1610  double log_f_min = log(Mf_ROM_min * 1.000001); // raise minimum frequency slightly, so exp(log()) doesn't go below it
1611  double log_f_rng_2 = log(Mf_final/2.0);
1612  double dlog_f = (log_f_rng_2 - log_f_min) / (N-1);
1613 
1614  // Set up data in log-log space
1615  for (int i=0; i<N; i++) {
1616  log_f_pts[i] = log_f_rng_2 - i*dlog_f; // gsl likes the x-values to be monotonically increasing
1617  // Compute time relative to origin at merger
1618  double time_M = gsl_spline_eval_deriv(spline_phi, exp(log_f_pts[i]), acc_phi) / (2*LAL_PI) - t_corr;
1619  log_t_pts[i] = log(time_M * Mtot_sec);
1620  }
1621 
1622  // Check whether time is in bounds
1623  double t_rng_2 = exp(log_t_pts[0]); // time of f_ringdown/2
1624  double t_min = exp(log_t_pts[N-1]); // time of f_min
1625  if (t < t_rng_2 || t > t_min) {
1626  gsl_spline_free(spline_phi);
1627  gsl_interp_accel_free(acc_phi);
1628  XLAL_ERROR(XLAL_EDOM, "The frequency of time %g is outside allowed frequency range.\n", t);
1629  }
1630 
1631  // create new spline for data
1632  gsl_interp_accel *acc = gsl_interp_accel_alloc();
1633  gsl_spline *spline = gsl_spline_alloc(gsl_interp_cspline, N);
1634  gsl_spline_init(spline, log_t_pts, log_f_pts, N);
1635 
1636  *frequency = exp(gsl_spline_eval(spline, log(t), acc)) / Mtot_sec;
1637 
1638  gsl_spline_free(spline);
1639  gsl_interp_accel_free(acc);
1640  gsl_spline_free(spline_phi);
1641  gsl_interp_accel_free(acc_phi);
1642 
1643  return(XLAL_SUCCESS);
1644 }
1645 
1646 
1647 /** Setup SEOBNRv4ROM model using data files installed in $LAL_DATA_PATH
1648  */
1649 UNUSED static void SEOBNRv4ROM_Init_LALDATA(void)
1650 {
1651  if (SEOBNRv4ROM_IsSetup()) return;
1652 
1653  // Expect ROM datafile in a directory listed in LAL_DATA_PATH,
1654 #ifdef LAL_HDF5_ENABLED
1655 #define datafile ROMDataHDF5
1656  char *path = XLAL_FILE_RESOLVE_PATH(datafile);
1657  if (path==NULL)
1658  XLAL_ERROR_VOID(XLAL_EIO, "Unable to resolve data file %s in $LAL_DATA_PATH\n", datafile);
1659  char *dir = dirname(path);
1660  int ret = SEOBNRv4ROM_Init(dir);
1661  XLALFree(path);
1662 
1663  if(ret!=XLAL_SUCCESS)
1664  XLAL_ERROR_VOID(XLAL_FAILURE, "Unable to find SEOBNRv4ROM data files in $LAL_DATA_PATH\n");
1665 #else
1666  XLAL_ERROR_VOID(XLAL_EFAILED, "SEOBNRv4ROM requires HDF5 support which is not enabled\n");
1667 #endif
1668 }
struct tagLALH5File LALH5File
int XLALSimNRTunedTidesFDTidalAmplitudeFrequencySeries(const REAL8Sequence *amp_tidal, const REAL8Sequence *fHz, REAL8 m1_SI, REAL8 m2_SI, REAL8 lambda1, REAL8 lambda2)
Function to call amplitude tidal series only; done for convenience to use for PhenomD_NRTidalv2 and S...
static const REAL8 Mf_ROM_max
static const REAL8 Mf_ROM_min
static void nudge(REAL8 *x, REAL8 X, REAL8 epsilon)
Auxiliary functions for SEOBNRv1/v2 reduced order modeling codes in LALSimIMRSEOBNRv2ROMDoubleSpin....
static UNUSED gsl_vector * Fit_cubic(const gsl_vector *xi, const gsl_vector *yi)
static UNUSED REAL8 Interpolate_Coefficent_Tensor(gsl_vector *v, REAL8 eta, REAL8 chi1, REAL8 chi2, int ncy, int ncz, gsl_bspline_workspace *bwx, gsl_bspline_workspace *bwy, gsl_bspline_workspace *bwz)
static UNUSED double SEOBNRROM_Ringdown_Mf_From_Mtot_Eta(const double Mtot_sec, const double eta, const double chi1, const double chi2, Approximant apx)
static UNUSED int SEOBNRROMdataDS_Init(SEOBNRROMdataDS *romdata, const char dir[])
int XLALSimIMRSEOBNRv4ROMFrequencyOfTime(REAL8 *frequency, REAL8 t, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2)
Compute the starting frequency so that the given amount of 'time' elapses in the ROM waveform from th...
static UNUSED void GluePhasing(SEOBNRROMdataDS_submodel *submodel_lo, SEOBNRROMdataDS_submodel *submodel_hi, gsl_vector *phi_f_lo, gsl_vector *phi_f_hi, const double Mfm, gsl_interp_accel **acc_phi_out, gsl_spline **spline_phi_out)
static UNUSED void SEOBNRv4ROM_Init_LALDATA(void)
Setup SEOBNRv4ROM model using data files installed in $LAL_DATA_PATH.
static UNUSED int SEOBNRROMdataDS_Init_submodel(UNUSED SEOBNRROMdataDS_submodel **submodel, UNUSED const char dir[], UNUSED const char grp_name[])
int(* load_dataPtr)(const char *, gsl_vector *, gsl_vector *, gsl_matrix *, gsl_matrix *, gsl_vector *)
static UNUSED int SEOBNRv4ROM_Init(const char dir[])
Setup SEOBNRv4ROM model using data files installed in dir.
static UNUSED void SplineData_Destroy(SplineData *splinedata)
static UNUSED REAL8 Interpolate_Coefficent_Matrix(gsl_vector *v, REAL8 eta, REAL8 chi, int ncx, int ncy, gsl_bspline_workspace *bwx, gsl_bspline_workspace *bwy)
static UNUSED int SEOBNRv4ROMTimeFrequencySetup(gsl_spline **spline_phi, gsl_interp_accel **acc_phi, REAL8 *Mf_final, REAL8 *Mtot_sec, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, REAL8 *Mf_ROM_min, REAL8 *Mf_ROM_max)
static UNUSED void SplineData_Init(SplineData **splinedata, int ncx, int ncy, int ncz, const double *etavec, const double *chi1vec, const double *chi2vec)
static SEOBNRROMdataDS __lalsim_SEOBNRv4ROMDS_data
static UNUSED int SEOBNRv4ROMCore(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, double phiRef, double fRef, double distance, double inclination, double Mtot_sec, double eta, double chi1, double chi2, const REAL8Sequence *freqs, double deltaF, int nk_max, LALDict *LALparams, NRTidal_version_type NRTidal_version)
Core function for computing the ROM waveform.
static UNUSED void SEOBNRROMdataDS_coeff_Init(SEOBNRROMdataDS_coeff **romdatacoeff, int nk_amp, int nk_phi)
static UNUSED void SEOBNRROMdataDS_Cleanup_submodel(SEOBNRROMdataDS_submodel *submodel)
static UNUSED void SEOBNRROMdataDS_coeff_Cleanup(SEOBNRROMdataDS_coeff *romdatacoeff)
static UNUSED void SEOBNRROMdataDS_Cleanup(SEOBNRROMdataDS *romdata)
static UNUSED void GlueAmplitude(SEOBNRROMdataDS_submodel *submodel_lo, SEOBNRROMdataDS_submodel *submodel_hi, gsl_vector *amp_f_lo, gsl_vector *amp_f_hi, double amp_pre_lo, double amp_pre_hi, const double Mfm, gsl_interp_accel **acc_amp, gsl_spline **spline_amp)
static int TP_Spline_interpolation_3d(REAL8 eta, REAL8 chi1, REAL8 chi2, gsl_vector *cvec_amp, gsl_vector *cvec_phi, int nk_amp, int nk_phi, int nk_max, int ncx, int ncy, int ncz, const double *etavec, const double *chi1vec, const double *chi2vec, gsl_vector *c_amp, gsl_vector *c_phi)
static size_t NextPow2(const size_t n)
static UNUSED bool SEOBNRv4ROM_IsSetup(void)
Helper function to check if the SEOBNRv4ROM model has been initialised.
int XLALSimIMRSEOBNRv4ROMTimeOfFrequency(REAL8 *t, REAL8 frequency, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2)
Compute the 'time' elapsed in the ROM waveform from a given starting frequency until the ringdown.
REAL8 XLALSimInspiralWaveformParamsLookupTidalLambda2(LALDict *params)
REAL8 XLALSimInspiralWaveformParamsLookupTidalLambda1(LALDict *params)
int s
Definition: bh_qnmode.c:137
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
#define XLAL_FILE_RESOLVE_PATH(fname)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALH5FileClose(LALH5File UNUSED *file)
LALH5File * XLALH5GroupOpen(LALH5File UNUSED *file, const char UNUSED *name)
LALH5File * XLALH5FileOpen(const char UNUSED *path, const char UNUSED *mode)
#define LAL_MSUN_SI
#define LAL_PI
#define LAL_MTSUN_SI
#define LAL_MRSUN_SI
double complex COMPLEX16
double REAL8
uint32_t UINT4
int32_t INT4
void * XLALCalloc(size_t m, size_t n)
void * XLALMalloc(size_t n)
void XLALFree(void *p)
NRTidal_version_type
Definition: LALSimIMR.h:80
@ NRTidalv2_V
version NRTidalv2: https://arxiv.org/abs/1905.06011
Definition: LALSimIMR.h:82
int XLALSimIMRSEOBNRv4ROM(struct tagCOMPLEX16FrequencySeries **hptilde, struct tagCOMPLEX16FrequencySeries **hctilde, REAL8 phiRef, REAL8 deltaF, REAL8 fLow, REAL8 fHigh, REAL8 fRef, REAL8 distance, REAL8 inclination, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, INT4 nk_max, LALDict *LALparams, NRTidal_version_type NRTidal_version)
Compute waveform in LAL format for the SEOBNRv4_ROM model.
int XLALSimIMRSEOBNRv4ROMFrequencySequence(struct tagCOMPLEX16FrequencySeries **hptilde, struct tagCOMPLEX16FrequencySeries **hctilde, const REAL8Sequence *freqs, REAL8 phiRef, REAL8 fRef, REAL8 distance, REAL8 inclination, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, INT4 nk_max, LALDict *LALparams, NRTidal_version_type NRTidal_version)
Compute waveform in LAL format at specified frequencies for the SEOBNRv4_ROM model.
@ SEOBNRv4
Spin nonprecessing EOBNR model v4.
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
const LALUnit lalStrainUnit
const LALUnit lalSecondUnit
LALUnit * XLALUnitMultiply(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
#define XLAL_ERROR_VOID(...)
int int int XLALPrintInfo(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
int int XLALPrintWarning(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EDOM
XLAL_EIO
XLAL_EFAILED
XLAL_FAILURE
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
path
int N
int pthread_once(pthread_once_t *once_control, void(*init_routine)(void))
REAL8 * data
gsl_bspline_workspace * bwx
gsl_bspline_workspace * bwz
gsl_bspline_workspace * bwy
SEOBNRROMdataDS_submodel * sub2
SEOBNRROMdataDS_submodel * sub1
SEOBNRROMdataDS_submodel * sub3