Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-8a6b96f
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
LALSimIMRSEOBNRv4TSurrogate.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2017 Michael Puerrer, Ben Lackey
3 * Reduced Order Model for SEOBNRv4T spin-aligned tidal effective-one-body model
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>
57static const char SurDataHDF5[] = "SEOBNRv4T_surrogate_v2.0.0.hdf5";
58#endif
59
60#include <lal/LALSimInspiral.h>
61#include <lal/LALSimIMR.h>
62
63#include "LALSimIMREOBNRv2.h"
68
69#include <lal/LALConfig.h>
70#ifdef LAL_PTHREAD_LOCK
71#include <pthread.h>
72#endif
73
74
75
76#ifdef LAL_PTHREAD_LOCK
77static pthread_once_t Surrogate_is_initialized = PTHREAD_ONCE_INIT;
78#endif
79
80/*************** type definitions ******************/
81
83{
84 gsl_matrix *hyp_amp; // GP hyperparameters log amplitude
85 gsl_matrix *hyp_phi; // GP hyperparameters for dephasing
86 gsl_matrix *kinv_dot_y_amp; // kinv_dot_y for log amplitude
87 gsl_matrix *kinv_dot_y_phi; // kinv_dot_y for dephasing
88 gsl_matrix *x_train; // Training points
89
90 // location of surrogate spline nodes
91 gsl_vector *mf_amp; // location of spline nodes for log amplitude
92 gsl_vector *mf_phi; // location of spline nodes for dephasing
93
94 // location of spline nodes for TaylorF2 at low frequency
95 gsl_vector *TF2_mf_amp_cubic; // cubic spline nodes for TaylorF2 amplitude
96 gsl_vector *TF2_mf_phi_cubic; // cubic spline nodes for TaylorF2 phasing
97 gsl_vector *TF2_mf_amp_linear; // linear spline nodes for TaylorF2 amplitude
98 gsl_vector *TF2_mf_phi_linear; // linear spline nodes for TaylorF2 phasing
99
100 // 5D parameter space bounds of surrogate
101 gsl_vector *q_bounds; // [q_min, q_max]
102 gsl_vector *chi1_bounds; // [chi1_min, chi1_max]
103 gsl_vector *chi2_bounds; // [chi2_min, chi2_max]
104 gsl_vector *lambda1_bounds; // [lambda1_min, lambda1_max]
105 gsl_vector *lambda2_bounds; // [lambda2_min, lambda2_max]
106};
107typedef struct tagSurrogatedata_submodel Surrogatedata_submodel;
108
110{
112 Surrogatedata_submodel* sub1;
113};
114typedef struct tagSurrogatedata Surrogatedata;
115
116static Surrogatedata __lalsim_SurrogateDS_data;
117
118typedef int (*load_dataPtr)(const char*, gsl_vector *, gsl_vector *, gsl_matrix *, gsl_matrix *, gsl_vector *);
119
120/**************** Internal functions **********************/
121
122UNUSED static void save_gsl_frequency_series(const char filename[], gsl_vector *x, gsl_vector *y);
123
124static gsl_vector *gsl_vector_prepend_value(gsl_vector *v, double value);
125
126double kernel(
127 gsl_vector *x1, // array with shape ndim
128 gsl_vector *x2, // array with shape ndim
129 gsl_vector *hyperparams, // Hyperparameters
130 gsl_vector *work // work array
131);
132
133double gp_predict(
134 gsl_vector *xst, // Point x_* where you want to evaluate the function.
135 gsl_vector *hyperparams, // Hyperparameters for the GPR kernel.
136 gsl_matrix *x_train, // Training set points.
137 gsl_vector *Kinv_dot_y, // The interpolating weights at each training set point.
138 gsl_vector *work // work array for kernel
139);
140
141
142UNUSED static void Surrogate_Init_LALDATA(void);
143UNUSED static int Surrogate_Init(const char dir[]);
144UNUSED static bool Surrogate_IsSetup(void);
145
146UNUSED static int Surrogatedata_Init(Surrogatedata *romdata, const char dir[]);
147UNUSED static void Surrogatedata_Cleanup(Surrogatedata *romdata);
148
149static double xi_of_lambda(double lambda);
150
151static int GPR_evaluation_5D(
152 double q, // Input: q-value (q >= 1)
153 double chi1, // Input: chi1-value
154 double chi2, // Input: chi2-value
155 double lambda1, // Input: lambda1-value
156 double lambda2, // Input: lambda2-value
157 gsl_matrix *hyp_amp, // Input: GPR hyperparameters for log amplitude
158 gsl_matrix *hyp_phi, // Input: GPR hyperparameters for dephasing
159 gsl_matrix *kinv_dot_y_amp, // Input: kinv_dot_y for log amplitude
160 gsl_matrix *kinv_dot_y_phi, // Input: kinv_dot_y for dephasing
161 gsl_matrix *x_train, // Input: GPR training points
162 gsl_vector *amp_at_EI_nodes, // Output: log amplitude at EI nodes (preallocated)
163 gsl_vector *phi_at_EI_nodes, // Output: dephasing at EI nodes (preallocated)
164 gsl_vector *work // Input: work array
165);
166
168 UNUSED Surrogatedata_submodel **submodel,
169 UNUSED const char dir[],
170 UNUSED const char grp_name[]
171);
172
173UNUSED static void Surrogatedata_Cleanup_submodel(Surrogatedata_submodel *submodel);
174
175UNUSED static int CheckParameterSpaceBounds(
176 Surrogatedata_submodel *sur,
177 double q, // mass-ratio q >= 1
178 double chi1,
179 double chi2,
180 double lambda1,
181 double lambda2
182);
183
184/**
185 * Core function for computing the ROM waveform.
186 * Interpolate projection coefficient data and evaluate coefficients at desired (q, chi).
187 * Construct 1D splines for amplitude and phase.
188 * Compute strain waveform from amplitude and phase.
189*/
190UNUSED static int SurrogateCore(
191 COMPLEX16FrequencySeries **hptilde,
192 COMPLEX16FrequencySeries **hctilde,
193 double phiRef,
194 double fRef,
195 double distance,
196 double inclination,
197 double Mtot_sec,
198 double eta,
199 double chi1,
200 double chi2,
201 double lambda1,
202 double lambda2,
203 const REAL8Sequence *freqs, /* Frequency points at which to evaluate the waveform (Hz) */
204 double deltaF,
205 /* If deltaF > 0, the frequency points given in freqs are uniformly spaced with
206 * spacing deltaF. Otherwise, the frequency points are spaced non-uniformly.
207 * Then we will use deltaF = 0 to create the frequency series we return. */
209);
210
211static size_t NextPow2(const size_t n);
212
213static int TaylorF2Phasing(
214 double Mtot, // Total mass in solar masses
215 double q, // Mass-ration m1/m2 >= 1
216 double chi1, // Dimensionless aligned spin of body 1
217 double chi2, // Dimensionless aligned spin of body 2
218 double lambda1, // Tidal deformability of body 1
219 double lambda2, // Tidal deformability of body 2
220 gsl_vector *Mfs, // Input geometric frequencies
221 gsl_vector **PNphase // Output: TaylorF2 phase at frequencies Mfs
222);
223
224static int TaylorF2Amplitude1PN(
225 double eta, // Symmetric mass-ratio
226 gsl_vector *Mfs, // Input geometric frequencies
227 gsl_vector **PNamp // Output: TaylorF2 amplitude at frequencies Mfs
228);
229
230/********************* Definitions begin here ********************/
231
232UNUSED static void save_gsl_frequency_series(const char filename[], gsl_vector *x, gsl_vector *y) {
233 FILE *fp = fopen(filename, "w");
234 fprintf(stderr, "save_gsl_frequency_series: %s: %zu %zu\n", filename, x->size, y->size);
235 XLAL_CHECK_ABORT(x->size == y->size);
236 for (size_t i=0; i<x->size; i++) {
237 fprintf(fp, "%g\t%g\n", gsl_vector_get(x, i), gsl_vector_get(y, i));
238 }
239 fclose(fp);
240}
241
242static gsl_vector *gsl_vector_prepend_value(gsl_vector *v, double value) {
243// Helper function to prepend a value to a gsl_vector
244// Returns the augmented gsl_vector
245// Deallocates the input gsl_vector
246 int n = v->size;
247 gsl_vector *vout = gsl_vector_alloc(n+1);
248
249 gsl_vector_set(vout, 0, value);
250 for (int i=1; i<=n; i++)
251 gsl_vector_set(vout, i, gsl_vector_get(v, i-1));
252 gsl_vector_free(v);
253
254 return vout;
255}
256
257double kernel(
258 gsl_vector *x1, // parameter space point 1
259 gsl_vector *x2, // parameter space point 2
260 gsl_vector *hyperparams, // GPR Hyperparameters
261 gsl_vector *work // work array
262)
263{
264 // Matern covariance function for n-dimensional data.
265 //
266 // Parameters
267 // ----------
268 // x1 : array with shape ndim
269 // x2 : array with shape ndim
270 // hyperparams : array with shape ndim+2 [sigma_f, ls0, ls1, ..., sigma_n]
271 // sigma_f : Approximately the range (ymax-ymin) of values that the data takes.
272 // sigma_f^2 called the signal variance.
273 // sigma_n : Noise term. The uncertainty in the y values of the data.
274 // lsi : Length scales for the variation in dimension i.
275 //
276 // Returns
277 // -------
278 // covariance : double
279
280 double sigma_f = gsl_vector_get(hyperparams, 0);
281 double sigma_n = gsl_vector_get(hyperparams, hyperparams->size-1);
282 gsl_vector ls = gsl_vector_subvector(hyperparams, 1, hyperparams->size-2).vector;
283
284 XLAL_CHECK((x1->size == x2->size) && (x1->size == ls.size), XLAL_EDIMS,
285 "kernel(): dimensions of vectors x1, x2 and ls: %zu, %zu, %zu have to be consistent.\n",
286 x1->size, x2->size, ls.size);
287
288 // Noise nugget for diagonal elements
289 double nugget = 0.0;
290 if (gsl_vector_equal(x1, x2))
291 nugget = sigma_n*sigma_n;
292
293 gsl_vector_memcpy(work, x1);
294 gsl_vector_sub(work, x2);
295 gsl_vector_div(work, &ls); // (x1 - x2) / ls
296 double r = gsl_blas_dnrm2(work);
297
298 // nu = 5/2 Matern covariance
299 double matern = (1.0 + sqrt(5.0)*r + 5.0*r*r/3.0) * exp(-sqrt(5.0)*r);
300
301 // Full covariance
302 // Include the nugget to agree with scikit-learn when the points x1, x2 are exactly the same.
303 return sigma_f*sigma_f * matern + nugget;
304}
305
307 gsl_vector *xst, // Point x_* where you want to evaluate the function.
308 gsl_vector *hyperparams, // Hyperparameters for the GPR kernel.
309 gsl_matrix *x_train, // Training set points.
310 gsl_vector *Kinv_dot_y, // The interpolating weights at each training set point.
311 gsl_vector *work
312)
313{
314 // Interpolate the function at the point xst using Gaussian process regression.
315 //
316 // Parameters
317 // ----------
318 // xst : array of shape ndim.
319 // Point x_* where you want to evaluate the function.
320 // hyperparams : array with shape ndim+2 [sigma_f, ls0, ls1, ..., sigma_n].
321 // Hyperparameters for the GPR kernel.
322 // x_train : array of shape (n_train, ndim).
323 // Training set points.
324 // Kinv_dot_y : array of shape n_train.
325 // The interpolating weights at each training set point.
326 //
327 // Returns
328 // -------
329 // yst : double
330 // Interpolated value at the point xst.
331
332 // Evaluate vector K_*
333 int n = x_train->size1;
334 gsl_vector *Kst = gsl_vector_alloc(n);
335 for (int i=0; i < n; i++) {
336 gsl_vector x = gsl_matrix_const_row(x_train, i).vector;
337 double ker = kernel(xst, &x, hyperparams, work);
338 gsl_vector_set(Kst, i, ker);
339 }
340
341 // Evaluate y_*
342 double res = 0;
343 gsl_blas_ddot(Kst, Kinv_dot_y, &res);
344 gsl_vector_free(Kst);
345 return res;
346}
347
348/** Setup Surrogate model using data files installed in dir */
349static int Surrogate_Init(const char dir[]) {
350 if(__lalsim_SurrogateDS_data.setup) {
351 XLALPrintError("Error: Surrogate data was already set up!");
353 }
355
356 if(__lalsim_SurrogateDS_data.setup) {
357 return(XLAL_SUCCESS);
358 }
359 else {
360 return(XLAL_EFAILED);
361 }
362}
363
364/** Helper function to check if the Surrogate model has been initialised */
365static bool Surrogate_IsSetup(void) {
367 return true;
368 else
369 return false;
370}
371
372/** Coordinate transformation */
373static double xi_of_lambda(double lambda) {
374 const double a = 100.0;
375 return log10(lambda / a + 1.0);
376}
377
378// Compute amplitude and phase at empirical interpolant nodes from GPRs.
379// This entails interpolation over the 5D parameter space (q, chi1, chi2, lambda1, lambda2).
381 double q, // Input: q-value (q >= 1)
382 double chi1, // Input: chi1-value
383 double chi2, // Input: chi2-value
384 double lambda1, // Input: lambda1-value
385 double lambda2, // Input: lambda2-value
386 gsl_matrix *hyp_amp, // Input: GPR hyperparameters for log amplitude
387 gsl_matrix *hyp_phi, // Input: GPR hyperparameters for dephasing
388 gsl_matrix *kinv_dot_y_amp, // Input: kinv_dot_y for log amplitude
389 gsl_matrix *kinv_dot_y_phi, // Input: kinv_dot_y for dephasing
390 gsl_matrix *x_train, // Input: GPR training points
391 gsl_vector *amp_at_nodes, // Output: log amplitude at frequency nodes (preallocated)
392 gsl_vector *phi_at_nodes, // Output: dephasing at frequency nodes (preallocated)
393 gsl_vector *work // Input: workspace
394)
395{
396 // Assemble evaluation point
397 gsl_vector *xst = gsl_vector_alloc(5);
398 double q_inv = 1.0/q;
399 gsl_vector_set(xst, 0, q_inv);
400 gsl_vector_set(xst, 1, chi1);
401 gsl_vector_set(xst, 2, chi2);
402 gsl_vector_set(xst, 3, xi_of_lambda(lambda1));
403 gsl_vector_set(xst, 4, xi_of_lambda(lambda2));
404
405 // Evaluate GPR for amplitude spline nodes
406 for (size_t i=0; i<amp_at_nodes->size; i++) {
407 gsl_vector hyp_amp_i = gsl_matrix_const_row(hyp_amp, i).vector;
408 gsl_vector kinv_dot_y_amp_i = gsl_matrix_const_row(kinv_dot_y_amp, i).vector;
409 double pred = gp_predict(xst, &hyp_amp_i, x_train, &kinv_dot_y_amp_i, work);
410 gsl_vector_set(amp_at_nodes, i, pred);
411 }
412
413 // Evaluate GPR for phase spline nodes
414 for (size_t i=0; i<phi_at_nodes->size; i++) {
415 gsl_vector hyp_phi_i = gsl_matrix_const_row(hyp_phi, i).vector;
416 gsl_vector kinv_dot_y_phi_i = gsl_matrix_const_row(kinv_dot_y_phi, i).vector;
417 double pred = gp_predict(xst, &hyp_phi_i, x_train, &kinv_dot_y_phi_i, work);
418 gsl_vector_set(phi_at_nodes, i, pred);
419 }
420
421 gsl_vector_free(xst);
422
423 return XLAL_SUCCESS;
424}
425
426/* Set up a new submodel, using data contained in dir */
428 Surrogatedata_submodel **submodel,
429 UNUSED const char dir[],
430 UNUSED const char grp_name[]
431) {
432 int ret = XLAL_FAILURE;
433
434 if(!submodel) exit(1);
435 /* Create storage for submodel structures */
436 if (!*submodel)
437 *submodel = XLALCalloc(1,sizeof(Surrogatedata_submodel));
438 else
440
441#ifdef LAL_HDF5_ENABLED
442 size_t size = strlen(dir) + strlen(SurDataHDF5) + 2;
443 char *path = XLALMalloc(size);
444 snprintf(path, size, "%s/%s", dir, SurDataHDF5);
445
447 //LALH5File *file = XLALH5FileOpen("/Users/mpuer/Documents/gpsurrogate/src/TEOB-LAL-implementation/TEOBv4_surrogate.hdf5", "r");
448 LALH5File *root = XLALH5GroupOpen(file, "/");
449
450 //////////////////////////////////////////////////////////////////////////////
451 // load everything we need
452 // GP hyperparameters
453 ReadHDF5RealMatrixDataset(root, "hyp_amp", & (*submodel)->hyp_amp);
454 ReadHDF5RealMatrixDataset(root, "hyp_phi", & (*submodel)->hyp_phi);
455
456 // K^{-1} . y
457 ReadHDF5RealMatrixDataset(root, "kinv_dot_y_amp", & (*submodel)->kinv_dot_y_amp);
458 ReadHDF5RealMatrixDataset(root, "kinv_dot_y_phi", & (*submodel)->kinv_dot_y_phi);
459
460 // Training points
461 ReadHDF5RealMatrixDataset(root, "x_train", & (*submodel)->x_train);
462
463 // Frequency grids for surrogate corrections
464 ReadHDF5RealVectorDataset(root, "spline_nodes_amp", & (*submodel)->mf_amp);
465 ReadHDF5RealVectorDataset(root, "spline_nodes_phase", & (*submodel)->mf_phi);
466
467 // Frequency grids for TaylorF2
468 ReadHDF5RealVectorDataset(root, "TF2_Mf_amp_cubic", & (*submodel)->TF2_mf_amp_cubic);
469 ReadHDF5RealVectorDataset(root, "TF2_Mf_phi_cubic", & (*submodel)->TF2_mf_phi_cubic);
470 ReadHDF5RealVectorDataset(root, "TF2_Mf_amp_linear", & (*submodel)->TF2_mf_amp_linear);
471 ReadHDF5RealVectorDataset(root, "TF2_Mf_phi_linear", & (*submodel)->TF2_mf_phi_linear);
472
473 // Physical domain covered by surrogate
474 ReadHDF5RealVectorDataset(root, "q_bounds", & (*submodel)->q_bounds);
475 ReadHDF5RealVectorDataset(root, "chi1_bounds", & (*submodel)->chi1_bounds);
476 ReadHDF5RealVectorDataset(root, "chi2_bounds", & (*submodel)->chi2_bounds);
477 ReadHDF5RealVectorDataset(root, "lambda1_bounds", & (*submodel)->lambda1_bounds);
478 ReadHDF5RealVectorDataset(root, "lambda2_bounds", & (*submodel)->lambda2_bounds);
479
480 // Prepend the point [mf_amp[0], 0] to the phase nodes
481 double mf_min = gsl_vector_get( (*submodel)->mf_amp, 0); // Follow definition of mf_a in GPSplineSurrogate constructor
482 gsl_vector *phi_nodes = gsl_vector_prepend_value((*submodel)->mf_phi, mf_min);
483 (*submodel)->mf_phi = phi_nodes;
484
485 XLALFree(path);
487 ret = XLAL_SUCCESS;
488#else
489 XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
490#endif
491
492 return ret;
493}
494
495/* Deallocate contents of the given Surrogatedata_submodel structure */
496static void Surrogatedata_Cleanup_submodel(Surrogatedata_submodel *submodel) {
497 if(submodel->hyp_amp) gsl_matrix_free(submodel->hyp_amp);
498 if(submodel->hyp_phi) gsl_matrix_free(submodel->hyp_phi);
499 if(submodel->kinv_dot_y_amp) gsl_matrix_free(submodel->kinv_dot_y_amp);
500 if(submodel->kinv_dot_y_phi) gsl_matrix_free(submodel->kinv_dot_y_phi);
501 if(submodel->x_train) gsl_matrix_free(submodel->x_train);
502 if(submodel->mf_amp) gsl_vector_free(submodel->mf_amp);
503 if(submodel->mf_phi) gsl_vector_free(submodel->mf_phi);
504 if(submodel->TF2_mf_amp_cubic) gsl_vector_free(submodel->TF2_mf_amp_cubic);
505 if(submodel->TF2_mf_phi_cubic) gsl_vector_free(submodel->TF2_mf_phi_cubic);
506 if(submodel->TF2_mf_amp_linear) gsl_vector_free(submodel->TF2_mf_amp_linear);
507 if(submodel->TF2_mf_phi_linear) gsl_vector_free(submodel->TF2_mf_phi_linear);
508
509 if(submodel->q_bounds) gsl_vector_free(submodel->q_bounds);
510 if(submodel->chi1_bounds) gsl_vector_free(submodel->chi1_bounds);
511 if(submodel->chi2_bounds) gsl_vector_free(submodel->chi2_bounds);
512 if(submodel->lambda1_bounds) gsl_vector_free(submodel->lambda1_bounds);
513 if(submodel->lambda2_bounds) gsl_vector_free(submodel->lambda2_bounds);
514}
515
516/* Set up a new ROM model, using data contained in dir */
518 UNUSED Surrogatedata *romdata,
519 UNUSED const char dir[])
520{
521 int ret = XLAL_FAILURE;
522
523 /* Create storage for structures */
524 if(romdata->setup) {
525 XLALPrintError("WARNING: You tried to setup the Surrogate model that was already initialised. Ignoring\n");
526 return (XLAL_FAILURE);
527 }
528
529#ifdef LAL_HDF5_ENABLED
530 // First, check we got the correct version number
531 size_t size = strlen(dir) + strlen(SurDataHDF5) + 2;
532 char *path = XLALMalloc(size);
533 snprintf(path, size, "%s/%s", dir, SurDataHDF5);
535
536 XLALPrintInfo("Surrogate metadata\n============\n");
537 PrintInfoStringAttribute(file, "Email");
538 PrintInfoStringAttribute(file, "Description");
539 ret = ROM_check_canonical_file_basename(file,SurDataHDF5,"CANONICAL_FILE_BASENAME");
540
541 XLALFree(path);
543
544 ret = Surrogatedata_Init_submodel(&(romdata)->sub1, dir, "sub1");
545 if (ret==XLAL_SUCCESS) XLALPrintInfo("%s : submodel 1 loaded successfully.\n", __func__);
546
547 if(XLAL_SUCCESS==ret)
548 romdata->setup=1;
549 else
550 Surrogatedata_Cleanup(romdata);
551#else
552 XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
553#endif
554
555 return (ret);
556}
557
558/* Deallocate contents of the given Surrogatedata structure */
559static void Surrogatedata_Cleanup(Surrogatedata *romdata) {
561 XLALFree((romdata)->sub1);
562 (romdata)->sub1 = NULL;
563 romdata->setup=0;
564}
565
566/* Return the closest higher power of 2 */
567// Note: NextPow(2^k) = 2^k for integer values k.
568static size_t NextPow2(const size_t n) {
569 return 1 << (size_t) ceil(log2(n));
570}
571
572
574 double Mtot, // Total mass in solar masses
575 double q, // Mass-ration m1/m2 >= 1
576 double chi1, // Dimensionless aligned spin of body 1
577 double chi2, // Dimensionless aligned spin of body 2
578 double lambda1, // Tidal deformability of body 1
579 double lambda2, // Tidal deformability of body 2
580 gsl_vector *Mfs, // Input geometric frequencies
581 gsl_vector **PNphase // Output: TaylorF2 phase at frequencies Mfs
582) {
583 XLAL_CHECK(PNphase != NULL, XLAL_EFAULT);
584 XLAL_CHECK(*PNphase == NULL, XLAL_EFAULT);
585 *PNphase = gsl_vector_alloc(Mfs->size);
586
587 PNPhasingSeries *pn = NULL;
588 LALDict *extraParams = XLALCreateDict();
593
594 // Use the universal relation fit to determine the quadrupole-monopole terms.
595 double dquadmon1 = XLALSimUniversalRelationQuadMonVSlambda2Tidal(lambda1) - 1.0;
596 double dquadmon2 = XLALSimUniversalRelationQuadMonVSlambda2Tidal(lambda2) - 1.0;
597
598 if ((dquadmon1 > 0) || (dquadmon2 > 0)) {
599 XLALPrintInfo("Using quadrupole-monopole terms from fit: %g, %g\n", dquadmon1, dquadmon2);
600 XLALSimInspiralWaveformParamsInsertdQuadMon1(extraParams, dquadmon1);
601 XLALSimInspiralWaveformParamsInsertdQuadMon2(extraParams, dquadmon2);
602 }
603
604 double m1OverM = q / (1.0+q);
605 double m2OverM = 1.0 / (1.0+q);
606 double m1 = Mtot * m1OverM * LAL_MSUN_SI;
607 double m2 = Mtot * m2OverM * LAL_MSUN_SI;
608 // This function is a thin wrapper around XLALSimInspiralPNPhasing_F2
609 XLALSimInspiralTaylorF2AlignedPhasing(&pn, m1, m2, chi1, chi2, extraParams);
610
611 // Compute and subtract pn_ss3 term (See LALSimInspiralPNCoefficients.c: XLALSimInspiralPNPhasing_F2()).
612 // Rationale: SEOBNRv4T does not contain this ss3 term, therefore
613 // we remove it from the TF2 phasing that is used as a base waveform for the surrogate.
614 double m1M = m1OverM;
615 double m2M = m2OverM;
616 double chi1L = chi1;
617 double chi2L = chi2;
618 double chi1sq = chi1*chi1;
619 double chi2sq = chi2*chi2;
620 double qm_def1 = 1 + dquadmon1;
621 double qm_def2 = 1 + dquadmon2;
622 double eta = q / ((1.0 + q)*(1.0 + q));
623 double pn_ss3 = XLALSimInspiralTaylorF2Phasing_6PNS1S2OCoeff(eta)*chi1L*chi2L
626 pn->v[6] -= pn_ss3 * pn->v[0];
627
628 for (size_t i=0; i < Mfs->size; i++) {
629 const double Mf = gsl_vector_get(Mfs, i);
630 const double v = cbrt(LAL_PI * Mf);
631 const double logv = log(v);
632 const double v2 = v * v;
633 const double v3 = v * v2;
634 const double v4 = v * v3;
635 const double v5 = v * v4;
636 const double v6 = v * v5;
637 const double v7 = v * v6;
638 const double v8 = v * v7;
639 const double v9 = v * v8;
640 const double v10 = v * v9;
641 const double v12 = v2 * v10;
642 double phasing = 0.0;
643
644 phasing += pn->v[7] * v7;
645 phasing += (pn->v[6] + pn->vlogv[6] * logv) * v6;
646 phasing += (pn->v[5] + pn->vlogv[5] * logv) * v5;
647 phasing += pn->v[4] * v4;
648 phasing += pn->v[3] * v3;
649 phasing += pn->v[2] * v2;
650 phasing += pn->v[1] * v;
651 phasing += pn->v[0];
652
653 /* Tidal terms in phasing */
654 phasing += pn->v[12] * v12;
655 phasing += pn->v[10] * v10;
656
657 phasing /= v5;
658
659 gsl_vector_set(*PNphase, i, -phasing);
660 }
661
662 XLALDestroyDict(extraParams);
663 XLALFree(pn);
664
665 return XLAL_SUCCESS;
666}
667
668// 1PN point-particle amplitude.
669// Expression from Eq. (6.10) of arXiv:0810.5336.
671 double eta, // Symmetric mass-ratio
672 gsl_vector *Mfs, // Input geometric frequencies
673 gsl_vector **PNamp // Output: TaylorF2 amplitude at frequencies Mfs
674) {
675 XLAL_CHECK(PNamp != NULL, XLAL_EFAULT);
676 XLAL_CHECK(*PNamp == NULL, XLAL_EFAULT);
677 *PNamp = gsl_vector_alloc(Mfs->size);
678
679 for (size_t i=0; i < Mfs->size; i++) {
680 const double Mf = gsl_vector_get(Mfs, i);
681 const double v = cbrt(LAL_PI * Mf);
682 const double x = v*v;
683
684 double a00 = sqrt( (5.0*LAL_PI/24.0)*eta );
685 double a10 = -323.0/224.0 + 451.0*eta/168.0;
686 double amp = -a00 * pow(x, -7.0/4.0) * (1.0 + a10*x);
687 gsl_vector_set(*PNamp, i, amp);
688 }
689
690 return XLAL_SUCCESS;
691}
692
694 Surrogatedata_submodel *sur,
695 double q, // mass-ratio q >= 1
696 double chi1,
697 double chi2,
698 double lambda1,
699 double lambda2
700) {
701 // convert to q >= 1
702 double q_max = 1.0 / gsl_vector_get(sur->q_bounds, 0);
703 double q_min = 1.0 / gsl_vector_get(sur->q_bounds, 1);
704
705 double chi1_min = gsl_vector_get(sur->chi1_bounds, 0);
706 double chi1_max = gsl_vector_get(sur->chi1_bounds, 1);
707 double chi2_min = gsl_vector_get(sur->chi2_bounds, 0);
708 double chi2_max = gsl_vector_get(sur->chi2_bounds, 1);
709
710 double lambda1_min = gsl_vector_get(sur->lambda1_bounds, 0);
711 double lambda1_max = gsl_vector_get(sur->lambda1_bounds, 1);
712 double lambda2_min = gsl_vector_get(sur->lambda2_bounds, 0);
713 double lambda2_max = gsl_vector_get(sur->lambda2_bounds, 1);
714
715 if (q < q_min || q > q_max) {
716 XLALPrintError("XLAL Error - %s: mass-ratio q (%f) out of bounds: [%f, %f]!\n", __func__, q, q_min, q_max);
718 }
719
720 if (chi1 < chi1_min || chi1 > chi1_max) {
721 XLALPrintError("XLAL Error - %s: aligned-spin chi1 (%f) out of bounds: [%f, %f]!\n", __func__, chi1, chi1_min, chi1_max);
723 }
724
725 if (chi2 < chi2_min || chi2 > chi2_max) {
726 XLALPrintError("XLAL Error - %s: aligned-spin chi2 (%f) out of bounds: [%f, %f]!\n", __func__, chi2, chi2_min, chi2_max);
728 }
729
730 if (lambda1 < lambda1_min || lambda1 > lambda1_max) {
731 XLALPrintError("XLAL Error - %s: tidal deformability lambda1 (%f) out of bounds: [%f, %f]!\n", __func__, lambda1, lambda1_min, lambda1_max);
733 }
734
735 if (lambda2 < lambda2_min || lambda2 > lambda2_max) {
736 XLALPrintError("XLAL Error - %s: tidal deformability lambda2 (%f) out of bounds: [%f, %f]!\n", __func__, lambda2, lambda2_min, lambda2_max);
738 }
739
740 return XLAL_SUCCESS;
741}
742
743/**
744 * Core function for computing the ROM waveform.
745 * Interpolate projection coefficient data and evaluate coefficients at desired (q, chi1, chi2).
746 * Construct 1D splines for amplitude and phase.
747 * Compute strain waveform from amplitude and phase.
748*/
749static int SurrogateCore(
750 COMPLEX16FrequencySeries **hptilde,
751 COMPLEX16FrequencySeries **hctilde,
752 double phiRef, // orbital reference phase
753 double fRef,
754 double distance,
755 double inclination,
756 double Mtot_sec,
757 double eta,
758 double chi1,
759 double chi2,
760 double lambda1,
761 double lambda2,
762 const REAL8Sequence *freqs_in, /* Frequency points at which to evaluate the waveform (Hz) */
763 double deltaF,
764 /* If deltaF > 0, the frequency points given in freqs are uniformly spaced with
765 * spacing deltaF. Otherwise, the frequency points are spaced non-uniformly.
766 * Then we will use deltaF = 0 to create the frequency series we return. */
768 )
769{
770 /* Check output arrays */
771 if(!hptilde || !hctilde)
773
774 Surrogatedata *romdata=&__lalsim_SurrogateDS_data;
775 if (!Surrogate_IsSetup()) {
777 "Error setting up Surrogate data - check your $LAL_DATA_PATH\n");
778 }
779
780 if(*hptilde || *hctilde) {
781 XLALPrintError("(*hptilde) and (*hctilde) are supposed to be NULL, but got %p and %p",
782 (*hptilde), (*hctilde));
784 }
785 int retcode=0;
786
787 double Mtot = Mtot_sec / LAL_MTSUN_SI;
788 double q = (1.0 + sqrt(1.0 - 4.0*eta) - 2.0*eta) / (2.0*eta);
789
790 Surrogatedata_submodel *sur;
791 sur = romdata->sub1;
792
793 retcode = CheckParameterSpaceBounds(sur, q, chi1, chi2, lambda1, lambda2);
794 if(retcode != 0) XLAL_ERROR(retcode);
795
796 /* Find frequency bounds */
797 if (!freqs_in) XLAL_ERROR(XLAL_EFAULT);
798 double fLow = freqs_in->data[0];
799 double fHigh = freqs_in->data[freqs_in->length - 1];
800
801 if(fRef==0.0)
802 fRef=fLow;
803
804 int N_amp = sur->mf_amp->size;
805 int N_phi = sur->mf_phi->size;
806
807 // Point to linear or cubic spline nodes and set order for gsl calls lateron
808 gsl_vector *TF2_mf_amp = NULL;
809 gsl_vector *TF2_mf_phi = NULL;
810 const gsl_interp_type *spline_type = NULL;
811 switch (spline_order) {
813 TF2_mf_amp = sur->TF2_mf_amp_cubic;
814 TF2_mf_phi = sur->TF2_mf_phi_cubic;
815 spline_type = gsl_interp_cspline;
816 break;
818 TF2_mf_amp = sur->TF2_mf_amp_linear;
819 TF2_mf_phi = sur->TF2_mf_phi_linear;
820 spline_type = gsl_interp_linear;
821 break;
822 default:
823 XLAL_ERROR( XLAL_EINVAL, "Unknown spline order!\n. Must be LINEAR or CUBIC." );
824 }
825 int N_amp_TF2 = TF2_mf_amp->size;
826 int N_phi_TF2 = TF2_mf_phi->size;
827
828 // Allowed geometric frequency ranges for
829 // surrogate and supported TaylorF2 low frequency extension
830 double Mf_TF2_min = fmax(gsl_vector_get(TF2_mf_amp, 0),
831 gsl_vector_get(TF2_mf_phi, 0));
832 double Mf_TF2_max = fmin(gsl_vector_get(TF2_mf_amp, N_amp_TF2-1),
833 gsl_vector_get(TF2_mf_phi, N_phi_TF2-1));
834 double Mf_sur_min = fmax(gsl_vector_get(sur->mf_amp, 0),
835 gsl_vector_get(sur->mf_phi, 0));
836 double Mf_sur_max = fmin(gsl_vector_get(sur->mf_amp, N_amp-1),
837 gsl_vector_get(sur->mf_phi, N_phi-1));
838
839 // sanity checks: sparse grids for TaylorF2 need to contain the
840 // frequency range where the surrogate corrections are applied
841 XLAL_CHECK(Mf_TF2_min < Mf_sur_min, XLAL_EFAULT);
842 XLAL_CHECK(Mf_TF2_max >= Mf_sur_max, XLAL_EFAULT);
843
844 double fLow_geom = fLow * Mtot_sec;
845 double fHigh_geom = fHigh * Mtot_sec;
846 double fRef_geom = fRef * Mtot_sec;
847 double deltaF_geom = deltaF * Mtot_sec;
848
849 // Enforce allowed geometric frequency range
850 if (fLow_geom < Mf_TF2_min)
851 XLAL_ERROR(XLAL_EDOM, "Starting frequency Mflow=%g is smaller than lowest frequency in surrogate Mf=%g.\n", fLow_geom, Mf_TF2_min);
852 if (fHigh_geom == 0 || fHigh_geom > Mf_sur_max)
853 fHigh_geom = Mf_sur_max;
854 else if (fHigh_geom < Mf_sur_min)
855 XLAL_ERROR(XLAL_EDOM, "End frequency %g is smaller than surrogate starting frequency %g!\n", fHigh_geom, Mf_TF2_min);
856 if (fHigh_geom <= fLow_geom)
857 XLAL_ERROR(XLAL_EDOM, "End frequency %g is smaller than (or equal to) starting frequency %g!\n", fHigh_geom, fLow_geom);
858 if (fRef_geom > Mf_sur_max) {
859 XLALPrintWarning("Reference frequency Mf_ref=%g is greater than maximal frequency in surrogate Mf=%g. Starting at maximal frequency in ROM.\n", fRef_geom, Mf_sur_max);
860 fRef_geom = Mf_sur_max; // If fref > fhigh we reset fref to default value of cutoff frequency.
861 }
862 if (fRef_geom < Mf_TF2_min) {
863 XLALPrintWarning("Reference frequency Mf_ref=%g is smaller than lowest frequency in surrogate Mf=%g. Starting at lowest frequency in ROM.\n", fLow_geom, Mf_TF2_min);
864 fRef_geom = Mf_TF2_min;
865 }
866
867 // Evaluate GPR for log amplitude and dephasing
868 gsl_vector *sur_amp_at_nodes = gsl_vector_alloc(N_amp);
869 gsl_vector *sur_phi_at_nodes_tmp = gsl_vector_alloc(N_phi - 1); // Will prepend a point below
870
871 // Allocate workspace for the kernel function
872 gsl_vector *work = gsl_vector_alloc(5);
873
874 retcode |= GPR_evaluation_5D(
875 q, chi1, chi2, lambda1, lambda2,
876 sur->hyp_amp,
877 sur->hyp_phi,
878 sur->kinv_dot_y_amp,
879 sur->kinv_dot_y_phi,
880 sur->x_train,
881 sur_amp_at_nodes,
882 sur_phi_at_nodes_tmp,
883 work
884 );
885
886 gsl_vector_free(work);
887
888 if(retcode != 0) {
889 gsl_vector_free(sur_amp_at_nodes);
890 gsl_vector_free(sur_phi_at_nodes_tmp);
891 XLAL_ERROR(retcode);
892 }
893
894 // Prepend the point [mf_min, 0] to the phase nodes
895 // This has already been done in the setup for mf_phi
896 gsl_vector *sur_phi_at_nodes = gsl_vector_prepend_value(sur_phi_at_nodes_tmp, 0.0);
897
898 // Spline the surrogate amplitude and phase corrections in frequency
899 gsl_interp_accel *acc_amp_sur = gsl_interp_accel_alloc();
900 gsl_spline *spline_amp_sur = gsl_spline_alloc(gsl_interp_cspline, N_amp);
901 gsl_spline_init(spline_amp_sur, gsl_vector_const_ptr(sur->mf_amp, 0),
902 gsl_vector_const_ptr(sur_amp_at_nodes, 0), N_amp);
903
904 gsl_interp_accel *acc_phi_sur = gsl_interp_accel_alloc();
905 gsl_spline *spline_phi_sur = gsl_spline_alloc(gsl_interp_cspline, N_phi);
906 gsl_spline_init(spline_phi_sur, gsl_vector_const_ptr(sur->mf_phi, 0),
907 gsl_vector_const_ptr(sur_phi_at_nodes, 0), N_phi);
908
909 gsl_vector_free(sur_amp_at_nodes);
910 gsl_vector_free(sur_phi_at_nodes);
911
912
913 // Evaluate TaylorF2 amplitude and phase on sparse grids
914 gsl_vector *TF2_phi_at_nodes = NULL;
915 retcode |= TaylorF2Phasing(Mtot, q, chi1, chi2, lambda1, lambda2, TF2_mf_phi, &TF2_phi_at_nodes);
916
917 gsl_vector *TF2_amp_at_nodes = NULL;
918 retcode |= TaylorF2Amplitude1PN(eta, TF2_mf_amp, &TF2_amp_at_nodes);
919
920 if(retcode != 0) {
921 gsl_vector_free(TF2_amp_at_nodes);
922 gsl_vector_free(TF2_phi_at_nodes);
923 XLAL_ERROR(retcode);
924 }
925
926 // Spline TaylorF2 amplitude and phase
927 // We add the surrogate corrections to the TaylorF2 spline data
928 // in the frequency range where the surrogate has support.
929
930 // amplitude
931 gsl_interp_accel *acc_amp_TF2 = gsl_interp_accel_alloc();
932 gsl_spline *spline_amp_TF2 = gsl_spline_alloc(spline_type, N_amp_TF2);
933 gsl_vector *spline_amp_values = gsl_vector_alloc(N_amp_TF2);
934 for (int i=0; i<N_amp_TF2; i++) {
935 double Mf = gsl_vector_get(TF2_mf_amp, i);
936 double amp_i = gsl_vector_get(TF2_amp_at_nodes, i);
937 if ((Mf >= Mf_sur_min) & (Mf <= Mf_sur_max))
938 amp_i *= exp(gsl_spline_eval(spline_amp_sur, Mf, acc_amp_sur));
939 gsl_vector_set(spline_amp_values, i, amp_i);
940 }
941 gsl_spline_init(spline_amp_TF2, gsl_vector_const_ptr(TF2_mf_amp, 0),
942 gsl_vector_const_ptr(spline_amp_values, 0), N_amp_TF2);
943
944 // phase
945 gsl_interp_accel *acc_phi_TF2 = gsl_interp_accel_alloc();
946 gsl_spline *spline_phi_TF2 = gsl_spline_alloc(spline_type, N_phi_TF2);
947 gsl_vector *spline_phi_values = gsl_vector_alloc(N_phi_TF2);
948 for (int i=0; i<N_phi_TF2; i++) {
949 double Mf = gsl_vector_get(TF2_mf_phi, i);
950 double phi_i = gsl_vector_get(TF2_phi_at_nodes, i);
951 if ((Mf >= Mf_sur_min) & (Mf <= Mf_sur_max))
952 phi_i += gsl_spline_eval(spline_phi_sur, Mf, acc_phi_sur);
953 gsl_vector_set(spline_phi_values, i, phi_i);
954 }
955 gsl_spline_init(spline_phi_TF2, gsl_vector_const_ptr(TF2_mf_phi, 0),
956 gsl_vector_const_ptr(spline_phi_values, 0), N_phi_TF2);
957
958 gsl_vector_free(TF2_amp_at_nodes);
959 gsl_vector_free(TF2_phi_at_nodes);
960 gsl_vector_free(spline_amp_values);
961 gsl_vector_free(spline_phi_values);
962
963 gsl_spline_free(spline_amp_sur);
964 gsl_spline_free(spline_phi_sur);
965 gsl_interp_accel_free(acc_amp_sur);
966 gsl_interp_accel_free(acc_phi_sur);
967
968
969 // Now setup LAL datastructures for waveform polarizations
970 size_t npts = 0;
971 LIGOTimeGPS tC = {0, 0};
972 UINT4 offset = 0; // Index shift between freqs and the frequency series
973 REAL8Sequence *freqs = NULL;
974 if (deltaF > 0) { // freqs contains uniform frequency grid with spacing deltaF; we start at frequency 0
975 /* Set up output array with size closest power of 2 */
976 npts = NextPow2(fHigh_geom / deltaF_geom) + 1;
977 if (fHigh_geom < fHigh * Mtot_sec) /* Resize waveform if user wants f_max larger than cutoff frequency */
978 npts = NextPow2(fHigh * Mtot_sec / deltaF_geom) + 1;
979
980 XLALGPSAdd(&tC, -1. / deltaF); /* coalesce at t=0 */
981 *hptilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &tC, 0.0, deltaF, &lalStrainUnit, npts);
982 *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &tC, 0.0, deltaF, &lalStrainUnit, npts);
983
984 // Recreate freqs using only the lower and upper bounds
985 // Use fLow, fHigh and deltaF rather than geometric frequencies for numerical accuracy
986 double fHigh_temp = fHigh_geom / Mtot_sec;
987 UINT4 iStart = (UINT4) ceil(fLow / deltaF);
988 UINT4 iStop = (UINT4) ceil(fHigh_temp / deltaF);
989 freqs = XLALCreateREAL8Sequence(iStop - iStart);
990 if (!freqs) {
991 XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
992 }
993 for (UINT4 i=iStart; i<iStop; i++)
994 freqs->data[i-iStart] = i*deltaF_geom;
995
996 offset = iStart;
997 } else { // freqs contains frequencies with non-uniform spacing; we start at lowest given frequency
998 npts = freqs_in->length;
999 *hptilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &tC, fLow, 0, &lalStrainUnit, npts);
1000 *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &tC, fLow, 0, &lalStrainUnit, npts);
1001 offset = 0;
1002
1003 freqs = XLALCreateREAL8Sequence(freqs_in->length);
1004 if (!freqs) {
1005 XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
1006 }
1007 for (UINT4 i=0; i<freqs_in->length; i++)
1008 freqs->data[i] = freqs_in->data[i] * Mtot_sec;
1009 }
1010
1011 if (!(*hptilde) || !(*hctilde)) {
1013 gsl_interp_accel_free(acc_phi_TF2);
1014 gsl_spline_free(spline_phi_TF2);
1015 gsl_interp_accel_free(acc_amp_TF2);
1016 gsl_spline_free(spline_amp_TF2);
1018 }
1019 memset((*hptilde)->data->data, 0, npts * sizeof(COMPLEX16));
1020 memset((*hctilde)->data->data, 0, npts * sizeof(COMPLEX16));
1021
1022 XLALUnitMultiply(&(*hptilde)->sampleUnits, &(*hptilde)->sampleUnits, &lalSecondUnit);
1023 XLALUnitMultiply(&(*hctilde)->sampleUnits, &(*hctilde)->sampleUnits, &lalSecondUnit);
1024
1025 COMPLEX16 *pdata=(*hptilde)->data->data;
1026 COMPLEX16 *cdata=(*hctilde)->data->data;
1027
1028 REAL8 cosi = cos(inclination);
1029 REAL8 pcoef = 0.5*(1.0 + cosi*cosi);
1030 REAL8 ccoef = cosi;
1031
1032 double amp0 = Mtot * Mtot_sec * LAL_MRSUN_SI / (distance); // amplitude prefactor
1033
1034 // Evaluate reference phase for setting phiRef correctly
1035 double phase_change = 0.0;
1036 phase_change = gsl_spline_eval(spline_phi_TF2, fRef_geom, acc_phi_TF2) - 2*phiRef;
1037
1038 // The maximum frequency for which we generate waveform data is set to the
1039 // maximum frequency covered by the surrogate.
1040 Mf_sur_max = gsl_vector_get(sur->mf_phi, N_phi-1);
1041 double Mf_final = Mf_sur_max;
1042
1043 // Assemble waveform from aplitude and phase
1044 for (UINT4 i=0; i<freqs->length; i++) { // loop over frequency points in sequence
1045 double f = freqs->data[i];
1046 if (f > Mf_final) continue; // We're beyond the highest allowed frequency;
1047 // since freqs may not be ordered, we'll just skip the current frequency and leave zero in the buffer
1048 int j = i + offset; // shift index for frequency series if needed
1049 double A = gsl_spline_eval(spline_amp_TF2, f, acc_amp_TF2);
1050 double phase = gsl_spline_eval(spline_phi_TF2, f, acc_phi_TF2) - phase_change;
1051 COMPLEX16 htilde = amp0*A * (cos(phase) + I*sin(phase));//cexp(I*phase);
1052 pdata[j] = pcoef * htilde;
1053 cdata[j] = -I * ccoef * htilde;
1054 }
1055
1056 /* Correct phasing so we coalesce at t=0 (with the definition of the epoch=-1/deltaF above) */
1057 UINT4 L = freqs->length;
1058 // prevent gsl interpolation errors
1059 if (Mf_final > freqs->data[L-1])
1060 Mf_final = freqs->data[L-1];
1061 if (Mf_final < freqs->data[0]) {
1063 gsl_interp_accel_free(acc_phi_TF2);
1064 gsl_spline_free(spline_phi_TF2);
1065 gsl_interp_accel_free(acc_amp_TF2);
1066 gsl_spline_free(spline_amp_TF2);
1067 XLAL_ERROR(XLAL_EDOM, "f_ringdown < f_min");
1068 }
1069
1070 // Time correction is t(f_final) = 1/(2pi) dphi/df (f_final)
1071 // We compute the dimensionless time correction t/M since we use geometric units.
1072 // We use the Schwarzschild ISCO as a rough proxy for the amplitude peak of the BNS waveform.
1073 // We used XLALSimNSNSMergerFreq() earlier and it turned out not to be reliable for extreme input parameters.
1074 REAL8 Mf_ISCO_Schwrazschild = 1.0 / (pow(6.,3./2.)*LAL_PI);
1075 REAL8 t_corr = gsl_spline_eval_deriv(spline_phi_TF2, Mf_ISCO_Schwrazschild, acc_phi_TF2) / (2*LAL_PI);
1076
1077 // Now correct phase
1078 for (UINT4 i=0; i<freqs->length; i++) { // loop over frequency points in sequence
1079 double f = freqs->data[i] - fRef_geom;
1080 int j = i + offset; // shift index for frequency series if needed
1081 double phase_factor = -2*LAL_PI * f * t_corr;
1082 COMPLEX16 t_factor = (cos(phase_factor) + I*sin(phase_factor));//cexp(I*phase_factor);
1083 pdata[j] *= t_factor;
1084 cdata[j] *= t_factor;
1085 }
1086
1088 gsl_interp_accel_free(acc_phi_TF2);
1089 gsl_spline_free(spline_phi_TF2);
1090 gsl_interp_accel_free(acc_amp_TF2);
1091 gsl_spline_free(spline_amp_TF2);
1092
1093 return(XLAL_SUCCESS);
1094}
1095
1096/**
1097 * @addtogroup LALSimIMRSEOBNRv4TSurrogate_c
1098 *
1099 * \author Michael Puerrer, Ben Lackey
1100 *
1101 * \brief C code for SEOBNRv4T surrogate model
1102 * See arXiv:1812.08643
1103 *
1104 * This is a frequency domain model that approximates the time domain TEOBv4 model.
1105 *
1106 * The binary data HDF5 file (SEOBNRv4T_surrogate_v2.0.0.hdf5)
1107 * is available at:
1108 * https://git.ligo.org/waveforms/software/lalsuite-waveform-data
1109 * and on Zenodo at: https://zenodo.org/records/14999310.
1110 * Get the lalsuite-waveform-data repo or put the data into a location in your
1111 * LAL_DATA_PATH.
1112 * The data is also available on CIT at /home/lalsimulation_data and and via CVMFS
1113 * at /cvmfs/shared.storage.igwn.org/igwn/shared/auxiliary/obs_sci/cbc/waveform/lalsimulation_data
1114 * Make sure the files are in your LAL_DATA_PATH.
1115 *
1116 * @note Note that due to its construction the iFFT of the surrogate has a small (~ 20 M) offset
1117 * in the peak time that scales with total mass as compared to the time-domain SEOBNRv4T model.
1118 *
1119 * @note Parameter ranges:
1120 * 1 <= q <= 3
1121 * -0.5 <= chi1 <= 0.5
1122 * -0.5 <= chi2 <= 0.5
1123 * 0 <= lambda1 <= 5000
1124 * 0 <= lambda2 <= 5000
1125 *
1126 * Aligned component spins chi1, chi2.
1127 * Tidal deformabilities of neutron stars lambda1, lambda2.
1128 * Mass-ratio q = m1/m2
1129 *
1130 * @{
1131 */
1132
1133
1134/**
1135 * Compute waveform in LAL format at specified frequencies for the SEOBNRv4T_surrogate model.
1136 *
1137 * XLALSimIMRSEOBNRv4TSurrogate() returns the plus and cross polarizations as a complex
1138 * frequency series with equal spacing deltaF and contains zeros from zero frequency
1139 * to the starting frequency and zeros beyond the cutoff frequency in the ringdown.
1140 *
1141 * In contrast, XLALSimIMRSEOBNRv4TSurrogateFrequencySequence() returns a
1142 * complex frequency series with entries exactly at the frequencies specified in
1143 * the sequence freqs (which can be unequally spaced). No zeros are added.
1144 *
1145 * If XLALSimIMRSEOBNRv4TSurrogateFrequencySequence() is called with frequencies that
1146 * are beyond the maxium allowed geometric frequency for the ROM, zero strain is returned.
1147 * It is not assumed that the frequency sequence is ordered.
1148 *
1149 * This function is designed as an entry point for reduced order quadratures.
1150 */
1152 struct tagCOMPLEX16FrequencySeries **hptilde, /**< Output: Frequency-domain waveform h+ */
1153 struct tagCOMPLEX16FrequencySeries **hctilde, /**< Output: Frequency-domain waveform hx */
1154 const REAL8Sequence *freqs, /**< Frequency points at which to evaluate the waveform (Hz) */
1155 REAL8 phiRef, /**< Orbital phase at reference time */
1156 REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
1157 REAL8 distance, /**< Distance of source (m) */
1158 REAL8 inclination, /**< Inclination of source (rad) */
1159 REAL8 m1SI, /**< Mass of companion 1 (kg) */
1160 REAL8 m2SI, /**< Mass of companion 2 (kg) */
1161 REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1162 REAL8 chi2, /**< Dimensionless aligned component spin 2 */
1163 REAL8 lambda1, /**< Dimensionless tidal deformability 1 */
1164 REAL8 lambda2, /**< Dimensionless tidal deformability 2 */
1165 SEOBNRv4TSurrogate_spline_order spline_order) /**< Spline order in frequency */
1166{
1167 /* Internally we need m1 > m2, so change around if this is not the case */
1168 if (m1SI < m2SI) {
1169 // Swap m1 and m2
1170 double m1temp = m1SI;
1171 double chi1temp = chi1;
1172 double lambda1temp = lambda1;
1173 m1SI = m2SI;
1174 chi1 = chi2;
1175 lambda1 = lambda2;
1176 m2SI = m1temp;
1177 chi2 = chi1temp;
1178 lambda2 = lambda1temp;
1179 }
1180
1181 /* Get masses in terms of solar mass */
1182 double mass1 = m1SI / LAL_MSUN_SI;
1183 double mass2 = m2SI / LAL_MSUN_SI;
1184 double Mtot = mass1+mass2;
1185 double eta = mass1 * mass2 / (Mtot*Mtot); /* Symmetric mass-ratio */
1186 double Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
1187
1188 if (!freqs) XLAL_ERROR(XLAL_EFAULT);
1189
1190 // Load ROM data if not loaded already
1191#ifdef LAL_PTHREAD_LOCK
1192 (void) pthread_once(&Surrogate_is_initialized, Surrogate_Init_LALDATA);
1193#else
1195#endif
1196
1197 if(!Surrogate_IsSetup()) {
1199 "Error setting up Surrogate data - check your $LAL_DATA_PATH\n");
1200 }
1201
1202 // Call the internal core function with deltaF = 0 to indicate that freqs is non-uniformly
1203 // spaced and we want the strain only at these frequencies
1204 int retcode = SurrogateCore(hptilde, hctilde, phiRef, fRef, distance,
1205 inclination, Mtot_sec, eta, chi1, chi2,
1206 lambda1, lambda2, freqs, 0, spline_order);
1207
1208 return(retcode);
1209}
1210
1211/**
1212 * Compute waveform in LAL format for the SEOBNRv4T_surrogate model.
1213 *
1214 * Returns the plus and cross polarizations as a complex frequency series with
1215 * equal spacing deltaF and contains zeros from zero frequency to the starting
1216 * frequency fLow and zeros beyond the cutoff frequency in the ringdown.
1217 */
1219 struct tagCOMPLEX16FrequencySeries **hptilde, /**< Output: Frequency-domain waveform h+ */
1220 struct tagCOMPLEX16FrequencySeries **hctilde, /**< Output: Frequency-domain waveform hx */
1221 REAL8 phiRef, /**< Phase at reference time */
1222 REAL8 deltaF, /**< Sampling frequency (Hz) */
1223 REAL8 fLow, /**< Starting GW frequency (Hz) */
1224 REAL8 fHigh, /**< End frequency; 0 defaults to Mf=0.14 */
1225 REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
1226 REAL8 distance, /**< Distance of source (m) */
1227 REAL8 inclination, /**< Inclination of source (rad) */
1228 REAL8 m1SI, /**< Mass of companion 1 (kg) */
1229 REAL8 m2SI, /**< Mass of companion 2 (kg) */
1230 REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1231 REAL8 chi2, /**< Dimensionless aligned component spin 2 */
1232 REAL8 lambda1, /**< Dimensionless tidal deformability 1 */
1233 REAL8 lambda2, /**< Dimensionless tidal deformability 2 */
1234 SEOBNRv4TSurrogate_spline_order spline_order) /**< Spline order in frequency */
1235{
1236 /* Internally we need m1 > m2, so change around if this is not the case */
1237 if (m1SI < m2SI) {
1238 // Swap m1 and m2
1239 double m1temp = m1SI;
1240 double chi1temp = chi1;
1241 double lambda1temp = lambda1;
1242 m1SI = m2SI;
1243 chi1 = chi2;
1244 lambda1 = lambda2;
1245 m2SI = m1temp;
1246 chi2 = chi1temp;
1247 lambda2 = lambda1temp;
1248 }
1249
1250 /* Get masses in terms of solar mass */
1251 double mass1 = m1SI / LAL_MSUN_SI;
1252 double mass2 = m2SI / LAL_MSUN_SI;
1253 double Mtot = mass1+mass2;
1254 double eta = mass1 * mass2 / (Mtot*Mtot); /* Symmetric mass-ratio */
1255 double Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
1256
1257 if(fRef==0.0)
1258 fRef=fLow;
1259
1260 // Load ROM data if not loaded already
1261#ifdef LAL_PTHREAD_LOCK
1262 (void) pthread_once(&Surrogate_is_initialized, Surrogate_Init_LALDATA);
1263#else
1265#endif
1266
1267 // Use fLow, fHigh, deltaF to compute freqs sequence
1268 // Instead of building a full sequency we only transfer the boundaries and let
1269 // the internal core function do the rest (and properly take care of corner cases).
1271 freqs->data[0] = fLow;
1272 freqs->data[1] = fHigh;
1273
1274 int retcode = SurrogateCore(hptilde, hctilde, phiRef, fRef, distance,
1275 inclination, Mtot_sec, eta, chi1, chi2,
1276 lambda1, lambda2, freqs, deltaF, spline_order);
1277
1279
1280 return(retcode);
1281}
1282
1283/** @} */
1284
1285
1286/** Setup Surrogate model using data files installed in $LAL_DATA_PATH
1287 */
1288UNUSED static void Surrogate_Init_LALDATA(void)
1289{
1290 if (Surrogate_IsSetup()) return;
1291
1292 // Expect ROM datafile in a directory listed in LAL_DATA_PATH,
1293#ifdef LAL_HDF5_ENABLED
1294#define datafile SurDataHDF5
1295 char *path = XLAL_FILE_RESOLVE_PATH(datafile);
1296 if (path==NULL)
1298 "Unable to resolve data file '%s' in $LAL_DATA_PATH.\n"
1299 "Note: LALSuite versions >= 7.25 require data files that are publicly available at:\n"
1300 "https://git.ligo.org/waveforms/software/lalsuite-waveform-data\n"
1301 "and on Zenodo at: https://zenodo.org/records/14999310.\n"
1302 "For earlier LALSuite versions, use the files in lalsuite-extra, available at:\n"
1303 "https://git.ligo.org/lscsoft/lalsuite-extra\n",
1304 datafile);
1305 char *dir = dirname(path);
1306 int ret = Surrogate_Init(dir);
1307 XLALFree(path);
1308
1309 if(ret!=XLAL_SUCCESS)
1310 XLAL_ERROR_VOID(XLAL_FAILURE, "Unable to find Surrogate data files in $LAL_DATA_PATH\n");
1311#else
1312 XLAL_ERROR_VOID(XLAL_EFAILED, "Surrogate requires HDF5 support which is not enabled\n");
1313#endif
1314}
struct tagLALH5File LALH5File
void XLALDestroyDict(LALDict *dict)
LALDict * XLALCreateDict(void)
Auxiliary functions related to HDF5 waveform data files.
static const REAL8 q_max
Auxiliary functions for SEOBNRv1/v2 reduced order modeling codes in LALSimIMRSEOBNRv2ROMDoubleSpin....
static UNUSED int Surrogate_Init(const char dir[])
Setup Surrogate model using data files installed in dir.
static UNUSED void save_gsl_frequency_series(const char filename[], gsl_vector *x, gsl_vector *y)
int(* load_dataPtr)(const char *, gsl_vector *, gsl_vector *, gsl_matrix *, gsl_matrix *, gsl_vector *)
static Surrogatedata __lalsim_SurrogateDS_data
static int GPR_evaluation_5D(double q, double chi1, double chi2, double lambda1, double lambda2, gsl_matrix *hyp_amp, gsl_matrix *hyp_phi, gsl_matrix *kinv_dot_y_amp, gsl_matrix *kinv_dot_y_phi, gsl_matrix *x_train, gsl_vector *amp_at_EI_nodes, gsl_vector *phi_at_EI_nodes, gsl_vector *work)
double gp_predict(gsl_vector *xst, gsl_vector *hyperparams, gsl_matrix *x_train, gsl_vector *Kinv_dot_y, gsl_vector *work)
static UNUSED int Surrogatedata_Init(Surrogatedata *romdata, const char dir[])
static UNUSED int Surrogatedata_Init_submodel(UNUSED Surrogatedata_submodel **submodel, UNUSED const char dir[], UNUSED const char grp_name[])
static UNUSED void Surrogatedata_Cleanup_submodel(Surrogatedata_submodel *submodel)
static UNUSED void Surrogatedata_Cleanup(Surrogatedata *romdata)
static int TaylorF2Amplitude1PN(double eta, gsl_vector *Mfs, gsl_vector **PNamp)
static UNUSED void Surrogate_Init_LALDATA(void)
Setup Surrogate model using data files installed in $LAL_DATA_PATH.
static double xi_of_lambda(double lambda)
Coordinate transformation.
static gsl_vector * gsl_vector_prepend_value(gsl_vector *v, double value)
double kernel(gsl_vector *x1, gsl_vector *x2, gsl_vector *hyperparams, gsl_vector *work)
static UNUSED bool Surrogate_IsSetup(void)
Helper function to check if the Surrogate model has been initialised.
static size_t NextPow2(const size_t n)
static UNUSED int CheckParameterSpaceBounds(Surrogatedata_submodel *sur, double q, double chi1, double chi2, double lambda1, double lambda2)
static UNUSED int SurrogateCore(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, double phiRef, double fRef, double distance, double inclination, double Mtot_sec, double eta, double chi1, double chi2, double lambda1, double lambda2, const REAL8Sequence *freqs, double deltaF, SEOBNRv4TSurrogate_spline_order spline_order)
Core function for computing the ROM waveform.
static int TaylorF2Phasing(double Mtot, double q, double chi1, double chi2, double lambda1, double lambda2, gsl_vector *Mfs, gsl_vector **PNphase)
static REAL8 UNUSED XLALSimInspiralTaylorF2Phasing_6PNS1S2OCoeff(REAL8 eta)
static REAL8 UNUSED XLALSimInspiralTaylorF2Phasing_6PNQM2SCoeff(REAL8 mByM)
static REAL8 UNUSED XLALSimInspiralTaylorF2Phasing_6PNSelf2SCoeff(REAL8 mByM)
int XLALSimInspiralWaveformParamsInsertTidalLambda1(LALDict *params, REAL8 value)
int XLALSimInspiralWaveformParamsInsertPNSpinOrder(LALDict *params, INT4 value)
int XLALSimInspiralWaveformParamsInsertTidalLambda2(LALDict *params, REAL8 value)
int XLALSimInspiralWaveformParamsInsertdQuadMon2(LALDict *params, REAL8 value)
int XLALSimInspiralWaveformParamsInsertPNTidalOrder(LALDict *params, INT4 value)
int XLALSimInspiralWaveformParamsInsertdQuadMon1(LALDict *params, REAL8 value)
REAL8 XLALSimUniversalRelationQuadMonVSlambda2Tidal(REAL8 lambda2bar)
#define fprintf
double i
Definition: bh_ringdown.c:118
const double pn
sigmaKerr data[0]
#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
void * XLALMalloc(size_t n)
void * XLALCalloc(size_t m, size_t n)
void XLALFree(void *p)
SEOBNRv4TSurrogate_spline_order
Definition: LALSimIMR.h:90
@ SEOBNRv4TSurrogate_CUBIC
use cubic splines in frequency
Definition: LALSimIMR.h:91
@ SEOBNRv4TSurrogate_LINEAR
use linear splines in frequency
Definition: LALSimIMR.h:92
int XLALSimIMRSEOBNRv4TSurrogateFrequencySequence(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, REAL8 lambda1, REAL8 lambda2, SEOBNRv4TSurrogate_spline_order spline_order)
Compute waveform in LAL format at specified frequencies for the SEOBNRv4T_surrogate model.
int XLALSimIMRSEOBNRv4TSurrogate(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, REAL8 lambda1, REAL8 lambda2, SEOBNRv4TSurrogate_spline_order spline_order)
Compute waveform in LAL format for the SEOBNRv4T_surrogate model.
@ LAL_SIM_INSPIRAL_SPIN_ORDER_35PN
@ LAL_SIM_INSPIRAL_TIDAL_ORDER_6PN
int XLALSimInspiralTaylorF2AlignedPhasing(PNPhasingSeries **pfa, const REAL8 m1, const REAL8 m2, const REAL8 chi1, const REAL8 chi2, LALDict *extraPars)
Returns structure containing TaylorF2 phasing coefficients for given physical parameters.
static const INT4 r
static const INT4 q
static const INT4 a
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,...)
#define XLAL_CHECK_ABORT(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_EDIMS
XLAL_EINVAL
XLAL_EFAILED
XLAL_FAILURE
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
list y
path
filename
x
REAL8 * data
Surrogatedata_submodel * sub1
FILE * fp