Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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
47#ifdef LAL_HDF5_ENABLED
48#include <lal/H5FileIO.h>
49#endif
50
51#include <lal/XLALError.h>
52#include <lal/LALConstants.h>
53#include <lal/LALSimIMR.h>
54
57
58
59//*************************************************************************/
60//************************* function definitions **************************/
61//*************************************************************************/
62
63
64
65#ifdef LAL_HDF5_ENABLED
66//********************* H5 wrapper functions ************************/
67
68/**
69 * Reads a REAL8 value from a H5 file/group.
70 */
71int ReadHDF5DoubleDataset(
72 REAL8 *param, /**< Output: REAL8 value from H5 file/group. */
73 LALH5File *sub, /**< H5 file or group. */
74 const char *name /**< Name of REAL8 dataset within file/group. */
75) {
77
78 // Expecting a single double
79 if(data == NULL || data->length != 1) {
82 "Failed to load `%s' scalar dataset\n", name);
83 }
84
85 *param = data->data[0];
87
88 return XLAL_SUCCESS;
89}
90
91/**
92 * Reads an INT8 value from a H5 file/group.
93 */
94int ReadHDF5IntDataset(
95 int *param, /**< Output: int value from H5 file/group. */
96 LALH5File *sub, /**< H5 file or group. */
97 const char *name /**< Name of int dataset within file/group. */
98) {
100
101 // Expecting a single int
102 if(data == NULL || data->length != 1) {
105 "Failed to load `%s' scalar dataset\n", name);
106 }
107
108 *param = (int)data->data[0];
110
111 return XLAL_SUCCESS;
112}
113
114
115//********************* Surrogate loading functions ************************/
116// These should only be called once, when initializing the surrogate
117
118/**
119 * Loads a single waveform data piece from a H5 file.
120 */
121static int NRHybSur_LoadDataPiece(
122 DataPiece **data_piece, /**< Output: Waveform data piece. *data_piece
123 should be NULL. Space will be allocated. */
124 LALH5File *file, /**< Opened HDF5 file. */
125 const char *sub_grp_name /**< H5 group name. */
126) {
127
128 if (data_piece == NULL || *data_piece != NULL) {
129 XLAL_ERROR(XLAL_EFAULT, "*data_piece should be NULL");
130 }
131 if (file == NULL) {
132 XLAL_ERROR(XLAL_EFAULT, "file should not be NULL");
133 }
134
135 // Open h5 group
136 LALH5File *sub;
137 sub = XLALH5GroupOpen(file, sub_grp_name);
138 *data_piece = XLALMalloc(sizeof(DataPiece));
139
140
141 gsl_matrix *ei_basis = NULL;
142
143 int ret = ReadHDF5RealMatrixDataset(sub, "ei_basis", &ei_basis);
144
145 if (ret != XLAL_SUCCESS) {
146 XLAL_ERROR(XLAL_EFUNC, "Failed to load ei_basis.");
147 }
148
149 (*data_piece)->ei_basis = ei_basis;
150
151 // Get number of empirical time nodes
152 int n_nodes;
153 ret = ReadHDF5IntDataset(&n_nodes, sub, "n_nodes");
154 if (ret != XLAL_SUCCESS) {
155 XLAL_ERROR(XLAL_EFUNC, "Failed to load n_nodes.");
156 }
157
158 (*data_piece)->n_nodes = n_nodes;
159 (*data_piece)->fit_data = XLALMalloc( n_nodes * sizeof(NRHybSurFitData *) );
160
161 // Load fit data for each empirical time node
162 const size_t str_size = 20;
163 char *node_name = XLALMalloc(str_size);
164 UNUSED size_t nwritten;
165 for (int i=0; i<n_nodes; i++) {
166
167 nwritten = snprintf(node_name, str_size, "node_num_%d", i);
168 XLAL_CHECK_ABORT(nwritten < str_size);
169 LALH5File *node_function = XLALH5GroupOpen(sub, node_name);
170 NRHybSurFitData *fit_data = XLALMalloc(sizeof(NRHybSurFitData));
171
172 GPRHyperParams *hyperparams = XLALMalloc(sizeof(GPRHyperParams));
173
174 // Load scalars needed for fit
175 ret = ReadHDF5DoubleDataset(&(hyperparams->constant_value),
176 node_function, "constant_value");
177 if (ret != XLAL_SUCCESS) {
178 XLALFree(node_name);
179 XLAL_ERROR(XLAL_EFUNC, "Failed to load constant_value.");
180 }
181
182 ret = ReadHDF5DoubleDataset(&(hyperparams->y_train_mean),
183 node_function, "y_train_mean");
184 if (ret != XLAL_SUCCESS) {
185 XLALFree(node_name);
186 XLAL_ERROR(XLAL_EFUNC, "Failed to load y_train_mean.");
187 }
188
189 ret = ReadHDF5DoubleDataset(&(fit_data->data_mean),
190 node_function, "data_mean");
191 if (ret != XLAL_SUCCESS) {
192 XLALFree(node_name);
193 XLAL_ERROR(XLAL_EFUNC, "Failed to load data_mean.");
194 }
195
196 ret = ReadHDF5DoubleDataset(&(fit_data->data_std),
197 node_function, "data_std");
198 if (ret != XLAL_SUCCESS) {
199 XLALFree(node_name);
200 XLAL_ERROR(XLAL_EFUNC, "Failed to load data_std.");
201 }
202
203 ret = ReadHDF5DoubleDataset(&(fit_data->lin_intercept),
204 node_function, "lin_intercept");
205 if (ret != XLAL_SUCCESS) {
206 XLALFree(node_name);
207 XLAL_ERROR(XLAL_EFUNC, "Failed to load lin_intercept.");
208 }
209
210 // Load arrays needed for fit
211 hyperparams->length_scale = NULL;
212 ret = ReadHDF5RealVectorDataset(node_function, "length_scale",
213 &(hyperparams->length_scale));
214 if (ret != XLAL_SUCCESS) {
215 XLALFree(node_name);
216 XLAL_ERROR(XLAL_EFUNC, "Failed to load length_scale.");
217 }
218
219 hyperparams->alpha = NULL;
220 ret = ReadHDF5RealVectorDataset(node_function, "alpha",
221 &(hyperparams->alpha));
222 if (ret != XLAL_SUCCESS) {
223 XLALFree(node_name);
224 XLAL_ERROR(XLAL_EFUNC, "Failed to load alpha.");
225 }
226
227 fit_data->lin_coef = NULL;
228 ret = ReadHDF5RealVectorDataset(node_function, "lin_coef",
229 &(fit_data->lin_coef));
230 if (ret != XLAL_SUCCESS) {
231 XLALFree(node_name);
232 XLAL_ERROR(XLAL_EFUNC, "Failed to load lin_coef.");
233 }
234
235 XLALH5FileClose(node_function);
236
237 fit_data->hyperparams = hyperparams;
238 (*data_piece)->fit_data[i] = fit_data;
239 }
240
241 XLALFree(node_name);
242 XLALH5FileClose(sub);
243
244 return ret;
245}
246
247/**
248 * Loads all data pieces of a single waveform mode.
249 */
250static int NRHybSur_LoadSingleModeData(
251 ModeDataPieces **mode_data_pieces, /**< Output: Waveform data pieces of a
252 given mode. Space will be allocated to
253 **mode_data_pieces. */
254 LALH5File *file, /**< Opened HDF5 file. */
255 const int mode_idx, /**< Index corresponding to the mode. */
256 const gsl_matrix_long *mode_list /**< List of all modes. */
257) {
258
259 if (mode_data_pieces == NULL) {
260 XLAL_ERROR(XLAL_EFAULT, "mode_data_pieces should not be NULL");
261 }
262 if (file == NULL) {
263 XLAL_ERROR(XLAL_EFAULT, "file should not be NULL");
264 }
265
266 int ret = 0;
267 const UINT4 ell = gsl_matrix_long_get(mode_list, mode_idx, 0);
268 const UINT4 m = gsl_matrix_long_get(mode_list, mode_idx, 1);
269
270 ModeDataPieces *data_pieces
271 = XLALMalloc(sizeof(*mode_data_pieces[mode_idx]));
272
273 data_pieces->ell = ell;
274 data_pieces->m = m;
275
276 // At most two of these data pieces are needed for each mode,
277 // so let's set them to NULL by default. This will also raise
278 // an error if the wrong data piece is used for a mode
279 data_pieces->ampl_data_piece = NULL;
280 data_pieces->phase_res_data_piece = NULL;
281 data_pieces->coorb_re_data_piece = NULL;
282 data_pieces->coorb_im_data_piece = NULL;
283
284 const size_t str_size = 20;
285 char *sub_grp_name = XLALMalloc(str_size);
286
287 UNUSED size_t nwritten;
288 if (ell == 2 && m ==2){
289
290 // Amplitude of 22 mode
291 nwritten = snprintf(sub_grp_name, str_size, "l%u_m%u/amp", ell, m);
292 XLAL_CHECK_ABORT(nwritten < str_size);
293 ret = NRHybSur_LoadDataPiece(&(data_pieces)->ampl_data_piece,
294 file, sub_grp_name);
295 if(ret != XLAL_SUCCESS) {
296 XLALFree(sub_grp_name);
298 "Failed to load `%s' data piece", sub_grp_name);
299 }
300
301 // phase of 22 mode
302 nwritten = snprintf(sub_grp_name, str_size, "l%u_m%u/phase", ell, m);
303 XLAL_CHECK_ABORT(nwritten < str_size);
304 ret = NRHybSur_LoadDataPiece(&(data_pieces)->phase_res_data_piece,
305 file, sub_grp_name);
306 if(ret != XLAL_SUCCESS) {
307 XLALFree(sub_grp_name);
309 "Failed to load `%s' data piece", sub_grp_name);
310 }
311
312 } else {
313 // For m=0, l=even, the imaginary part is zero, so we only need to
314 // load the real part. But when m!=0, we still want to load the real
315 // part.
316 if (m != 0 || ell % 2 == 0) {
317 // Real part of coorbital frame mode
318 nwritten = snprintf(sub_grp_name, str_size, "l%u_m%u/re", ell, m);
319 XLAL_CHECK_ABORT(nwritten < str_size);
320 ret = NRHybSur_LoadDataPiece(&(data_pieces)->coorb_re_data_piece,
321 file, sub_grp_name);
322 if(ret != XLAL_SUCCESS) {
323 XLALFree(sub_grp_name);
325 "Failed to load `%s' data piece", sub_grp_name);
326 }
327 }
328
329 // Similarly, for m=0, l=odd, the real part is zero, so we only need
330 // to load the imaginary part. But when m!=0, we still want to load
331 // the imaginary part.
332 if (m != 0 || ell % 2 == 1) {
333 // Imaginary part of coorbital frame mode
334 nwritten = snprintf(sub_grp_name, str_size, "l%u_m%u/im", ell, m);
335 XLAL_CHECK_ABORT(nwritten < str_size);
336 ret = NRHybSur_LoadDataPiece(&(data_pieces)->coorb_im_data_piece,
337 file, sub_grp_name);
338 if(ret != XLAL_SUCCESS) {
339 XLALFree(sub_grp_name);
341 "Failed to load `%s' data piece", sub_grp_name);
342 }
343 }
344 }
345
346 mode_data_pieces[mode_idx] = data_pieces;
347 XLALFree(sub_grp_name);
348 return ret;
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 */
403int NRHybSur_Init(
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#endif
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 */
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
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 */
650static 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 */
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 = 0;
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.
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.
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.
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_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.
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 y
x
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