LALSimulation  5.4.0.1-fe68b98
LALSimNRHybSurUtilities.c
Go to the documentation of this file.
1 /* Copyright (C) 2018 Vijay Varma
2  * Utility functions that are useful for evaluating NRHybrid surrogates.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with with program; see the file COPYING. If not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301 USA
18  */
19 
20 /**
21  * \author Vijay Varma
22  *
23  * \file
24  *
25  * \brief Utilities needed for aligned-spin NR-hybrid surrogate models.
26  *
27  * Called from:
28  * LALSimIMRNRHybSur3dq8.h
29  */
30 
31 #ifdef __GNUC__
32 #define UNUSED __attribute__ ((unused))
33 #else
34 #define UNUSED
35 #endif
36 
37 
39 
40 #include <gsl/gsl_bspline.h>
41 #include <gsl/gsl_blas.h>
42 #include <gsl/gsl_spline.h>
43 #include <gsl/gsl_complex_math.h>
44 
45 #include <lal/SeqFactories.h>
46 #include <lal/H5FileIO.h>
47 
48 #include <lal/XLALError.h>
49 #include <lal/LALConstants.h>
50 #include <lal/LALSimIMR.h>
51 
54 
55 
56 //*************************************************************************/
57 //************************* function definitions **************************/
58 //*************************************************************************/
59 
60 
61 
62 
63 //********************* H5 wrapper functions ************************/
64 
65 /**
66  * Reads a REAL8 value from a H5 file/group.
67  */
69  REAL8 *param, /**< Output: REAL8 value from H5 file/group. */
70  LALH5File *sub, /**< H5 file or group. */
71  const char *name /**< Name of REAL8 dataset within file/group. */
72 ) {
74 
75  // Expecting a single double
76  if(data == NULL || data->length != 1) {
79  "Failed to load `%s' scalar dataset\n", name);
80  }
81 
82  *param = data->data[0];
84 
85  return XLAL_SUCCESS;
86 }
87 
88 
89 /**
90  * Reads an INT8 value from a H5 file/group.
91  */
93  int *param, /**< Output: int value from H5 file/group. */
94  LALH5File *sub, /**< H5 file or group. */
95  const char *name /**< Name of int dataset within file/group. */
96 ) {
98 
99  // Expecting a single int
100  if(data == NULL || data->length != 1) {
103  "Failed to load `%s' scalar dataset\n", name);
104  }
105 
106  *param = (int)data->data[0];
108 
109  return XLAL_SUCCESS;
110 }
111 
112 
113 
114 
115 
116 //********************* Surrogate loading functions ************************/
117 // These should only be called once, when initializing the surrogate
118 
119 /**
120  * Loads a single waveform data piece from a H5 file.
121  */
123  DataPiece **data_piece, /**< Output: Waveform data piece. *data_piece
124  should be NULL. Space will be allocated. */
125  LALH5File *file, /**< Opened HDF5 file. */
126  const char *sub_grp_name /**< H5 group name. */
127 ) {
128 
129  if (data_piece == NULL || *data_piece != NULL) {
130  XLAL_ERROR(XLAL_EFAULT, "*data_piece should be NULL");
131  }
132  if (file == NULL) {
133  XLAL_ERROR(XLAL_EFAULT, "file should not be NULL");
134  }
135 
136  // Open h5 group
137  LALH5File *sub;
138  sub = XLALH5GroupOpen(file, sub_grp_name);
139  *data_piece = XLALMalloc(sizeof(DataPiece));
140 
141  gsl_matrix *ei_basis = NULL;
142  int ret = ReadHDF5RealMatrixDataset(sub, "ei_basis", &ei_basis);
143  if (ret != XLAL_SUCCESS) {
144  XLAL_ERROR(XLAL_EFUNC, "Failed to load ei_basis.");
145  }
146 
147  (*data_piece)->ei_basis = ei_basis;
148 
149  // Get number of empirical time nodes
150  int n_nodes;
151  ret = ReadHDF5IntDataset(&n_nodes, sub, "n_nodes");
152  if (ret != XLAL_SUCCESS) {
153  XLAL_ERROR(XLAL_EFUNC, "Failed to load n_nodes.");
154  }
155 
156  (*data_piece)->n_nodes = n_nodes;
157  (*data_piece)->fit_data = XLALMalloc( n_nodes * sizeof(NRHybSurFitData *) );
158 
159  // Load fit data for each empirical time node
160  const size_t str_size = 20;
161  char *node_name = XLALMalloc(str_size);
162  UNUSED size_t nwritten;
163  for (int i=0; i<n_nodes; i++) {
164 
165  nwritten = snprintf(node_name, str_size, "node_num_%d", i);
166  XLAL_CHECK_ABORT(nwritten < str_size);
167  LALH5File *node_function = XLALH5GroupOpen(sub, node_name);
168  NRHybSurFitData *fit_data = XLALMalloc(sizeof(NRHybSurFitData));
169 
170  GPRHyperParams *hyperparams = XLALMalloc(sizeof(GPRHyperParams));
171 
172  // Load scalars needed for fit
173  ret = ReadHDF5DoubleDataset(&(hyperparams->constant_value),
174  node_function, "constant_value");
175  if (ret != XLAL_SUCCESS) {
176  XLALFree(node_name);
177  XLAL_ERROR(XLAL_EFUNC, "Failed to load constant_value.");
178  }
179 
180  ret = ReadHDF5DoubleDataset(&(hyperparams->y_train_mean),
181  node_function, "y_train_mean");
182  if (ret != XLAL_SUCCESS) {
183  XLALFree(node_name);
184  XLAL_ERROR(XLAL_EFUNC, "Failed to load y_train_mean.");
185  }
186 
187  ret = ReadHDF5DoubleDataset(&(fit_data->data_mean),
188  node_function, "data_mean");
189  if (ret != XLAL_SUCCESS) {
190  XLALFree(node_name);
191  XLAL_ERROR(XLAL_EFUNC, "Failed to load data_mean.");
192  }
193 
194  ret = ReadHDF5DoubleDataset(&(fit_data->data_std),
195  node_function, "data_std");
196  if (ret != XLAL_SUCCESS) {
197  XLALFree(node_name);
198  XLAL_ERROR(XLAL_EFUNC, "Failed to load data_std.");
199  }
200 
201  ret = ReadHDF5DoubleDataset(&(fit_data->lin_intercept),
202  node_function, "lin_intercept");
203  if (ret != XLAL_SUCCESS) {
204  XLALFree(node_name);
205  XLAL_ERROR(XLAL_EFUNC, "Failed to load lin_intercept.");
206  }
207 
208 
209  // Load arrays needed for fit
210  hyperparams->length_scale = NULL;
211  ret = ReadHDF5RealVectorDataset(node_function, "length_scale",
212  &(hyperparams->length_scale));
213  if (ret != XLAL_SUCCESS) {
214  XLALFree(node_name);
215  XLAL_ERROR(XLAL_EFUNC, "Failed to load length_scale.");
216  }
217 
218  hyperparams->alpha = NULL;
219  ret = ReadHDF5RealVectorDataset(node_function, "alpha",
220  &(hyperparams->alpha));
221  if (ret != XLAL_SUCCESS) {
222  XLALFree(node_name);
223  XLAL_ERROR(XLAL_EFUNC, "Failed to load alpha.");
224  }
225 
226  fit_data->lin_coef = NULL;
227  ret = ReadHDF5RealVectorDataset(node_function, "lin_coef",
228  &(fit_data->lin_coef));
229  if (ret != XLAL_SUCCESS) {
230  XLALFree(node_name);
231  XLAL_ERROR(XLAL_EFUNC, "Failed to load lin_coef.");
232  }
233 
234  XLALH5FileClose(node_function);
235 
236  fit_data->hyperparams = hyperparams;
237  (*data_piece)->fit_data[i] = fit_data;
238  }
239 
240  XLALFree(node_name);
241  XLALH5FileClose(sub);
242 
243  return ret;
244 }
245 
246 /**
247  * Loads all data pieces of a single waveform mode.
248  */
250  ModeDataPieces **mode_data_pieces, /**< Output: Waveform data pieces of a
251  given mode. Space will be allocated to
252  **mode_data_pieces. */
253  LALH5File *file, /**< Opened HDF5 file. */
254  const int mode_idx, /**< Index corresponding to the mode. */
255  const gsl_matrix_long *mode_list /**< List of all modes. */
256 ) {
257 
258  if (mode_data_pieces == NULL) {
259  XLAL_ERROR(XLAL_EFAULT, "mode_data_pieces should not be NULL");
260  }
261  if (file == NULL) {
262  XLAL_ERROR(XLAL_EFAULT, "file should not be NULL");
263  }
264 
265  int ret;
266  const UINT4 ell = gsl_matrix_long_get(mode_list, mode_idx, 0);
267  const UINT4 m = gsl_matrix_long_get(mode_list, mode_idx, 1);
268 
269  ModeDataPieces *data_pieces
270  = XLALMalloc(sizeof(*mode_data_pieces[mode_idx]));
271 
272  data_pieces->ell = ell;
273  data_pieces->m = m;
274 
275  // At most two of these data pieces are needed for each mode,
276  // so let's set them to NULL by default. This will also raise
277  // an error if the wrong data piece is used for a mode
278  data_pieces->ampl_data_piece = NULL;
279  data_pieces->phase_res_data_piece = NULL;
280  data_pieces->coorb_re_data_piece = NULL;
281  data_pieces->coorb_im_data_piece = NULL;
282 
283  const size_t str_size = 20;
284  char *sub_grp_name = XLALMalloc(str_size);
285 
286  UNUSED size_t nwritten;
287  if (ell == 2 && m ==2){
288 
289  // Amplitude of 22 mode
290  nwritten = snprintf(sub_grp_name, str_size, "l%u_m%u/amp", ell, m);
291  XLAL_CHECK_ABORT(nwritten < str_size);
292  ret = NRHybSur_LoadDataPiece(&(data_pieces)->ampl_data_piece,
293  file, sub_grp_name);
294  if(ret != XLAL_SUCCESS) {
295  XLALFree(sub_grp_name);
297  "Failed to load `%s' data piece", sub_grp_name);
298  }
299 
300  // phase of 22 mode
301  nwritten = snprintf(sub_grp_name, str_size, "l%u_m%u/phase", ell, m);
302  XLAL_CHECK_ABORT(nwritten < str_size);
303  ret = NRHybSur_LoadDataPiece(&(data_pieces)->phase_res_data_piece,
304  file, sub_grp_name);
305  if(ret != XLAL_SUCCESS) {
306  XLALFree(sub_grp_name);
308  "Failed to load `%s' data piece", sub_grp_name);
309  }
310 
311  } else {
312  // For m=0, l=even, the imaginary part is zero, so we only need to
313  // load the real part. But when m!=0, we still want to load the real
314  // part.
315  if (m != 0 || ell % 2 == 0) {
316  // Real part of coorbital frame mode
317  nwritten = snprintf(sub_grp_name, str_size, "l%u_m%u/re", ell, m);
318  XLAL_CHECK_ABORT(nwritten < str_size);
319  ret = NRHybSur_LoadDataPiece(&(data_pieces)->coorb_re_data_piece,
320  file, sub_grp_name);
321  if(ret != XLAL_SUCCESS) {
322  XLALFree(sub_grp_name);
324  "Failed to load `%s' data piece", sub_grp_name);
325  }
326  }
327 
328  // Similarly, for m=0, l=odd, the real part is zero, so we only need
329  // to load the imaginary part. But when m!=0, we still want to load
330  // the imaginary part.
331  if (m != 0 || ell % 2 == 1) {
332  // Imaginary part of coorbital frame mode
333  nwritten = snprintf(sub_grp_name, str_size, "l%u_m%u/im", ell, m);
334  XLAL_CHECK_ABORT(nwritten < str_size);
335  ret = NRHybSur_LoadDataPiece(&(data_pieces)->coorb_im_data_piece,
336  file, sub_grp_name);
337  if(ret != XLAL_SUCCESS) {
338  XLALFree(sub_grp_name);
340  "Failed to load `%s' data piece", sub_grp_name);
341  }
342  }
343  }
344 
345  mode_data_pieces[mode_idx] = data_pieces;
346  XLALFree(sub_grp_name);
347  return ret;
348 }
349 
350 
351 /**
352  * Initialize a NRHybSurData structure from an open H5 file.
353  * This will typically only be called once.
354  * For example from NRHybSur3dq8_Init_LALDATA.
355  *
356  * The HDF5 file should have the following structure:
357  *
358  * - Top level: \n
359  * 'GPR_X_train': h5 dataset, shape=(N,dim) where N=size of training dataset. \n
360  * 'domain': h5 dataset, size = number of time samples, can be sparse. \n
361  * 'TaylorT3_t_ref': h5 dataset, INT8, used in TaylorT3 piece. \n
362  * 'mode_list': h5 dataset, INT8, shape=(M,2), where M=num. of modes. Each
363  * element is (ell,m) of that mode. Only m>=0 modes are modeled. First
364  * mode should always be (2,2). \n
365  * 'phaseAlignIdx: h5 dataset, INT8. Needed in TaylorT3 piece. \n
366  * 'l2_m0': h5 group, surrogate data needed for this mode. \n
367  * 'l2_m1': \n
368  * 'l2_m2': \n
369  *
370  * - Subgroups for each mode: \n
371  * - 'l2_m2': \n
372  * 'amp': h5 group, surrogate data needed for amplitude data piece. \n
373  * 'phase' : h5 group, surrogate data needed for phase data piece. \n
374  *
375  * - 'l2_m1' and all other modes: \n
376  * 're': h5 group, data needed for real part of coorbital frame
377  * waveform data piece. \n
378  * 'im': h5 group, data needed for imag part of coorbital frame
379  * waveform data piece. \n
380  * - Subgroups for each data piece: \n
381  * 'ei_basis': h5 dataset, shape=(n_nodes, L), where L=len(domain). \n
382  * 'n_nodes': h5 dataset, INT8. Num. of empirical time nodes for this data
383  * piece. \n
384  * 'node_num_0': h5 group, fit data for this node. \n
385  * 'node_num_1': \n
386  * - Subgroups for each node: \n
387  * 'alpha': h5 dataset, size=N, where N=size of training dataset. \n
388  * 'constant_value': h5 dataset, REAL8, constant value in kernel. \n
389  * 'data_mean': h5 dataset, REAL8. Mean of fit data. \n
390  * 'data_std': h5 dataset, REAL8. Standard deviation of fit data. \n
391  * 'length_scale': h5 dataset, size=dom, where dim=dimensions of the model.
392  * Length scale parameters in the kernel. \n
393  * 'lin_coef': h5 dataset, size=dim. Coefs of of linear fit. \n
394  * 'lin_intercept': h5 dataset, REAL8. Intercept of linear fit. \n
395  * 'L': h5 dataset, shape=(N,N), where N=size of training dataset. Not used
396  * right now, needed to evaluate error estimate. \n
397  * 'noise_level': h5 dataset, REAL8. Noise parameter, not needed for
398  * evaluating fit. \n
399  * 'y_train_mean': h5 dataset, REAL8. Mean value before doing GPR fit,
400  * usually zero. We already remove the mean and store it in
401  * data_mean. \n
402  */
404  NRHybSurData *NR_hybsur_data, /**< Output: Struct to save surrogate data. */
405  LALH5File *file /**< Opened HDF5 file. */
406 ) {
407 
408  if (NR_hybsur_data == NULL) {
409  XLAL_ERROR(XLAL_EFAULT, "NR_hybsur_data should not be NULL");
410  }
411  if (file == NULL) {
412  XLAL_ERROR(XLAL_EFAULT, "file should not be NULL");
413  }
414 
415  if (NR_hybsur_data->setup) {
417  "Model was already initialized. Ignoring.");
418  }
419 
420  gsl_vector *domain = NULL;
421  int ret = ReadHDF5RealVectorDataset(file, "domain", &domain);
422  if (ret != XLAL_SUCCESS) {
423  XLAL_ERROR(XLAL_EFUNC, "Failed to load domain.");
424  }
425  NR_hybsur_data->domain = domain;
426 
427  gsl_matrix *x_train = NULL;
428  ret = ReadHDF5RealMatrixDataset(file, "GPR_X_train", &x_train);
429  if (ret != XLAL_SUCCESS) {
430  XLAL_ERROR(XLAL_EFUNC, "Failed to load GPR_X_train.");
431  }
432  NR_hybsur_data->x_train = x_train;
433 
434  // dimension of the surrogate
435  NR_hybsur_data->params_dim = x_train->size2;
436 
437  gsl_matrix_long *mode_list = NULL;
438  ret = ReadHDF5LongMatrixDataset(file, "mode_list", &mode_list);
439  if (ret != XLAL_SUCCESS) {
440  XLAL_ERROR(XLAL_EFUNC, "Failed to load mode_list.");
441  }
442  NR_hybsur_data->mode_list = mode_list;
443 
444  UINT4 num_modes_modeled = mode_list->size1;
445  NR_hybsur_data->num_modes_modeled = num_modes_modeled;
446 
447  ModeDataPieces **mode_data_pieces
448  = XLALMalloc((NR_hybsur_data->num_modes_modeled)
449  * sizeof(*mode_data_pieces));
450 
451  // These are needed for the TaylorT3 term
452  int phaseAlignIdx;
453  ret = ReadHDF5IntDataset(&phaseAlignIdx, file, "phaseAlignIdx");
454  if (ret != XLAL_SUCCESS) {
455  XLAL_ERROR(XLAL_EFUNC, "Failed to load phaseAlignIdx.");
456  }
457 
458  REAL8 TaylorT3_t_ref;
459  ret = ReadHDF5DoubleDataset(&TaylorT3_t_ref, file, "TaylorT3_t_ref");
460  if (ret != XLAL_SUCCESS) {
461  XLAL_ERROR(XLAL_EFUNC, "Failed to load TaylorT3_t_ref.");
462  }
463 
464  // This stores a precomputed vector needed in computing the TaylorT3 term
465  gsl_vector *TaylorT3_factor_without_eta = gsl_vector_alloc(domain->size);
466  if (TaylorT3_factor_without_eta == NULL){
467  XLAL_ERROR(XLAL_ENOMEM, "gsl_vector_alloc failed.");
468  }
469 
470  for (UINT4 i=0; i<domain->size; i++){
471  const REAL8 tVal = gsl_vector_get(domain, i);
472  const REAL8 theta_without_eta = pow((TaylorT3_t_ref - tVal)/5., -1./8);
473  gsl_vector_set(TaylorT3_factor_without_eta, i,
474  -2./pow(theta_without_eta, 5));
475  }
476  NR_hybsur_data->TaylorT3_factor_without_eta = TaylorT3_factor_without_eta;
477  NR_hybsur_data->phaseAlignIdx = phaseAlignIdx;
478 
479  for (UINT4 mode_idx = 0; mode_idx < num_modes_modeled; mode_idx++) {
480  ret = NRHybSur_LoadSingleModeData(mode_data_pieces, file,
481  mode_idx, mode_list);
482  if (ret != XLAL_SUCCESS) {
483  XLAL_ERROR(XLAL_EFUNC, "Failed to load mode data.");
484  }
485  }
486 
487  NR_hybsur_data->mode_data_pieces = mode_data_pieces;
488 
489  if (ret == XLAL_SUCCESS){
490  NR_hybsur_data->setup = 1;
491  }
492 
493  return ret;
494 }
495 
496 
497 
498 
499 
500 
501 //******************* Surrogate evaluation functions **********************/
502 
503 /**
504  * The GPR Kernel evaluated at a single pair of points.
505  *
506  * We follow sklearn closely. We use the kernel given in
507  * Eq.(S3) of arxiv:1809.09125 :
508  * \f[
509  * K(x, x') = \sigma_k^2 exp(-1/2 \sum_i^D (x^{i} - x'^{i})^2/\sigma_i^2)
510  * \f]
511  * where D is the dimension of the model.
512  *
513  * Note that Eq.(S3) also includes the WhiteKernel, which has a noise
514  * parameter, but we don't need that here since we only need to evaluate \f$
515  * K_{x* x} \f$ when evaluating the fits (See the mean value in Eq.(S2) of same
516  * paper). The other term we need is alpha = \f$ K_{x x}^{-1} {\bf f}\f$, which
517  * involves the WhiteKernel, but is precomputed offline. alpha is a vector of
518  * size N, where N is the number of cases in the training data set.
519  */
520 static REAL8 kernel(
521  const gsl_vector *x1, /**< Parameter space point 1. */
522  const gsl_vector *x2, /**< Parameter space point 2. */
523  const GPRHyperParams *hyperparams, /**< GPR hyperparameters. */
524  gsl_vector *dummy_worker /**< Dummy worker array for computations. */
525  )
526 {
527  const gsl_vector ls = *hyperparams->length_scale;
528 
529  XLAL_CHECK(
530  (x1->size == x2->size) && (x1->size == ls.size)
531  && (x1->size == dummy_worker->size), XLAL_EDIMS,
532  "Mismatch in size of x1, x2, dummy_worker, ls: %zu, %zu, %zu, %zu.\n",
533  x1->size, x2->size, dummy_worker->size, ls.size);
534 
535  gsl_vector_memcpy(dummy_worker, x1);
536  gsl_vector_sub(dummy_worker, x2);
537  gsl_vector_div(dummy_worker, &ls); // (x1 - x2) / ls
538  const REAL8 r = gsl_blas_dnrm2(dummy_worker);
539 
540  // RBF kernel
541  return hyperparams->constant_value * exp(-r*r/2.0);
542 }
543 
544 
545 /**
546  * Evaluate a GPR fit. See Eq.(S2) of arxiv:1809.09125.
547  */
549  const gsl_vector *xst, /**< Point \f$ x_* \f$ where fit will be
550  evaluated. */
551  const GPRHyperParams *hyperparams, /**< Hyperparams for the GPR kernel. */
552  const gsl_matrix *x_train, /**< Training set points. */
553  gsl_vector *dummy_worker /**< Dummy worker array for computations. */
554  )
555 {
556  // Evaluate vector K_*
557  const UINT4 n = x_train->size1;
558  gsl_vector *Kst = gsl_vector_alloc(n);
559  if (Kst == NULL){
560  XLAL_ERROR(XLAL_ENOMEM, "gsl_vector_alloc failed.");
561  }
562  for (UINT4 i=0; i < n; i++) {
563  const gsl_vector x = gsl_matrix_const_row(x_train, i).vector;
564  const REAL8 ker = kernel(xst, &x, hyperparams, dummy_worker);
565  gsl_vector_set(Kst, i, ker);
566  }
567 
568  // Evaluate y_*
569  REAL8 res = 0;
570  gsl_blas_ddot(Kst, hyperparams->alpha, &res);
571  gsl_vector_free(Kst);
572 
573  return res + hyperparams->y_train_mean;
574 }
575 
576 
577 
578 /**
579  * Evaluate a NRHybSur fit.
580  */
582  const NRHybSurFitData *fit_data, /**< Data for fit. */
583  const gsl_vector *fit_params, /**< Parameter space point to evaluate the fit
584  at. size=D, the dimension of the model. */
585  const gsl_matrix *x_train, /**< Training set points. */
586  gsl_vector *dummy_worker /**< Dummy worker array for computations. */
587 ) {
588 
589  // Get GPR evaluation
590  REAL8 fit_val = gp_predict(fit_params, fit_data->hyperparams,
591  x_train, dummy_worker);
592 
593  // The data was zero-meaned and normalized before constructing the fit,
594  // now do the inverse of that.
595  fit_val = fit_val * fit_data->data_std + fit_data->data_mean;
596 
597  // A linear fit was removed first, now add that back
598  gsl_vector_memcpy(dummy_worker, fit_data->lin_coef);
599  gsl_vector_mul(dummy_worker, fit_params);
600  for (UINT4 i=0; i < dummy_worker->size; i++) {
601  fit_val += gsl_vector_get(dummy_worker, i);
602  }
603  fit_val += fit_data->lin_intercept;
604 
605  return fit_val;
606 }
607 
608 /**
609  * Evaluate a single NRHybSur waveform data piece.
610  */
612  gsl_vector **result, /**< Output: Should have already been assigned
613  space.*/
614  const gsl_vector *fit_params, /**< Parameter space point to evaluate the
615  fit at. size=D, the dimension of the model. */
616  const DataPiece *data_piece, /**< The waveform data piece to evaluate */
617  const gsl_matrix *x_train, /**< Training set points. */
618  gsl_vector *dummy_worker /**< Dummy worker array for computations. */
619 ) {
620 
621  gsl_vector *nodes = gsl_vector_alloc(data_piece->n_nodes);
622  if (nodes == NULL){
623  XLAL_ERROR(XLAL_ENOMEM, "gsl_vector_alloc failed.");
624  }
625  for (int i=0; i < data_piece->n_nodes; i++) {
626  const REAL8 fit_val = NRHybSur_eval_fit(data_piece->fit_data[i],
627  fit_params, x_train, dummy_worker);
628  gsl_vector_set(nodes, i, fit_val);
629  }
630 
631  // Evaluate the empirical interpolant
632  gsl_blas_dgemv(CblasTrans, 1.0, data_piece->ei_basis, nodes, 0.0, *result);
633  gsl_vector_free(nodes);
634 
635  return XLAL_SUCCESS;
636 }
637 
638 
639 /**
640  * Do cubic spline interpolation using a gsl_interp_cspline.
641  * Results differ slightly from scipy.interpolate.InterpolatedUnivariateSpline
642  * due to different boundary conditions.
643  *
644  * gwsurrogate by default will use gsl_interp_cspline, and results should agree
645  * exactly. However, gwsurrogate also has the option to use scipy interpolation,
646  * in which case the waveform values at the start and end can be slightly
647  * different. gwsurrogate will only use the scipy interpolation if for some
648  * reasong the gsl build fails.
649  */
650 static gsl_vector *spline_array_interp(
651  const gsl_vector *xout, /**< The vector of points onto which we want to
652  interpolate. */
653  const gsl_vector *x, /**< The x values of the data to interpolate. */
654  const gsl_vector *y /**< The y values of the data to interpolate. */
655 ) {
656  gsl_interp_accel *acc = gsl_interp_accel_alloc();
657  gsl_spline *spline = gsl_spline_alloc(gsl_interp_cspline, x->size);
658  gsl_spline_init(spline, x->data, y->data, x->size);
659 
660  gsl_vector *res = gsl_vector_alloc(xout->size);
661  REAL8 tmp;
662  for (UINT4 i=0; i<xout->size; i++) {
663  tmp = gsl_spline_eval(spline, gsl_vector_get(xout, i), acc);
664  gsl_vector_set(res, i, tmp);
665  }
666  gsl_spline_free(spline);
667  gsl_interp_accel_free(acc);
668  return res;
669 }
670 
671 /**
672  * Computes omega_22 by forward differences.
673  */
675  const gsl_vector *times, /**< Time array. */
676  const gsl_vector *phi_22, /**< Phase of (2,2) mode. */
677  const int idx /**< Index to compute omega_22 at. */
678 ) {
679  const REAL8 t_next = gsl_vector_get(times, idx+1);
680  const REAL8 t = gsl_vector_get(times, idx);
681  const REAL8 phase_next = gsl_vector_get(phi_22, idx+1);
682  const REAL8 phase = gsl_vector_get(phi_22, idx);
683 
684  // Compute omegaM_22 by forward differences
685  const REAL8 omegaM_22 = (phase_next - phase)/(t_next - t);
686 
687  return omegaM_22;
688 }
689 
690 
691 /**
692  * Finds closest index such that omegaM_22[index] = omegaM_22_val.
693  */
694 static int search_omega_22(
695  int *omega_idx, /**< Output: closest index such that
696  omegaM_22[index] = omegaM_22_val. */
697  const gsl_vector *times, /**< Time array. */
698  const gsl_vector *phi_22, /**< Phase of (2,2) mode. */
699  const REAL8 omegaM_22_val /**< Desired frequency of (2,2) mode. */
700 ) {
701  REAL8 omegaM_22 = -10;
702  REAL8 omegaM_22_prev = -10;
703  int idx = -1;
704  while (omegaM_22 < omegaM_22_val){
705  // save omegaM_22 of previous index and increase index
706  omegaM_22_prev = omegaM_22;
707  idx++;
708 
709  // Compute omegaM_22 by forward differences
710  omegaM_22 = compute_omega_22(times, phi_22, idx);
711 
712  if (gsl_vector_get(times, idx) > 0){
714  "fMin or fRef is larger than the peak frequency=%.7f,"
715  " reduce it. Note that this is in code units, radians/M.",
716  omegaM_22);
717  }
718  }
719 
720  // At this point idx corresponds to first index with
721  // omegaM_22 > omegaM_22_val. If idx-1 is closer to omegaM_22_val,
722  // we pick that instead.
723  if (fabs(omegaM_22_prev - omegaM_22_val) < fabs(omegaM_22-omegaM_22_val)){
724  idx -= 1;
725  }
726 
727  // output idx
728  *omega_idx = idx;
729 
730  return XLAL_SUCCESS;
731 }
732 
733 /**
734  * Evaluate the phase of the (2,2) mode on the sprase surrogate domain.
735  *
736  * The surrogate actually models the phase residual \f$ \phi^{res}_{22} \f$
737  * defined in Eq.(44) of arxiv:1812.07865. Here we first evaluate that and then
738  * add the 0PN TaylorT3 phase to get the (2,2) mode phase.
739  */
741  gsl_vector **phi_22_sparse,/**< Output: (2,2) mode phase on sparse
742  domain.*/
743  const REAL8 eta, /**< Symmetric mass ratio. */
744  const gsl_vector *fit_params, /**< Parameter space point to evaluate the
745  fit at. size=D, the dimension of the model. */
746  gsl_vector *dummy_dp, /**< Dummy vector to store phase evaluation. */
747  const gsl_matrix *x_train, /**< Training set points. */
748  gsl_vector *dummy_worker, /**< Dummy worker array for computations. */
749  const NRHybSurData *NR_hybsur_data /**< Loaded surrogate data. */
750 ){
751 
752  // The first mode in mode_list is always the (2,2) mode
753  const ModeDataPieces *data_pieces = NR_hybsur_data->mode_data_pieces[0];
754 
755  // sanity check
756  if (data_pieces->ell != 2 || data_pieces->m != 2){
757  XLAL_ERROR(XLAL_EINVAL, "Expected first mode to be (2,2)");
758  }
759 
760  // Get phi_22_residual, this is phi_22 with the 0PN phase subtracted.
761  // The 0PN contribution will be added below.
762  int ret = NRHybSur_eval_data_piece(&dummy_dp, fit_params,
763  data_pieces->phase_res_data_piece, x_train, dummy_worker);
764  if (ret != XLAL_SUCCESS) {
765  XLAL_ERROR(XLAL_EFUNC, "Failed to evaluate phase_res_data_piece.");
766  }
767 
768  gsl_vector_memcpy(*phi_22_sparse, dummy_dp);
769 
770 
771  // compute 0PN TaylorT3 phase
772  gsl_vector *phi_22_T3 = gsl_vector_alloc((*phi_22_sparse)->size);
773  gsl_vector_memcpy(phi_22_T3, NR_hybsur_data->TaylorT3_factor_without_eta);
774  gsl_vector_scale(phi_22_T3, 1./pow(eta, 3./8.));
775 
776  // set phi_22_T3 to zero at phaseAlignIdx
777  const int phaseAlignIdx = NR_hybsur_data->phaseAlignIdx;
778  gsl_vector_add_constant(phi_22_T3,
779  -gsl_vector_get(phi_22_T3, phaseAlignIdx));
780 
781  // Add 0PN TaylorT3 phase to get the (2,2) mode phase
782  gsl_vector_add(*phi_22_sparse, phi_22_T3);
783 
784  gsl_vector_free(phi_22_T3);
785 
786  return ret;
787 }
788 
789 /**
790  * Upsamples sparse \f$ \phi_{22} \f$ such that time step size is deltaTOverM,
791  * and initial frequency is roughly omegaM_22_min. The initial frequency will
792  * be refined later on.
793  */
795  gsl_vector **phi_22_dense, /**< Output: Densely sampled (2,2) mode phase.*/
796  gsl_vector **times_dense, /**< Output: Dense time array. */
797  const REAL8 deltaTOverM, /**< Time step in M. */
798  const REAL8 omegaM_22_min, /**< Start frequency of (2,2) mode in rad/M. */
799  const gsl_vector *phi_22_sparse, /**< (2,2) mode phase on sparse domain. */
800  const gsl_vector *domain /**< Sparse surrogate domain. */
801 ){
802 
803  // Sanity checks
804  const REAL8 min_allowed_omegaM_22
805  = compute_omega_22(domain, phi_22_sparse, 0);
806  if (omegaM_22_min < min_allowed_omegaM_22){
808  "fMin=%.7f is lesser than the minimum allowed value=%.7f."
809  " Note that these are in code units, radians/M.",
810  omegaM_22_min, min_allowed_omegaM_22);
811  }
812 
813  // Find init_idx such that omegaM_22 ~ omegaM_22_min, using the
814  // sparse data. This will be refined below.
815  int init_idx;
816  int ret = search_omega_22(&init_idx, domain, phi_22_sparse, omegaM_22_min);
817  if (ret != XLAL_SUCCESS) {
818  XLAL_ERROR(XLAL_EFUNC, "Failed fMin search.\n");
819  }
820 
821  // Go a few indices below to ensure omegaM_22_min is included.
822  // This is sometimes required because the search based on the sparse
823  // phi_22_sparse above can be inaccurate.
824  init_idx -= 5;
825 
826  // But if going out of bounds, just choose the first index.
827  if (init_idx < 0){
828  init_idx = 0;
829  }
830 
831  // Truncated sparse phi_22 and domain
832  gsl_vector *domain_trunc
833  = gsl_vector_alloc(phi_22_sparse->size - init_idx);
834  gsl_vector *phi_22_sparse_trunc = gsl_vector_alloc(domain_trunc->size);
835  for (UINT4 j=0; j < domain_trunc->size; j++) {
836  gsl_vector_set(domain_trunc, j, gsl_vector_get(domain, j+init_idx));
837  gsl_vector_set(phi_22_sparse_trunc, j,
838  gsl_vector_get(phi_22_sparse, j+init_idx));
839  }
840 
841  // initial time (this is temporary, as we will truncate further to refine
842  // initial frequency)
843  const REAL8 t0 = gsl_vector_get(domain_trunc, 0);
844 
845  // final time
846  const REAL8 tf = gsl_vector_get(domain_trunc, domain_trunc->size-1);
847 
848  // Setup times_dense, this a dense time array with the required
849  // step size. Later on, we will exclude frequencies < omegaM_22_min.
850  const int num_times = (int) ceil((tf - t0)/deltaTOverM);
851  *times_dense = gsl_vector_alloc(num_times);
852  for (int j=0; j < num_times; j++) {
853  gsl_vector_set(*times_dense, j, t0 + deltaTOverM*j);
854  }
855 
856  // interpolate phi_22_sparse_trunc onto times_dense
857  *phi_22_dense = spline_array_interp(*times_dense, domain_trunc,
858  phi_22_sparse_trunc);
859 
860  gsl_vector_free(phi_22_sparse_trunc);
861  gsl_vector_free(domain_trunc);
862 
863  return ret;
864 }
865 
866 
867 /**
868  * Evaluate the phase of the (2,2) mode
869  *
870  * The surrogate actually models the phase residual \f$ \phi^{res}_{22} \f$
871  * defined in Eq.(44) of arxiv:1812.07865. Here we first evaluate that and then
872  * add the 0PN TaylorT3 phase to get the (2,2) mode phase.
873  *
874  * Sets the orbital phase to phiRef at the reference frequency omegaM_22_ref.
875  * The orbital phase is obtained as phi_22/2, so this leaves a pi ambiguity.
876  * But the surrogate data is already aligned such that the heavier BH is on
877  * the +ve x-axis at t=-1000M. See Sec.VI.A.4 of arxiv:1812.07865, the resolves
878  * the pi ambiguity. This means that the after the realignment, the orbital
879  * phase at reference frequency omegaM_22_ref is phiRef, or the heavier BH is
880  * at azimuthal angle = phiRef from the +ve x-axis.
881  *
882  * Only uses data at (2,2) mode frequencies >= omegaM_22_min. This determines
883  * the start time. The start time, along with the step size deltaTOverM, is used
884  * to determine the output_times. Uses cubic spline interpolation to
885  * interpolate from the surrogate's time array to output_times.
886  */
888  gsl_vector **phi_22, /**< Output: (2,2) mode phase. */
889  gsl_vector **output_times, /**< Output: Time array. */
890  const REAL8 eta, /**< Symmetric mass ratio. */
891  const gsl_vector *fit_params, /**< Parameter space point to evaluate the fit
892  at. size=D, the dimension of the model. */
893  const REAL8 omegaM_22_min, /**< Start frequency of (2,2) mode in rad/M. */
894  const REAL8 deltaTOverM, /**< Time step in M. */
895  const REAL8 phiRef, /**< Orbital phase at reference frequency. */
896  const REAL8 omegaM_22_ref, /**< Reference freq of (2,2) mode in rad/M. */
897  gsl_vector *dummy_dp, /**< Dummy vector to store phase evaluation. */
898  const gsl_matrix *x_train, /**< Training set points. */
899  gsl_vector *dummy_worker, /**< Dummy worker array for computations. */
900  const NRHybSurData *NR_hybsur_data /**< Loaded surrogate data. */
901 ) {
902 
903  if (omegaM_22_ref + 1e-13 < omegaM_22_min){
904  XLAL_ERROR(XLAL_EINVAL, "fRef cannot be lesser than fMin.");
905  }
906 
907  // Evaluate phi_22 on sparse surrogate domain
908  const gsl_vector *domain = NR_hybsur_data->domain;
909  gsl_vector *phi_22_sparse = gsl_vector_alloc(domain->size);
910  int ret = NRHybSur_eval_phase_22_sparse(&phi_22_sparse, eta, fit_params,
911  dummy_dp, x_train, dummy_worker, NR_hybsur_data);
912  if (ret != XLAL_SUCCESS) {
913  XLAL_ERROR(XLAL_EFUNC, "Failed phi_22 sparse evaluation.\n");
914  }
915 
916  // Upsample to time step deltaTOverM, but restrict to frequencies
917  // approximately >= omegaM_22_min.
918  gsl_vector *phi_22_dense = NULL;
919  gsl_vector *times_dense = NULL;
920  ret = NRHybSur_upsample_phi_22(&phi_22_dense, &times_dense, deltaTOverM,
921  omegaM_22_min, phi_22_sparse, domain);
922  gsl_vector_free(phi_22_sparse);
923  if (ret != XLAL_SUCCESS) {
924  XLAL_ERROR(XLAL_EFUNC, "Failed phi_22 upsampling.\n");
925  }
926 
927  // Now refine the start frequency. Find start_idx such that
928  // omegaM_22 = omegaM_22_min, and drop everyting below
929  int start_idx;
930  ret = search_omega_22(&start_idx, times_dense, phi_22_dense,
931  omegaM_22_min);
932  if (ret != XLAL_SUCCESS) {
933  XLAL_ERROR(XLAL_EFUNC, "Failed fMin search.\n");
934  }
935 
936  *output_times = gsl_vector_alloc(times_dense->size - start_idx);
937  *phi_22 = gsl_vector_alloc((*output_times)->size);
938  for (UINT4 i=0; i < (*output_times)->size; i++){
939  gsl_vector_set(*phi_22, i, gsl_vector_get(phi_22_dense, i+start_idx));
940  gsl_vector_set(*output_times, i,
941  gsl_vector_get(times_dense, i+start_idx));
942  }
943  gsl_vector_free(phi_22_dense);
944  gsl_vector_free(times_dense);
945 
946  // Set reference orbital phase at omegaM_22_ref
947 
948  // If omegaM_22_ref is the same as omegaM_22_min, ref_idx should be 0
949  int ref_idx = 0;
950 
951  // Else, find ref_idx such that omegaM_22[ref_idx] = omegaM_22_ref
952  if (fabs(omegaM_22_ref - omegaM_22_min)/omegaM_22_min > 1e-13){
953  ret = search_omega_22(&ref_idx, *output_times, *phi_22, omegaM_22_ref);
954  if (ret != XLAL_SUCCESS) {
955  XLAL_ERROR(XLAL_EFUNC, "Failed fRef search.\n");
956  }
957  }
958 
959  // set (2,2) phase to 2*phiRef at ref_idx
960  gsl_vector_add_constant(*phi_22,
961  -gsl_vector_get(*phi_22,ref_idx)+2*phiRef);
962 
963  return ret;
964 }
965 
966 
967 /**
968  * Evaluate waveform data pieces of a single mode.
969  *
970  * For (2,2) mode we model the amplitude and phase, but the phase is
971  * evaluated using NRHybSur_eval_phase_22, since it is required for all
972  * modes to transform from the coorbital frame to the inertial frame.
973  * For all other modes we evaluate the real and imaginary parts of the coorbital
974  * frame strain, defined in Eq.(39) of arxiv:1812.07865.
975  *
976  * Only the required data pieces for a given mode will be evaluated.
977  */
979  EvaluatedDataPieces **this_mode_eval_dp, /**< Output: evaluated waveform
980  data pieces of a single mode. */
981  const UINT4 ell, /**< \f$\ell\f$ index of mode. */
982  const UINT4 m, /**< m index of mode. */
983  const ModeDataPieces *data_pieces,/**< Surrogate data pieces of this mode.*/
984  const gsl_vector *output_times, /**< Time vector to evaluate at. */
985  const gsl_vector *fit_params, /**< Parameter space point to evaluate the fit
986  at. size=D, the dimension of the model. */
987  gsl_vector *dummy_dp, /**< Dummy vector to store phase evaluation. */
988  const gsl_matrix *x_train, /**< Training set points. */
989  gsl_vector *dummy_worker, /**< Dummy worker array for computations. */
990  const NRHybSurData *NR_hybsur_data /**< Loaded surrogate data. */
991 ) {
992 
993  int ret;
994  const gsl_vector *domain = NR_hybsur_data->domain;
995  (*this_mode_eval_dp)->ell = ell;
996  (*this_mode_eval_dp)->m = m;
997 
998  if (ell == 2 && m ==2){
999 
1000  // The phase was already evaluated so, only evaluate the
1001  // amplitude
1002  ret = NRHybSur_eval_data_piece(&dummy_dp, fit_params,
1003  data_pieces->ampl_data_piece, x_train, dummy_worker);
1004  if (ret != XLAL_SUCCESS) {
1005  XLAL_ERROR(XLAL_EFUNC, "Failed (2,2) mode amplitude evaluation.\n");
1006  }
1007  (*this_mode_eval_dp)->ampl_eval
1008  = spline_array_interp(output_times, domain, dummy_dp);
1009 
1010  } else {
1011  // For m=0, l=even, the imaginary part is zero, so we only need to
1012  // evaluate the real part. But when m!=0, we still want to evaluate
1013  // the real part.
1014  if (m != 0 || ell % 2 == 0) {
1015 
1016  // evaluate real part of coorbital frame mode
1017  ret = NRHybSur_eval_data_piece(&dummy_dp, fit_params,
1018  data_pieces->coorb_re_data_piece, x_train, dummy_worker);
1019  if (ret != XLAL_SUCCESS) {
1020  XLAL_ERROR(XLAL_EFUNC, "Failed (%u,%u) mode real part evaluation.\n",
1021  ell, m);
1022  }
1023  // interpolate on to output_times
1024  (*this_mode_eval_dp)->coorb_re_eval
1025  = spline_array_interp(output_times, domain, dummy_dp);
1026  }
1027 
1028  // For m=0, l=odd, the imaginary part is zero, so we only need to
1029  // evaluate the imaginary part. But when m!=0, we still want to
1030  // evaluate the imaginary part.
1031  if (m != 0 || ell % 2 == 1) {
1032 
1033  // evaluate imaginary part of coorbital frame mode
1034  ret = NRHybSur_eval_data_piece(&dummy_dp, fit_params,
1035  data_pieces->coorb_im_data_piece, x_train, dummy_worker);
1036  if (ret != XLAL_SUCCESS) {
1037  XLAL_ERROR(XLAL_EFUNC, "Failed (%u,%u) mode imag part evaluation.\n",
1038  ell, m);
1039  }
1040 
1041  // interpolate on to output_times
1042  (*this_mode_eval_dp)->coorb_im_eval
1043  = spline_array_interp(output_times, domain, dummy_dp);
1044  }
1045  }
1046 
1047  return ret;
1048 }
1049 
1050 
1051 /**
1052  * Destroy phi_22 and an EvaluatedDataPieces structure.
1053  *
1054  * Free all associated memory.
1055  */
1057  gsl_vector *phi_22, /**< \f$\phi_{22}\f$ data piece. */
1058  EvaluatedDataPieces **evaluated_mode_dps, /**< All other data pieces. */
1059  const UINT4 num_modes_incl /**< Number of models included. */
1060 ) {
1061 
1062  // The phase of the (2,2) mode is always evaluated, free it.
1063  gsl_vector_free(phi_22);
1064 
1065  // Free all other data pieces
1066  for (UINT4 mode_idx = 0; mode_idx < num_modes_incl; mode_idx++){
1067  EvaluatedDataPieces *this_mode_eval_dp = evaluated_mode_dps[mode_idx];
1068  const UINT4 ell = this_mode_eval_dp->ell;
1069  const UINT4 m = this_mode_eval_dp->m;
1070 
1071  if (ell == 2 && m ==2){
1072  // For (2,2) mode we only have the amplitude, the phase is
1073  // evaluated separately
1074  gsl_vector_free(this_mode_eval_dp->ampl_eval);
1075  } else {
1076  // for m=0, l=odd, the real part is zero and not evaluated. For all
1077  // other cases free the real part
1078  if (m != 0 || ell % 2 == 0) {
1079  gsl_vector_free(this_mode_eval_dp->coorb_re_eval);
1080  }
1081 
1082  // for m=0, l=even, the imaginary part is zero and not evaluated.
1083  // For all other cases free the imaginary part
1084  if (m != 0 || ell % 2 == 1) {
1085  gsl_vector_free(this_mode_eval_dp->coorb_im_eval);
1086  }
1087  }
1088  XLALFree(this_mode_eval_dp);
1089  }
1090  XLALFree(evaluated_mode_dps);
1091 
1092 }
1093 
1094 
1095 /**
1096  * Sanity check (warning only, not error) that the sample rate is high enough
1097  * to capture the ringdown frequencies, by ensuring Nyquist frequency is
1098  * greater than the QNM frequency of the (max_ell,max_ell,0) mode, where
1099  * max_ell is the maximum ell index among the included modes.
1100  */
1102  REAL8 deltaT, /**< Sampling interval (s). */
1103  REAL8 m1, /**< Mass of Bh1 (kg). */
1104  REAL8 m2, /**< Mass of Bh2 (kg). */
1105  REAL8 chi1z, /**< Dimensionless spin of Bh1. */
1106  REAL8 chi2z, /**< Dimensionless spin of Bh2. */
1107  UINT4 max_ell /**< Max ell index included. */
1108 ){
1109  /* Ringdown freq used to check the sample rate */
1110  COMPLEX16Vector modefreqVec;
1111  COMPLEX16 modeFreq;
1112 
1113  /* Before calculating everything else, check sample freq is high enough */
1114  modefreqVec.length = 1;
1115  modefreqVec.data = &modeFreq;
1116 
1117  // m=ell mode should have the highest frequency
1118  UINT4 mode_highest_freqL = max_ell;
1119  UINT4 mode_highest_freqM = max_ell;
1120  UINT4 num_overtones = 1; // One overtone should be good enough for a test
1121  Approximant SpinAlignedEOBapproximant = SEOBNRv4;
1122 
1123  const REAL8 spin1[3] = {0, 0, chi1z};
1124  const REAL8 spin2[3] = {0, 0, chi2z};
1125 
1126  // NOTE: XLALSimIMREOBGenerateQNMFreqV2 expects masses in Solar mass
1127  int ret = XLALSimIMREOBGenerateQNMFreqV2(&modefreqVec, m1/LAL_MSUN_SI,
1128  m2/LAL_MSUN_SI, spin1, spin2, mode_highest_freqL,
1129  mode_highest_freqM, num_overtones, SpinAlignedEOBapproximant);
1130  if (ret != XLAL_SUCCESS) {
1131  XLAL_ERROR(XLAL_EFUNC, "XLALSimIMREOBGenerateQNMFreqV2 failed");
1132  }
1133 
1134  const REAL8 nyquist_freq = 1./2./deltaT;
1135  const REAL8 ringdown_freq = creal(modeFreq)/(2.*LAL_PI);
1136 
1137  if (nyquist_freq < ringdown_freq){
1138  XLAL_PRINT_WARNING("Nyquist frequency=%.7f Hz is lesser than the QNM"
1139  " frequency=%.7f Hz of the (%u,%u,0) mode. Consider reducing time"
1140  " step.", nyquist_freq, ringdown_freq, max_ell, max_ell);
1141  }
1142 
1143  return XLAL_SUCCESS;
1144 }
1145 
1146 
1147 /**
1148  * Activates all modes of an NRHybSur model. For NRHybSur3dq8 that is \f$ \ell
1149  * \leq 4, m \geq 0 \f$, and (5,5), but not (4,1) or (4,0).
1150  */
1152  LALValue *ModeArray, /**< Output: Container for modes. */
1153  const NRHybSurData *NR_hybsur_data /**< Loaded surrogate data. */
1154  )
1155 {
1156  // list of available modes
1157  const gsl_matrix_long *mode_list = NR_hybsur_data->mode_list;
1158  // number of available modes
1159  const UINT4 num_modes_modeled = NR_hybsur_data->num_modes_modeled;
1160  UINT4 ell, m;
1161  for (UINT4 idx = 0; idx < num_modes_modeled; idx++){
1162  ell = gsl_matrix_long_get(mode_list, idx, 0);
1163  m = gsl_matrix_long_get(mode_list, idx, 1);
1164  XLALSimInspiralModeArrayActivateMode(ModeArray, ell, m);
1165  }
1166 }
1167 
1168 /**
1169  * Sanity checks on ModeArray.
1170  *
1171  * Will raise an error if an unavailable mode is requested. Note that we only
1172  * look for m>=0 modes in ModeArray, and will ignore m<0 modes even if present.
1173  * The m<0 modes automatically get added when evaluting the waveform.
1174  */
1176  UINT4 *num_modes_incl, /**< Output: Number of modes to include. */
1177  UINT4 *max_ell, /**< Output: Max ell index included. */
1178  LALValue *ModeArray, /**< Container for modes. */
1179  const NRHybSurData *NR_hybsur_data /**< Loaded surrogate data. */
1180  )
1181 {
1182  INT4 modeAvailable = 0;
1183  UINT4 ell, m;
1184 
1185  // number of available modes
1186  const UINT4 num_modes_modeled = NR_hybsur_data->num_modes_modeled;
1187 
1188  const gsl_matrix_long *mode_list = NR_hybsur_data->mode_list;
1189 
1190  *num_modes_incl = 0;
1191  *max_ell = 2;
1192  for (UINT4 ELL = 2; ELL <= LAL_SIM_L_MAX_MODE_ARRAY; ELL++) {
1193  for (UINT4 EMM = 0; EMM <= ELL; EMM++) {
1194 
1195  modeAvailable = 0;
1196  if (XLALSimInspiralModeArrayIsModeActive(ModeArray,ELL,EMM) == 1) {
1197 
1198  for (UINT4 idx = 0; idx < num_modes_modeled; idx++){
1199 
1200  ell = gsl_matrix_long_get(mode_list, idx, 0);
1201  m = gsl_matrix_long_get(mode_list, idx, 1);
1202 
1203  // Check if the (ELL, EMM) mode is included
1204  if ((ell == ELL) && (m == EMM)) {
1205  modeAvailable=1;
1206  *num_modes_incl += 1;
1207  if (ell > *max_ell) {
1208  *max_ell = ell;
1209  }
1210  }
1211  }
1212 
1213  if (modeAvailable != 1) {
1215  "Mode (%d,%d) is not available.",ELL,EMM);
1216  }
1217  }
1218  }
1219  }
1220 
1221  if (*num_modes_incl == 0) {
1222  XLAL_ERROR(XLAL_EINVAL, "Zero available modes requested.");
1223  }
1224 
1225  return XLAL_SUCCESS;
1226 }
struct tagLALH5File LALH5File
Auxiliary functions for SEOBNRv1/v2 reduced order modeling codes in LALSimIMRSEOBNRv2ROMDoubleSpin....
const char * name
static REAL8 compute_omega_22(const gsl_vector *times, const gsl_vector *phi_22, const int idx)
Computes omega_22 by forward differences.
static REAL8 kernel(const gsl_vector *x1, const gsl_vector *x2, const GPRHyperParams *hyperparams, gsl_vector *dummy_worker)
The GPR Kernel evaluated at a single pair of points.
int NRHybSur_check_mode_array(UINT4 *num_modes_incl, UINT4 *max_ell, LALValue *ModeArray, const NRHybSurData *NR_hybsur_data)
Sanity checks on ModeArray.
int ReadHDF5DoubleDataset(REAL8 *param, LALH5File *sub, const char *name)
Reads a REAL8 value from a H5 file/group.
static REAL8 gp_predict(const gsl_vector *xst, const GPRHyperParams *hyperparams, const gsl_matrix *x_train, gsl_vector *dummy_worker)
Evaluate a GPR fit.
void NRHybSur_set_default_modes(LALValue *ModeArray, const NRHybSurData *NR_hybsur_data)
Activates all modes of an NRHybSur model.
static int NRHybSur_eval_data_piece(gsl_vector **result, const gsl_vector *fit_params, const DataPiece *data_piece, const gsl_matrix *x_train, gsl_vector *dummy_worker)
Evaluate a single NRHybSur waveform data piece.
int ReadHDF5IntDataset(int *param, LALH5File *sub, const char *name)
Reads an INT8 value from a H5 file/group.
static int NRHybSur_eval_phase_22_sparse(gsl_vector **phi_22_sparse, const REAL8 eta, const gsl_vector *fit_params, gsl_vector *dummy_dp, const gsl_matrix *x_train, gsl_vector *dummy_worker, const NRHybSurData *NR_hybsur_data)
Evaluate the phase of the (2,2) mode on the sprase surrogate domain.
int NRHybSur_eval_phase_22(gsl_vector **phi_22, gsl_vector **output_times, const REAL8 eta, const gsl_vector *fit_params, const REAL8 omegaM_22_min, const REAL8 deltaTOverM, const REAL8 phiRef, const REAL8 omegaM_22_ref, gsl_vector *dummy_dp, const gsl_matrix *x_train, gsl_vector *dummy_worker, const NRHybSurData *NR_hybsur_data)
Evaluate the phase of the (2,2) mode.
static int NRHybSur_upsample_phi_22(gsl_vector **phi_22_dense, gsl_vector **times_dense, const REAL8 deltaTOverM, const REAL8 omegaM_22_min, const gsl_vector *phi_22_sparse, const gsl_vector *domain)
Upsamples sparse such that time step size is deltaTOverM, and initial frequency is roughly omegaM_22...
int NRHybSur_eval_mode_data_pieces(EvaluatedDataPieces **this_mode_eval_dp, const UINT4 ell, const UINT4 m, const ModeDataPieces *data_pieces, const gsl_vector *output_times, const gsl_vector *fit_params, gsl_vector *dummy_dp, const gsl_matrix *x_train, gsl_vector *dummy_worker, const NRHybSurData *NR_hybsur_data)
Evaluate waveform data pieces of a single mode.
static int search_omega_22(int *omega_idx, const gsl_vector *times, const gsl_vector *phi_22, const REAL8 omegaM_22_val)
Finds closest index such that omegaM_22[index] = omegaM_22_val.
int NRHybSur_sanity_check_sample_rate(REAL8 deltaT, REAL8 m1, REAL8 m2, REAL8 chi1z, REAL8 chi2z, UINT4 max_ell)
Sanity check (warning only, not error) that the sample rate is high enough to capture the ringdown fr...
REAL8 NRHybSur_eval_fit(const NRHybSurFitData *fit_data, const gsl_vector *fit_params, const gsl_matrix *x_train, gsl_vector *dummy_worker)
Evaluate a NRHybSur fit.
void NRHybSur_DestroyEvaluatedDataPieces(gsl_vector *phi_22, EvaluatedDataPieces **evaluated_mode_dps, const UINT4 num_modes_incl)
Destroy phi_22 and an EvaluatedDataPieces structure.
static gsl_vector * spline_array_interp(const gsl_vector *xout, const gsl_vector *x, const gsl_vector *y)
Do cubic spline interpolation using a gsl_interp_cspline.
int NRHybSur_Init(NRHybSurData *NR_hybsur_data, LALH5File *file)
Initialize a NRHybSurData structure from an open H5 file.
static int NRHybSur_LoadDataPiece(DataPiece **data_piece, LALH5File *file, const char *sub_grp_name)
Loads a single waveform data piece from a H5 file.
static int NRHybSur_LoadSingleModeData(ModeDataPieces **mode_data_pieces, LALH5File *file, const int mode_idx, const gsl_matrix_long *mode_list)
Loads all data pieces of a single waveform mode.
Utilities needed for aligned-spin NR-hybrid surrogate models.
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
sigmaKerr data[0]
REAL8Vector * XLALH5FileReadREAL8Vector(LALH5File *file, const char *name)
INT8Vector * XLALH5FileReadINT8Vector(LALH5File *file, const char *name)
void XLALH5FileClose(LALH5File UNUSED *file)
LALH5File * XLALH5GroupOpen(LALH5File UNUSED *file, const char UNUSED *name)
#define LAL_MSUN_SI
#define LAL_PI
double complex COMPLEX16
double REAL8
uint32_t UINT4
int32_t INT4
void * XLALMalloc(size_t n)
void XLALFree(void *p)
INT4 XLALSimIMREOBGenerateQNMFreqV2(COMPLEX16Vector *modefreqs, const REAL8 mass1, const REAL8 mass2, const REAL8 spin1[3], const REAL8 spin2[3], UINT4 l, INT4 m, UINT4 nmodes, Approximant approximant)
These functions generate the quasinormal mode frequencies for a black hole ringdown.
#define LAL_SIM_L_MAX_MODE_ARRAY
Maximum L spherical harmonic mode that is supported in Mode Array.
Approximant
Enum that specifies the PN approximant to be used in computing the waveform.
@ SEOBNRv4
Spin nonprecessing EOBNR model v4.
int XLALSimInspiralModeArrayIsModeActive(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralModeArrayActivateMode(LALValue *modes, unsigned l, int m)
static const INT4 r
static const INT4 m
void XLALDestroyREAL8Vector(REAL8Vector *vector)
void XLALDestroyINT8Vector(INT8Vector *vector)
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
#define XLAL_PRINT_WARNING(...)
#define XLAL_CHECK_ABORT(assertion)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EDIMS
XLAL_EINVAL
XLAL_FAILURE
list x
list y
COMPLEX16 * data
NRHybSur data for a single waveform data piece.
int n_nodes
Number of empirical nodes.
NRHybSurFitData ** fit_data
NRHybSurFitData at each empirical node.
gsl_matrix * ei_basis
Empirical interpolation matrix.
NRHybSur evaluated data for a single mode.
gsl_vector * coorb_re_eval
Coorbital frame real part evaluation.
gsl_vector * ampl_eval
Amplitude data piece evaluation.
gsl_vector * coorb_im_eval
Coorbital frame imag part evaluation.
Data used in a single GPR fit.
REAL8 constant_value
in kernel.
gsl_vector * alpha
Precomputed .
gsl_vector * length_scale
in kernel.
REAL8 y_train_mean
Mean value before GPR fit, usually zero.
NRHybSur data pieces of a single mode.
DataPiece * phase_res_data_piece
Phase residual data piece.
UINT4 m
Mode m value.
DataPiece * ampl_data_piece
Amplitude data piece.
UINT4 ell
Mode value.
DataPiece * coorb_im_data_piece
Coorbital frame imag part data piece.
DataPiece * coorb_re_data_piece
Coorbital frame real part data piece.
NRHybSur surrogate data for all modes, to be loaded from a h5 file.
ModeDataPieces ** mode_data_pieces
Data pieces of all modes, same order as mode_list.
gsl_vector * domain
Common time samples for the surrogate.
gsl_vector * TaylorT3_factor_without_eta
Precomputed quantity for use in evaluating the 0PN TaylorT3 phase contribution.
REAL8 params_dim
Dimensions of the model.
UINT4 setup
Indicates if NRHybSur has been initialized.
gsl_matrix * x_train
Training set parameters, needed for GPR fits.
int phaseAlignIdx
Alignment index for phase data piece.
UINT4 num_modes_modeled
Number of modeled modes.
gsl_matrix_long * mode_list
List of modeled modes, first element is always (2,2).
Data used in a single NRHybSur fit.
REAL8 data_std
Standard deviation after removing mean.
REAL8 data_mean
Mean of data after linear fit.
GPRHyperParams * hyperparams
Hyperparameters from GPR fit.
gsl_vector * lin_coef
Linear coefs from linear fit, size=D.
REAL8 lin_intercept
Constant intercept from linear fit.
double deltaT
Definition: unicorn.c:24