Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-8a6b96f
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
LALSimIMRSEOBNRv5HMROM.c
Go to the documentation of this file.
1
2/*
3 * Copyright (C) 2022 Lorenzo Pompili
4 * Copyright (C) 2019 Michael Puerrer, John Veitch, Roberto Cotesta, Sylvain Marsat
5 * Copyright (C) 2014, 2015, 2016 Michael Puerrer, John Veitch
6 * Reduced Order Model for SEOBNR
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with with program; see the file COPYING. If not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301 USA
22 */
23
24#ifdef __GNUC__
25#define UNUSED __attribute__ ((unused))
26#else
27#define UNUSED
28#endif
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <math.h>
33#include <complex.h>
34#include <time.h>
35#include <stdbool.h>
36#include <alloca.h>
37#include <string.h>
38#include <libgen.h>
39
40
41#include <gsl/gsl_errno.h>
42#include <gsl/gsl_bspline.h>
43#include <gsl/gsl_blas.h>
44#include <gsl/gsl_min.h>
45#include <gsl/gsl_spline.h>
46#include <gsl/gsl_poly.h>
47#include <lal/Units.h>
48#include <lal/SeqFactories.h>
49#include <lal/LALConstants.h>
50#include <lal/XLALError.h>
51#include <lal/FrequencySeries.h>
52#include <lal/Date.h>
53#include <lal/StringInput.h>
54#include <lal/Sequence.h>
55#include <lal/LALStdio.h>
56#include <lal/FileIO.h>
57#include <lal/LALSimSphHarmMode.h>
58#include <lal/LALDatatypes.h>
59#include <lal/LALSimInspiral.h>
60#include <lal/AVFactories.h>
61#include <lal/SphericalHarmonics.h>
63
64#define NMODES 7
65#define f_hyb_ini 0.003
66#define f_hyb_end 0.004
67
68#ifdef LAL_HDF5_ENABLED
69#include <lal/H5FileIO.h>
70static const char ROMDataHDF5[] = "SEOBNRv5HMROM_v1.0.hdf5";
71static const char ROM22DataHDF5[] = "SEOBNRv5ROM_v1.0.hdf5";
72#endif
73
74#include <lal/LALSimInspiral.h>
75#include <lal/LALSimIMR.h>
76
79
80#include <lal/LALConfig.h>
81#ifdef LAL_PTHREAD_LOCK
82#include <pthread.h>
83#endif
84
85
86
87#ifdef LAL_PTHREAD_LOCK
88static pthread_once_t SEOBNRv5HMROM_is_initialized = PTHREAD_ONCE_INIT;
89static pthread_once_t SEOBNRv5ROM_is_initialized = PTHREAD_ONCE_INIT;
90#endif
91
92
93/*************** mode array definitions ******************/
94
95const char mode_array_v5hm[NMODES][3] =
96{"22",
97"33",
98"21",
99"44",
100"55",
101"32",
102"43"};
103
104const UINT4 lmModes_v5hm[NMODES][2] = {{2, 2}, {3, 3}, {2, 1}, {4, 4}, {5, 5}, {3, 2}, {4, 3}};
105
106/*************** constant phase shifts ******************/
107
108// const REAL8 const_phaseshift_lm_v5hm[NMODES] = {0.,-LAL_PI/2.,LAL_PI/2.,LAL_PI,LAL_PI/2.,0.,-LAL_PI/2.};
109const REAL8 const_phaseshift_lm_v5hm[NMODES] = {0.,0.,0.,0.,0.,0.,0.};
110
111/* This constant phaseshift is coming from the relations between the phase
112* of the lm mode phi_lm and the orbital phase phi. At leading order phi_lm = -m*phi + c_lm
113* where c_lm are these constant phaseshift. They can be derived from the PN expression of the modes
114* see Blanchet, L. Living Rev. Relativ. (2014) 17: 2. https://doi.org/10.12942/lrr-2014-2 Eqs(327-329)
115*/
116
117/*************** constant factors used to define fmax for each mode ******************/
118
119const REAL8 const_fmax_lm_v5hm[NMODES] = {1.7, 1.55, 1.7, 1.35, 1.25, 1.7, 1.7};
120
121/*************** starting geometric frequency 22 mode non-hybridized ROM ***********************/
122
123#define Mf_low_22 0.0004925491025543576
124#define Mf_low_33 (Mf_low_22 * (3.0/2.0))
125#define Mf_low_21 (Mf_low_22 * (1.0/2.0))
126#define Mf_low_44 (Mf_low_22 * (4.0/2.0))
127#define Mf_low_55 (Mf_low_22 * (5.0/2.0))
128#define Mf_low_32 (Mf_low_22)
129#define Mf_low_43 (Mf_low_22 * (3.0/2.0))
131
132/*************** type definitions ******************/
133
134typedef struct tagSEOBNRROMdataDS_coeff
135{
136 gsl_vector* c_real;
137 gsl_vector* c_imag;
138 gsl_vector* c_phase;
140
142{
143 gsl_vector* cvec_real; // Flattened Re(c-mode) projection coefficients
144 gsl_vector* cvec_imag; // Flattened Im(c-mode) projection coefficients
145 gsl_vector* cvec_phase; // Flattened orbital phase projection coefficients
146 gsl_matrix *Breal; // Reduced SVD basis for Re(c-mode)
147 gsl_matrix *Bimag; // Reduced SVD basis for Im(c-mode)
148 gsl_matrix *Bphase; // Reduced SVD basis for orbital phase
149 int nk_cmode; // Number frequency points for c-mode
150 int nk_phase; // Number of frequency points for orbital phase
151 gsl_vector *gCMode; // Sparse frequency points for c-mode
152 gsl_vector *gPhase; // Sparse frequency points for orbital phase
153 gsl_vector *qvec; // B-spline knots in q
154 gsl_vector *chi1vec; // B-spline knots in chi1
155 gsl_vector *chi2vec; // B-spline knots in chi2
156 int ncx, ncy, ncz; // Number of points in q, chi1, chi2
157 double q_bounds[2]; // [q_min, q_max]
158 double chi1_bounds[2]; // [chi1_min, chi1_max]
159 double chi2_bounds[2]; // [chi2_min, chi2_max]
160};
161typedef struct tagSEOBNRROMdataDS_submodel SEOBNRROMdataDS_submodel;
162
164{
165 UINT4 setup;
166 SEOBNRROMdataDS_submodel* highf;
167 SEOBNRROMdataDS_submodel* lowf;
168};
169typedef struct tagSEOBNRROMdataDS SEOBNRROMdataDS;
170
171static SEOBNRROMdataDS __lalsim_SEOBNRv5HMROMDS_data[NMODES];
172static SEOBNRROMdataDS __lalsim_SEOBNRv5ROMDS_data[1];
173
174typedef int (*load_dataPtr)(const char*, gsl_vector *, gsl_vector *, gsl_matrix *, gsl_matrix *, gsl_vector *);
175
176typedef struct tagSplineData
177{
178 gsl_bspline_workspace *bwx;
179 gsl_bspline_workspace *bwy;
180 gsl_bspline_workspace *bwz;
181} SplineData;
182
183typedef struct tagAmpPhaseSplineData
184{
185 gsl_spline *spline_amp;
186 gsl_spline *spline_phi;
187 gsl_interp_accel *acc_amp;
188 gsl_interp_accel *acc_phi;
189 gsl_vector *f;
191
192/**************** Internal functions **********************/
193
194UNUSED static bool SEOBNRv5HMROM_IsSetup(UINT4, SEOBNRROMdataDS *romdataset);
195UNUSED static void SEOBNRv5HMROM_Init_LALDATA(void);
196UNUSED static void SEOBNRv5ROM_Init_LALDATA(void);
197UNUSED static int SEOBNRv5HMROM_Init(const char dir[],UINT4, bool, SEOBNRROMdataDS *romdataset);
198UNUSED static int SEOBNRROMdataDS_Init(SEOBNRROMdataDS *romdata, const char dir[], UINT4, bool);
199UNUSED static void SEOBNRROMdataDS_Cleanup(SEOBNRROMdataDS *romdata);
200
202 UNUSED SEOBNRROMdataDS_submodel **submodel,
203 UNUSED const char dir[],
204 UNUSED const char grp_name[],
205 UNUSED UINT4 index_mode,
206 UNUSED bool use_hm
207);
208UNUSED static void SEOBNRROMdataDS_Cleanup_submodel(SEOBNRROMdataDS_submodel *submodel);
209UNUSED static void SplineData_Destroy(SplineData *splinedata);
210UNUSED static void SplineData_Init(
211 SplineData **splinedata,
212 int ncx, // Number of points in q + 2
213 int ncy, // Number of points in chi1 + 2
214 int ncz, // Number of points in chi2 + 2
215 const double *qvec, // B-spline knots in q
216 const double *chi1vec, // B-spline knots in chi1
217 const double *chi2vec // B-spline knots in chi2
218);
219UNUSED static void AmpPhaseSplineData_Init(
221 const int num_modes
222);
223UNUSED static void AmpPhaseSplineData_Destroy(AmpPhaseSplineData **data, const int num_modes);
224
225UNUSED static int TaylorF2Amplitude(
226 const double q, // Mass-ration m1/m2 >= 1
227 const double chi1, // Dimensionless aligned spin of body 1
228 const double chi2, // Dimensionless aligned spin of body 2
229 const int l, // First mode number l
230 const int m, // Second mode number m
231 gsl_vector *Mfs, // Input geometric frequencies
232 gsl_vector **PNamp // Output: TaylorF2 amplitude at frequencies Mfs
233);
234
235UNUSED static int TaylorF2Phasing(
236 double Mtot, // Total mass in solar masses
237 double q, // Mass-ration m1/m2 >= 1
238 double chi1, // Dimensionless aligned spin of body 1
239 double chi2, // Dimensionless aligned spin of body 2
240 int l, // First mode number l
241 int m, // Second mode number m
242 gsl_vector *Mfs, // Input geometric frequencies
243 gsl_vector **PNphase // Output: TaylorF2 phase at frequencies Mfs
244);
245
246UNUSED static double GetDeltaphilm(int l, int m);
247UNUSED static double GetModeAmpFactor(double eta, double delta, double chi1, double chi2, int l, int m, double v);
248
249/* Build geom frequency grid suitable for waveform amp/phase interpolation */
250UNUSED static int BuildInspiralGeomFrequencyGrid(
251 gsl_vector** Mfreq, /* Output: pointer to real vector */
252 const double Mfmin, /* Starting geometric frequency */
253 const double Mfmax, /* Ending geometric frequency */
254 const double q, /* Mass ratio */
255 const double acc); /* Desired phase interpolation error for inspiral */
256
257/**
258 * Core function for computing the ROM waveform.
259 * Interpolate projection coefficient data and evaluate coefficients at desired (q, chi).
260 * Construct 1D splines for amplitude and phase.
261 * Compute strain waveform from amplitude and phase.
262*/
263UNUSED static int SEOBNRv5HMROMCoreModes(
264 SphHarmFrequencySeries **hlm_list, /**< Spherical modes frequency series for the waveform */
265 REAL8 phiRef, /**< Orbital phase at reference time */
266 REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
267 REAL8 distance, /**< Distance of source (m) */
268 REAL8 Mtot_sec, /**< Total mass in seconds **/
269 REAL8 q, /**< Mass ratio **/
270 REAL8 chi1, /**< Dimensionless aligned component spin 1 */
271 REAL8 chi2, /**< Dimensionless aligned component spin 2 */
272 const REAL8Sequence *freqs, /**< Frequency points at which to evaluate the waveform (Hz) */
273 REAL8 deltaF,
274 /**< If deltaF > 0, the frequency points given in freqs are uniformly spaced with
275 * spacing deltaF. Otherwise, the frequency points are spaced non-uniformly.
276 * Then we will use deltaF = 0 to create the frequency series we return. */
277 INT4 nk_max, /**< truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
278 UINT4 nModes, /**< Number of modes to generate */
279 REAL8 sign_odd_modes, /**< Sign of the odd-m modes, used when swapping the two bodies */
280 SEOBNRROMdataDS *romdataset /**< Dataset for the 22 or HM ROM */
281);
282UNUSED static void SEOBNRROMdataDS_coeff_Init(SEOBNRROMdataDS_coeff **romdatacoeff, int nk_cmode, int nk_phase);
283UNUSED static void SEOBNRROMdataDS_coeff_Cleanup(SEOBNRROMdataDS_coeff *romdatacoeff);
284
285static size_t NextPow2(const size_t n);
286static UINT8 Setup_EOBROM__std_mode_array_structure(LALValue *ModeArray, UINT4 nModes);
287static INT8 Check_EOBROM_mode_array_structure(LALValue *ModeArray,UINT4 nModes);
290 *hplusFS, /**<< Output: frequency series for hplus, already created */
292 *hcrossFS, /**<< Output: frequency series for hplus, already created */
293 LALValue *ModeArray, /**<< Input: ModeArray structure with the modes to include */
295 *hlm, /**<< Input: list with frequency series for each mode hlm */
296 REAL8 inc, /**<< Input: inclination */
297 REAL8 phi /**<< Input: phase */
298);
299
301 REAL8 q, // Input: eta-value for which projection coefficients should be evaluated
302 REAL8 chi1, // Input: chi1-value for which projection coefficients should be evaluated
303 REAL8 chi2, // Input: chi2-value for which projection coefficients should be evaluated
304 gsl_vector *cvec, // Input: data for spline coefficients
305 int nk, // number of SVD-modes == number of basis functions
306 int nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
307 int ncx, // Number of points in eta + 2
308 int ncy, // Number of points in chi1 + 2
309 int ncz, // Number of points in chi2 + 2
310 const double *qvec, // B-spline knots in eta
311 const double *chi1vec, // B-spline knots in chi1
312 const double *chi2vec, // B-spline knots in chi2
313 gsl_vector *c_out // Output: interpolated projection coefficients
314);
315UNUSED static UINT8 SEOBNRv5HMROM_Select_HF_patch(REAL8 q, REAL8 chi1);
316UNUSED static UINT8 SEOBNRv5HMROM_phase_sparse_grid(gsl_vector* phase_f, REAL8 q, REAL8 chi1,REAL8 chi2, const char* freq_range, INT4 nk_max, SEOBNRROMdataDS *romdataset);
317UNUSED static UINT8 SEOBNRv5HMROM_cmode_sparse_grid(gsl_vector* creal_f, gsl_vector* cimag_f, REAL8 q, REAL8 chi1,REAL8 chi2, const char* freq_range, UINT8 nMode,INT4 nk_max,SEOBNRROMdataDS *romdataset);
318UNUSED static UINT8 SEOBNRv5HMROM_phase_sparse_grid_hybrid(gsl_vector* freq, gsl_vector* phase, gsl_vector* freq_lo, gsl_vector* freq_hi, REAL8 q, REAL8 chi1,REAL8 chi2,INT4 nk_max, SEOBNRROMdataDS *romdataset);
319UNUSED static UINT8 SEOBNRv5HMROM_cmode_sparse_grid_hybrid(gsl_vector* freq, gsl_vector* cmode_real, gsl_vector* cmode_imag, gsl_vector* freq_lo, gsl_vector* freq_hi, REAL8 q, REAL8 chi1,REAL8 chi2, UINT8 nMode,INT8 nk_max,SEOBNRROMdataDS *romdataset);
320UNUSED static UINT8 SEOBNRv5HMROM_freq_phase_sparse_grid_hybrid(UNUSED gsl_vector** freq, UNUSED gsl_vector** phase, REAL8 q, REAL8 chi1,REAL8 chi2,INT4 nk_max,SEOBNRROMdataDS *romdataset);
321UNUSED static UINT8 SEOBNRv5HMROM_freq_cmode_sparse_grid_hybrid(gsl_vector** freq_cmode, gsl_vector** cmode_real, gsl_vector** cmode_imag, REAL8 q, REAL8 chi1,REAL8 chi2, UINT8 nMode,INT8 nk_max, SEOBNRROMdataDS *romdataset);
322UNUSED static UINT8 SEOBNRv5HMROM_approx_phi_lm(gsl_vector* freq_mode_lm, gsl_vector* phase_approx_lm, gsl_vector* freq_carrier_hyb, gsl_vector* phase_carrier_hyb, UINT4 nMode);
323
325 gsl_vector **freq, // Output: hybrid frequency array
326 gsl_vector **phase, // Output: hybrid phase
327 REAL8* Deltat_align, // Output: time alignment
328 REAL8* Deltaphi_align, // Output: phase alignment
329 gsl_vector *phase_lo, // low frequency phase
330 gsl_vector *phase_hi, // high frequency phase
331 gsl_vector *freq_lo, // frequency array for low frequency phase
332 gsl_vector *freq_hi, // frequency array for high frequency phase
333 double f_hyb_lo, // start frequency of hybridization window
334 double f_hyb_hi // end frequency of hybridization window
335);
336
338 gsl_vector **freq, // Output: hybrid frequency array
339 gsl_vector **phase, // Output: hybrid phase
340 gsl_vector *phase_lo, // low frequency phase
341 gsl_vector *phase_hi, // high frequency phase
342 gsl_vector *freq_lo, // frequency array for low frequency phase
343 gsl_vector *freq_hi, // frequency array for high frequency phase
344 double f_hyb_lo, // start frequency of hybridization window
345 double f_hyb_hi, // end frequency of hybridization window
346 REAL8 Deltat_22_align, // Input: 22 time alignment
347 REAL8 Deltaphi_22_align, // Input: 22 phase alignment
348 INT4 modeM // Input: m mode number
349);
350
352 gsl_vector **freq, // Output: hybrid frequency array
353 gsl_vector **amp, // Output: hybrid amplitude
354 gsl_vector *amp_lo, // low frequency amplitude
355 gsl_vector *amp_hi, // high frequency amplitude
356 gsl_vector *freq_lo, // frequency array for low frequency amplitude
357 gsl_vector *freq_hi, // frequency array for high frequency amplitude
358 double f_hyb_lo, // start frequency of hybridization window
359 double f_hyb_hi // end frequency of hybridization window
360);
361
362UNUSED static int hybridize_ROM_with_PN_amplitude(
363 gsl_spline **hyb_spline, /**< Output: spline */
364 AmpPhaseSplineData *ampPhaseSplineData_for_mode, /**< ROM spline data */
365 gsl_vector *PN_freq, /**< PN frequency */
366 gsl_vector *PN_amp, /**< PN amplitude */
367 double f_hyb_win_lo, /**< hybridization window start */
368 double f_hyb_win_hi /**< hybridization window end */
369);
370
372 gsl_spline **hyb_spline, /**< Output: spline */
373 REAL8* Deltat_align, /**< Output: time alignment */
374 REAL8* Deltaphi_align, /**< Output: phase alignment */
375 AmpPhaseSplineData *ampPhaseSplineData_for_mode, /**< ROM spline data */
376 gsl_vector *PN_freq, /**< PN frequency */
377 gsl_vector *PN_phase, /**< PN phase */
378 double f_hyb_win_lo, /**< hybridization window start */
379 double f_hyb_win_hi /**< hybridization window end */
380);
381
383 gsl_spline **hyb_spline, /**< Output: spline */
384 AmpPhaseSplineData *ampPhaseSplineData_for_mode, /**< ROM spline data */
385 gsl_vector *PN_freq, /**< PN frequency */
386 gsl_vector *PN_phase, /**< PN phase */
387 double f_hyb_win_lo, /**< hybridization window start */
388 double f_hyb_win_hi, /**< hybridization window end */
389 REAL8 Deltat_22_align, /**< Input: 22 time alignment */
390 REAL8 Deltaphi_22_align, /**< Input: 22 phase alignment */
391 INT4 modeM /**< Input: m mode number */
392);
393
395 UNUSED AmpPhaseSplineData **ampPhaseSplineData, /**<< Output: amplitude and phase splines for the modes */
396 UNUSED REAL8 q, /**< Mass ratio **/
397 UNUSED REAL8 chi1, /**< Dimensionless aligned component spin 1 */
398 UNUSED REAL8 chi2, /**< Dimensionless aligned component spin 2 */
399 UNUSED INT4 nk_max, /**< truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 (default). */
400 UNUSED UINT4 nModes, /**< Number of modes to generate */
401 UNUSED SEOBNRROMdataDS *romdataset /**< Dataset for the 22 or HM ROM */
402);
403
404UNUSED static int SEOBNRv5HMROMCoreModesHybridized(
405 UNUSED SphHarmFrequencySeries **hlm_list, /**<< Output: Spherical modes frequency series for the waveform */
406 UNUSED REAL8 phiRef, /**<< orbital reference phase */
407 UNUSED REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
408 UNUSED REAL8 distance, /**< Distance of source (m) */
409 UNUSED REAL8 Mtot_sec, /**< Total mass in seconds **/
410 UNUSED REAL8 q, /**< Mass ratio **/
411 UNUSED REAL8 chi1, /**< Dimensionless aligned component spin 1 */
412 UNUSED REAL8 chi2, /**< Dimensionless aligned component spin 2 */
413 UNUSED const REAL8Sequence *freqs_in, /**< Frequency points at which to evaluate the waveform (Hz) */
414 UNUSED REAL8 deltaF, /**< Frequency points at which to evaluate the waveform (Hz) */
415 UNUSED INT4 nk_max, /**< truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1. We
416 *nk_max == -1 is the default setting */
417 UNUSED UINT4 nModes, /**< Number of modes to generate */
418 REAL8 sign_odd_modes, /**< Sign of the odd-m modes, used when swapping the two bodies */
419 UNUSED SEOBNRROMdataDS *romdataset, /**< Dataset for the 22 or HM ROM */
420 LALDict *LALParams, /**< For other relevant parameters e.g. tidal deformaiblity*/
421 NRTidal_version_type NRTidal_version /**< NRTidal version; only NRTidalv3_V or NoNRT_V in case of BBH baseline*/
422);
423
424UNUSED static int SEOBNRv5ROMTimeFrequencySetup(
425 gsl_spline **spline_phi, // phase spline
426 gsl_interp_accel **acc_phi, // phase spline accelerator
427 REAL8 *Mf_final, // ringdown frequency in Mf
428 REAL8 *Mtot_sec, // total mass in seconds
429 REAL8 m1SI, // Mass of companion 1 (kg)
430 REAL8 m2SI, // Mass of companion 2 (kg)
431 REAL8 chi1, // Aligned spin of companion 1
432 REAL8 chi2, // Aligned spin of companion 2
433 REAL8 Mf_start, // Starting geometric frequency
434 REAL8 *Mf_ROM_min, // Lowest geometric frequency for ROM
435 REAL8 *Mf_ROM_max // Highest geometric frequency for ROM
436);
437
438UNUSED static int spline_to_gsl_vectors(gsl_spline *s, gsl_vector **x, gsl_vector **y);
439
440/********************* Definitions begin here ********************/
441
442
443UNUSED static int spline_to_gsl_vectors(gsl_spline *s, gsl_vector **x, gsl_vector **y) {
444 if ((*x) || (*y))
446
447 *x = gsl_vector_alloc(s->size);
448 *y = gsl_vector_alloc(s->size);
449 for (size_t i=0; i < s->size; i++) {
450 gsl_vector_set(*x, i, s->x[i]);
451 gsl_vector_set(*y, i, s->y[i]);
452 }
453
454 return(XLAL_SUCCESS);
455}
456
457
458/** Setup SEOBNRv5HMROM model using data files installed in $LAL_DATA_PATH
459 */
460UNUSED static void SEOBNRv5HMROM_Init_LALDATA(void)
461{
462 // Loop over all the available modes and check if they have already been initialized
463 for(int i = 0; i < NMODES; i++) {
465 }
466
467 // Expect ROM datafile in a directory listed in LAL_DATA_PATH,
468#ifdef LAL_HDF5_ENABLED
469#define datafile ROMDataHDF5
470 char *path = XLAL_FILE_RESOLVE_PATH(datafile);
471 if (path==NULL){
473 "Unable to resolve data file '%s' in $LAL_DATA_PATH.\n"
474 "Note: LALSuite versions >= 7.25 require data files that are publicly available at:\n"
475 "https://git.ligo.org/waveforms/software/lalsuite-waveform-data\n"
476 "and on Zenodo at: https://zenodo.org/records/14999310.\n"
477 "For earlier LALSuite versions, use the files in lalsuite-extra, available at:\n"
478 "https://git.ligo.org/lscsoft/lalsuite-extra\n",
479 datafile);
480 }
481 char *dir = dirname(path);
482
483 UINT4 ret = XLAL_SUCCESS;
484 bool use_hm = true;
485 for(int i = 0; i < NMODES; i++) {
487 if(ret != XLAL_SUCCESS)
488 XLAL_ERROR_VOID(XLAL_FAILURE, "Unable to find SEOBNRv5HMROM data \
489 files in $LAL_DATA_PATH for the mode = %d\n", i);
490 }
491 XLALFree(path);
492#else
493 XLAL_ERROR_VOID(XLAL_EFAILED, "SEOBNRv5HMROM requires HDF5 support which is not enabled\n");
494#endif
495}
496
497/** Setup SEOBNRv5ROM model using data files installed in $LAL_DATA_PATH
498 */
499UNUSED static void SEOBNRv5ROM_Init_LALDATA(void)
500{
501 // Loop over all the available modes and check if they have already been initialized
502 for(int i = 0; i < 1; i++) {
504 }
505
506 // Expect ROM datafile in a directory listed in LAL_DATA_PATH,
507#ifdef LAL_HDF5_ENABLED
508#define datafile22 ROM22DataHDF5
509 char *path = XLAL_FILE_RESOLVE_PATH(datafile22);
510 if (path==NULL){
511 XLAL_ERROR_VOID(XLAL_EIO, "Unable to resolve data file %s in $LAL_DATA_PATH\n", datafile22);
512 }
513 char *dir = dirname(path);
514
515 UINT4 ret = XLAL_SUCCESS;
516 bool use_hm = false;
517 for(int i = 0; i < 1; i++) {
519 if(ret != XLAL_SUCCESS)
520 XLAL_ERROR_VOID(XLAL_FAILURE, "Unable to find SEOBNRv5ROM data \
521 files in $LAL_DATA_PATH for the mode = %d\n", i);
522 }
523 XLALFree(path);
524#else
525 XLAL_ERROR_VOID(XLAL_EFAILED, "SEOBNRv5ROM requires HDF5 support which is not enabled\n");
526#endif
527}
528
529/** Helper function to check if the SEOBNRv5HMROM model has been initialised */
530static bool SEOBNRv5HMROM_IsSetup(UINT4 index_mode, SEOBNRROMdataDS *romdataset) {
531 if(romdataset[index_mode].setup){
532 return true;
533 }
534 else{
535 return false;
536 }
537}
538
539// Setup B-spline basis functions for given points
540static void SplineData_Init(
541 SplineData **splinedata,
542 int ncx, // Number of points in q + 2
543 int ncy, // Number of points in chi1 + 2
544 int ncz, // Number of points in chi2 + 2
545 const double *qvec, // B-spline knots in q
546 const double *chi1vec, // B-spline knots in chi1
547 const double *chi2vec // B-spline knots in chi2
548)
549{
550 if(!splinedata) exit(1);
551 if(*splinedata) SplineData_Destroy(*splinedata);
552
553 (*splinedata)=XLALCalloc(1,sizeof(SplineData));
554
555 // Set up B-spline basis for desired knots
556 const size_t nbreak_x = ncx-2; // must have nbreak = n-2 for cubic splines
557 const size_t nbreak_y = ncy-2; // must have nbreak = n-2 for cubic splines
558 const size_t nbreak_z = ncz-2; // must have nbreak = n-2 for cubic splines
559
560 // Allocate a cubic bspline workspace (k = 4)
561 gsl_bspline_workspace *bwx = gsl_bspline_alloc(4, nbreak_x);
562 gsl_bspline_workspace *bwy = gsl_bspline_alloc(4, nbreak_y);
563 gsl_bspline_workspace *bwz = gsl_bspline_alloc(4, nbreak_z);
564
565 // Set breakpoints (and thus knots by hand)
566 gsl_vector *breakpts_x = gsl_vector_alloc(nbreak_x);
567 gsl_vector *breakpts_y = gsl_vector_alloc(nbreak_y);
568 gsl_vector *breakpts_z = gsl_vector_alloc(nbreak_z);
569 for (UINT4 i=0; i<nbreak_x; i++)
570 gsl_vector_set(breakpts_x, i, qvec[i]);
571 for (UINT4 j=0; j<nbreak_y; j++)
572 gsl_vector_set(breakpts_y, j, chi1vec[j]);
573 for (UINT4 k=0; k<nbreak_z; k++)
574 gsl_vector_set(breakpts_z, k, chi2vec[k]);
575
576 gsl_bspline_knots(breakpts_x, bwx);
577 gsl_bspline_knots(breakpts_y, bwy);
578 gsl_bspline_knots(breakpts_z, bwz);
579
580 gsl_vector_free(breakpts_x);
581 gsl_vector_free(breakpts_y);
582 gsl_vector_free(breakpts_z);
583
584 (*splinedata)->bwx=bwx;
585 (*splinedata)->bwy=bwy;
586 (*splinedata)->bwz=bwz;
587}
588
589/* Destroy B-spline basis functions for given points */
590static void SplineData_Destroy(SplineData *splinedata)
591{
592 if(!splinedata) return;
593 if(splinedata->bwx) gsl_bspline_free(splinedata->bwx);
594 if(splinedata->bwy) gsl_bspline_free(splinedata->bwy);
595 if(splinedata->bwz) gsl_bspline_free(splinedata->bwz);
596 XLALFree(splinedata);
597}
598
599// Allocate memory for an array of AmpPhaseSplineData structs to store all modes
601 AmpPhaseSplineData ***data_array,
602 const int num_modes
603)
604{
605 if(*data_array != NULL) AmpPhaseSplineData_Destroy(*data_array, num_modes);
606
607 *data_array = XLALCalloc(num_modes, sizeof(AmpPhaseSplineData));
608
609 for (int i=0; i<num_modes; i++) {
610 // The elements of the AmpPhaseSplineData structs will be allocated elsewhere.
611 (*data_array)[i] = XLALCalloc(1, sizeof(AmpPhaseSplineData));
612 }
613}
614
615static void AmpPhaseSplineData_Destroy(AmpPhaseSplineData **data_array, const int num_modes)
616{
617 if(!data_array) return;
618
619 AmpPhaseSplineData* data = NULL;
620 for (int i=0; i<num_modes; i++) {
621 data = data_array[i];
622 if (!data)
623 continue;
624 else {
625 if (data->spline_amp) gsl_spline_free(data->spline_amp);
626 if (data->spline_phi) gsl_spline_free(data->spline_phi);
627 if (data->acc_amp) gsl_interp_accel_free(data->acc_amp);
628 if (data->acc_phi) gsl_interp_accel_free(data->acc_phi);
629 if (data->f) gsl_vector_free(data->f);
630 XLALFree(data);
631 }
632 }
633 XLALFree(data_array);
634}
635
636
637// Interpolate projection coefficients for either Re(c-mode), Im(c-mode) or orbital phase over the parameter space (q, chi).
638// The multi-dimensional interpolation is carried out via a tensor product decomposition.
640 REAL8 q, // Input: eta-value for which projection coefficients should be evaluated
641 REAL8 chi1, // Input: chi1-value for which projection coefficients should be evaluated
642 REAL8 chi2, // Input: chi2-value for which projection coefficients should be evaluated
643 gsl_vector *cvec, // Input: data for spline coefficients
644 int nk, // number of SVD-modes == number of basis functions
645 int nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
646 int ncx, // Number of points in eta + 2
647 int ncy, // Number of points in chi1 + 2
648 int ncz, // Number of points in chi2 + 2
649 const double *qvec, // B-spline knots in eta
650 const double *chi1vec, // B-spline knots in chi1
651 const double *chi2vec, // B-spline knots in chi2
652 gsl_vector *c_out // Output: interpolated projection coefficients
653 ) {
654 if (nk_max != -1) {
655 if (nk_max > nk)
656 XLAL_ERROR(XLAL_EDOM, "Truncation parameter nk_max %d must be smaller or equal to nk %d\n", nk_max, nk);
657 else { // truncate SVD modes
658 nk = nk_max;
659 }
660 }
661
662 SplineData *splinedata=NULL;
663 SplineData_Init(&splinedata, ncx, ncy, ncz, qvec, chi1vec, chi2vec);
664
665 gsl_bspline_workspace *bwx=splinedata->bwx;
666 gsl_bspline_workspace *bwy=splinedata->bwy;
667 gsl_bspline_workspace *bwz=splinedata->bwz;
668
669 int N = ncx*ncy*ncz; // Size of the data matrix for one SVD-mode
670 // Evaluate the TP spline for all SVD modes - amplitude
671 for (int k=0; k<nk; k++) { // For each SVD mode
672 gsl_vector v = gsl_vector_subvector(cvec, k*N, N).vector; // Pick out the coefficient matrix corresponding to the k-th SVD mode.
673 REAL8 csum = Interpolate_Coefficent_Tensor(&v, q, chi1, chi2, ncy, ncz, bwx, bwy, bwz);
674 gsl_vector_set(c_out, k, csum);
675 }
676 SplineData_Destroy(splinedata);
677
678 return(0);
679}
680
681/** Setup SEOBNRv5HMROM mode using data files installed in dir
682 */
683static int SEOBNRv5HMROM_Init(const char dir[],UINT4 index_mode, bool use_hm, SEOBNRROMdataDS *romdataset) {
684 if(romdataset[index_mode].setup) {
685 XLALPrintError("Error: SEOBNRv5HMROM data was already set up!");
687 }
688 SEOBNRROMdataDS_Init(&romdataset[index_mode], dir, index_mode, use_hm);
689
690 if(romdataset[index_mode].setup) {
691 return(XLAL_SUCCESS);
692 }
693 else {
694 return(XLAL_EFAILED);
695 }
696 return(XLAL_SUCCESS);
697}
698
699/* Set up a new ROM mode, using data contained in dir */
701 UNUSED SEOBNRROMdataDS *romdata,
702 UNUSED const char dir[],
703 UNUSED UINT4 index_mode,
704 UNUSED bool use_hm
705 )
706{
707 int ret = XLAL_FAILURE;
708
709 /* Create storage for structures */
710 if(romdata->setup) {
711 XLALPrintError("WARNING: You tried to setup the SEOBNRv5HMROM model that was already initialised. Ignoring\n");
712 return (XLAL_FAILURE);
713 }
714
715#ifdef LAL_HDF5_ENABLED
716 // First, check we got the correct version number
717 size_t size;
718 if (use_hm == true){
719 size = strlen(dir) + strlen(ROMDataHDF5) + 2;
720 }
721 else{
722 size = strlen(dir) + strlen(ROM22DataHDF5) + 2;
723 }
724 char *path = XLALMalloc(size);
725 if (use_hm == true){
726 snprintf(path, size, "%s/%s", dir, ROMDataHDF5);
727 }
728 else{
729 snprintf(path, size, "%s/%s", dir, ROM22DataHDF5);
730 }
732
733 XLALPrintInfo("ROM metadata\n============\n");
734 if (use_hm == true){
735 PrintInfoStringAttribute(file, "Email");
736 PrintInfoStringAttribute(file, "Description");
737 ret = ROM_check_canonical_file_basename(file,ROMDataHDF5,"CANONICAL_FILE_BASENAME");
738 }
739 else{
740 PrintInfoStringAttribute(file, "Email");
741 PrintInfoStringAttribute(file, "Description");
742 ret = ROM_check_canonical_file_basename(file,ROM22DataHDF5,"CANONICAL_FILE_BASENAME");
743 }
744
745 ret |= SEOBNRROMdataDS_Init_submodel(&(romdata)->highf, dir, "highf",index_mode,use_hm);
746 if (ret==XLAL_SUCCESS) XLALPrintInfo("%s : submodel high freqs loaded sucessfully.\n", __func__);
747
748 ret |= SEOBNRROMdataDS_Init_submodel(&(romdata)->lowf, dir, "lowf",index_mode,use_hm);
749 if (ret==XLAL_SUCCESS) XLALPrintInfo("%s : submodel low freqs loaded sucessfully.\n", __func__);
750
751
752 if(XLAL_SUCCESS==ret){
753 romdata->setup=1;
754 }
755 else
757
758 XLALFree(path);
760 ret = XLAL_SUCCESS;
761
762#else
763 XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
764#endif
765
766 return (ret);
767}
768
769/* Deallocate contents of the given SEOBNRROMdataDS structure */
770static void SEOBNRROMdataDS_Cleanup(SEOBNRROMdataDS *romdata) {
771 SEOBNRROMdataDS_Cleanup_submodel((romdata)->highf);
772 XLALFree((romdata)->highf);
773 (romdata)->highf = NULL;
774 SEOBNRROMdataDS_Cleanup_submodel((romdata)->lowf);
775 XLALFree((romdata)->lowf);
776 (romdata)->lowf = NULL;
777 romdata->setup=0;
778}
779
780/* Deallocate contents of the given SEOBNRROMdataDS_submodel structure */
781static void SEOBNRROMdataDS_Cleanup_submodel(SEOBNRROMdataDS_submodel *submodel) {
782 if(submodel->cvec_real) gsl_vector_free(submodel->cvec_real);
783 if(submodel->cvec_imag) gsl_vector_free(submodel->cvec_imag);
784 if(submodel->cvec_phase) gsl_vector_free(submodel->cvec_phase);
785 if(submodel->Breal) gsl_matrix_free(submodel->Breal);
786 if(submodel->Bimag) gsl_matrix_free(submodel->Bimag);
787 if(submodel->Bphase) gsl_matrix_free(submodel->Bphase);
788 if(submodel->gCMode) gsl_vector_free(submodel->gCMode);
789 if(submodel->gPhase) gsl_vector_free(submodel->gPhase);
790 if(submodel->qvec) gsl_vector_free(submodel->qvec);
791 if(submodel->chi1vec) gsl_vector_free(submodel->chi1vec);
792 if(submodel->chi2vec) gsl_vector_free(submodel->chi2vec);
793}
794
795/* Set up a new ROM submodel, using data contained in dir */
797 SEOBNRROMdataDS_submodel **submodel,
798 UNUSED const char dir[],
799 UNUSED const char grp_name[],
800 UNUSED UINT4 index_mode,
801 UNUSED bool use_hm
802) {
803 int ret = XLAL_FAILURE;
804 if(!submodel) exit(1);
805 /* Create storage for submodel structures */
806 if (!*submodel)
807 *submodel = XLALCalloc(1,sizeof(SEOBNRROMdataDS_submodel));
808 else
810
811#ifdef LAL_HDF5_ENABLED
812 size_t size;
813 if (use_hm == true){
814 size = strlen(dir) + strlen(ROMDataHDF5) + 2;
815 }
816 else{
817 size = strlen(dir) + strlen(ROM22DataHDF5) + 2;
818 }
819 char *path = XLALMalloc(size);
820 if (use_hm == true){
821 snprintf(path, size, "%s/%s", dir, ROMDataHDF5);
822 }
823 else{
824 snprintf(path, size, "%s/%s", dir, ROM22DataHDF5);
825 }
826
828 LALH5File *sub = XLALH5GroupOpen(file, grp_name);
829
830 // Read ROM coefficients
831
832 //// c-modes coefficients
833 char* path_to_dataset = concatenate_strings(3,"CF_modes/",mode_array_v5hm[index_mode],"/coeff_re_flattened");
834 ReadHDF5RealVectorDataset(sub, path_to_dataset, & (*submodel)->cvec_real);
835 free(path_to_dataset);
836 path_to_dataset = concatenate_strings(3,"CF_modes/",mode_array_v5hm[index_mode],"/coeff_im_flattened");
837 ReadHDF5RealVectorDataset(sub, path_to_dataset, & (*submodel)->cvec_imag);
838 free(path_to_dataset);
839 //// orbital phase coefficients
840 //// They are used only in the 22 mode
841 if(index_mode == 0){
842 ReadHDF5RealVectorDataset(sub, "phase_carrier/coeff_flattened", & (*submodel)->cvec_phase);
843 }
844
845
846 // Read ROM basis functions
847
848 //// c-modes basis
849 path_to_dataset = concatenate_strings(3,"CF_modes/",mode_array_v5hm[index_mode],"/basis_re");
850 ReadHDF5RealMatrixDataset(sub, path_to_dataset, & (*submodel)->Breal);
851 free(path_to_dataset);
852 path_to_dataset = concatenate_strings(3,"CF_modes/",mode_array_v5hm[index_mode],"/basis_im");
853 ReadHDF5RealMatrixDataset(sub, path_to_dataset, & (*submodel)->Bimag);
854 free(path_to_dataset);
855 //// orbital phase basis
856 //// Used only in the 22 mode
857 if(index_mode == 0){
858 ReadHDF5RealMatrixDataset(sub, "phase_carrier/basis", & (*submodel)->Bphase);
859 }
860 // Read sparse frequency points
861
862 //// c-modes grid
863 path_to_dataset = concatenate_strings(3,"CF_modes/",mode_array_v5hm[index_mode],"/MF_grid");
864 ReadHDF5RealVectorDataset(sub, path_to_dataset, & (*submodel)->gCMode);
865 free(path_to_dataset);
866 //// orbital phase grid
867 //// Used only in the 22 mode
868 if(index_mode == 0){
869 ReadHDF5RealVectorDataset(sub, "phase_carrier/MF_grid", & (*submodel)->gPhase);
870 }
871 // Read parameter space nodes
872 ReadHDF5RealVectorDataset(sub, "qvec", & (*submodel)->qvec);
873 ReadHDF5RealVectorDataset(sub, "chi1vec", & (*submodel)->chi1vec);
874 ReadHDF5RealVectorDataset(sub, "chi2vec", & (*submodel)->chi2vec);
875
876
877 // Initialize other members
878 (*submodel)->nk_cmode = (*submodel)->gCMode->size;
879 //// Used only in the 22 mode
880 if(index_mode == 0){
881 (*submodel)->nk_phase = (*submodel)->gPhase->size;
882 }
883 (*submodel)->ncx = (*submodel)->qvec->size + 2;
884 (*submodel)->ncy = (*submodel)->chi1vec->size + 2;
885 (*submodel)->ncz = (*submodel)->chi2vec->size + 2;
886
887 // Domain of definition of submodel
888 (*submodel)->q_bounds[0] = gsl_vector_get((*submodel)->qvec, 0);
889 (*submodel)->q_bounds[1] = gsl_vector_get((*submodel)->qvec, (*submodel)->qvec->size - 1);
890 (*submodel)->chi1_bounds[0] = gsl_vector_get((*submodel)->chi1vec, 0);
891 (*submodel)->chi1_bounds[1] = gsl_vector_get((*submodel)->chi1vec, (*submodel)->chi1vec->size - 1);
892 (*submodel)->chi2_bounds[0] = gsl_vector_get((*submodel)->chi2vec, 0);
893 (*submodel)->chi2_bounds[1] = gsl_vector_get((*submodel)->chi2vec, (*submodel)->chi2vec->size - 1);
894
895 XLALFree(path);
897 XLALH5FileClose(sub);
898 ret = XLAL_SUCCESS;
899#else
900 XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
901#endif
902
903 return ret;
904}
905
906/* Create structure for internal use to store coefficients for orbital phase and real/imaginary part of co-orbital modes */
907static void SEOBNRROMdataDS_coeff_Init(SEOBNRROMdataDS_coeff **romdatacoeff, int nk_cmode, int nk_phase) {
908 if(!romdatacoeff) exit(1);
909 /* Create storage for structures */
910 if(!*romdatacoeff)
911 *romdatacoeff=XLALCalloc(1,sizeof(SEOBNRROMdataDS_coeff));
912 else
913 SEOBNRROMdataDS_coeff_Cleanup(*romdatacoeff);
914
915 (*romdatacoeff)->c_real = gsl_vector_alloc(nk_cmode);
916 (*romdatacoeff)->c_imag = gsl_vector_alloc(nk_cmode);
917 (*romdatacoeff)->c_phase = gsl_vector_alloc(nk_phase);
918}
919
920/* Deallocate contents of the given SEOBNRROMdataDS_coeff structure */
922 if(romdatacoeff->c_real) gsl_vector_free(romdatacoeff->c_real);
923 if(romdatacoeff->c_imag) gsl_vector_free(romdatacoeff->c_imag);
924 if(romdatacoeff->c_phase) gsl_vector_free(romdatacoeff->c_phase);
925 XLALFree(romdatacoeff);
926}
927/* Return the closest higher power of 2 */
928// Note: NextPow(2^k) = 2^k for integer values k.
929static size_t NextPow2(const size_t n) {
930 return 1 << (size_t) ceil(log2(n));
931}
932/* This function returns the orbital phase in the sparse grid hybridized between LF and HF ROM */
933UNUSED static UINT8 SEOBNRv5HMROM_phase_sparse_grid_hybrid(gsl_vector* freq, gsl_vector* phase, gsl_vector* freq_lo, gsl_vector* freq_hi, REAL8 q, REAL8 chi1,REAL8 chi2,INT4 nk_max, SEOBNRROMdataDS *romdataset){
934
935 gsl_vector *phase_f_lo = gsl_vector_alloc(freq_lo->size);
936 gsl_vector *phase_f_hi = gsl_vector_alloc(freq_hi->size);
937
938 /* Get LF and HF orbital phase */
939 UNUSED UINT8 retcode = SEOBNRv5HMROM_phase_sparse_grid(phase_f_lo,q,chi1,chi2, "LF",nk_max, romdataset);
940 retcode = SEOBNRv5HMROM_phase_sparse_grid(phase_f_hi,q,chi1,chi2, "HF",nk_max, romdataset);
941
942 /* Align LF and HF orbital phase */
943 /* We will discard the constant and slope of the linear fit */
944 REAL8 constant = 0.;
945 REAL8 slope = 0.;
946 retcode = align_wfs_window(freq_lo, freq_hi, phase_f_lo, phase_f_hi, &constant, &slope, f_hyb_ini, f_hyb_end);
947
948 /* Blend LF and HF orbital phase together */
949 retcode = blend_functions(freq,phase,freq_lo,phase_f_lo,freq_hi,phase_f_hi,f_hyb_ini, f_hyb_end);
950
951 /* Cleanup */
952 gsl_vector_free(phase_f_lo);
953 gsl_vector_free(phase_f_hi);
954
955 return XLAL_SUCCESS;
956
957}
958
959/* This function returns the frequency and orbital phase in a sparse grid. The frequency and orbital phase are hybridized between LF and HF ROM. */
960UNUSED static UINT8 SEOBNRv5HMROM_freq_phase_sparse_grid_hybrid(UNUSED gsl_vector** freq, UNUSED gsl_vector** phase, REAL8 q, REAL8 chi1,REAL8 chi2, INT4 nk_max, SEOBNRROMdataDS *romdataset){
961
962 /* We point to the data for computing the orbital phase. */
963 /* These data are stored in the structures for all modes */
964 /* We use the 22 mode (index 0) structure because the 22 mode will always be present */
965 SEOBNRROMdataDS *romdata=&romdataset[0];
966 if (!SEOBNRv5HMROM_IsSetup(0, romdataset)) {
968 "Error setting up SEOBNRv5ROM data - check your $LAL_DATA_PATH\n");
969 }
970
971 UNUSED UINT4 retcode=0;
972
973 //We always need to glue two submodels together for this ROM
974 SEOBNRROMdataDS_submodel *submodel_hi; // high frequency ROM
975 SEOBNRROMdataDS_submodel *submodel_lo; // low frequency ROM
976
977 /* Select ROM patches */
978 //There is only one LF patch
979 submodel_lo = romdata->lowf;
980 //There is only one HF patch
981 submodel_hi = romdata->highf;
982
983 /* Compute hybridized orbital phase */
984
985 INT8 i_max_LF = 0;
986 INT8 i_min_HF = 0;
987
988 // Get sparse frequency array for the LF orbital phase
989 gsl_vector* freq_lo = gsl_vector_alloc(submodel_lo->nk_phase);
990 for(unsigned int i = 0; i < freq_lo->size; i++){
991 freq_lo->data[i] = submodel_lo->gPhase->data[i];
992 }
993 gsl_vector* freq_hi = gsl_vector_alloc(submodel_hi->nk_phase);
994
995 //Compute prefactor for rescaling the frequency.
996 //The HF ROM is build using freq/RD_freq, we need to compute RD_freq and undo the scaling
997
998 REAL8 omegaQNM = Get_omegaQNM_SEOBNRv5(q,chi1,chi2,2,2);
999 REAL8 inv_scaling = 1. / (2*LAL_PI/omegaQNM);
1000 for(unsigned int i = 0; i < freq_hi->size; i++){
1001 freq_hi->data[i] = inv_scaling*submodel_hi->gPhase->data[i];
1002 }
1003
1004 //Initialize arrays for hybridized phase
1005 // Compute length of hybridized frequency array
1006 retcode = compute_i_max_LF_i_min_HF(&i_max_LF, &i_min_HF,freq_lo,freq_hi,f_hyb_ini);
1007 *freq = gsl_vector_alloc(i_max_LF + freq_hi->size - i_min_HF + 1);
1008
1009 //It's important to initialize this function to 0
1010 *phase = gsl_vector_calloc((*freq)->size);
1011
1012 // Fill the hybridized frequency array
1013 for(unsigned int i = 0; i <= i_max_LF;i++){
1014 (*freq)->data[i] = freq_lo->data[i];
1015 };
1016 for(unsigned int i = i_min_HF; i < freq_hi->size;i++){
1017 (*freq)->data[i_max_LF+i-i_min_HF+1] = freq_hi->data[i];
1018 };
1019
1020 /* Get the hybridized orbital phase */
1021
1022 retcode = SEOBNRv5HMROM_phase_sparse_grid_hybrid(*freq,*phase,freq_lo, freq_hi,q,chi1,chi2,nk_max,romdataset);
1023
1024 /* Cleanup */
1025 gsl_vector_free(freq_lo);
1026 gsl_vector_free(freq_hi);
1027
1028
1029 return XLAL_SUCCESS;
1030
1031}
1032
1033// Convention for alignment quantities:
1034// phase_hi ~= phase_lo + Deltaphi_align + 2pi*f*Deltat_align
1036 gsl_vector **freq, // Output: hybrid frequency array
1037 gsl_vector **phase, // Output: hybrid phase
1038 REAL8* Deltat_align, // Output: time alignment
1039 REAL8* Deltaphi_align, // Output: phase alignment
1040 gsl_vector *phase_lo, // low frequency phase
1041 gsl_vector *phase_hi, // high frequency phase
1042 gsl_vector *freq_lo, // frequency array for low frequency phase
1043 gsl_vector *freq_hi, // frequency array for high frequency phase
1044 double f_hyb_lo, // start frequency of hybridization window
1045 double f_hyb_hi // end frequency of hybridization window
1046){
1047 INT8 i_max_LF = 0;
1048 INT8 i_min_HF = 0;
1049
1050 // Initialize arrays for hybridized phase
1051
1052 // Find indices of f_hyb_lo in freq_lo and freq_hi s. t.
1053 // freq_lo[i_max_LF] < f_hyb_lo
1054 // freq_hi[i_min_HF] >= f_hyb_lo
1055 UINT4 retcode = compute_i_max_LF_i_min_HF(&i_max_LF, &i_min_HF, freq_lo, freq_hi, f_hyb_lo);
1056 if (retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1057
1058 // Compute length of hybridized frequency array
1059 *freq = gsl_vector_alloc(i_max_LF + freq_hi->size - i_min_HF + 1);
1060
1061 // Fill the hybridized frequency array
1062 for(unsigned int i=0; i <= i_max_LF; i++)
1063 (*freq)->data[i] = freq_lo->data[i];
1064 for(unsigned int i=i_min_HF; i < freq_hi->size; i++)
1065 (*freq)->data[i_max_LF+i-i_min_HF+1] = freq_hi->data[i];
1066
1067 // Initialize phase array with zeros
1068 *phase = gsl_vector_calloc((*freq)->size);
1069
1070 // Align low and high frequency phase over window [f_hyb_lo, f_hyb_hi]
1071 // Save phase and time shift from the linear fit of phase_hi - phase_lo
1072 REAL8 Deltat = 0.;
1073 REAL8 Deltaphi = 0.;
1074 retcode = align_wfs_window(freq_lo, freq_hi, phase_lo, phase_hi, &Deltat, &Deltaphi, f_hyb_lo, f_hyb_hi);
1075 if (retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1076
1077 // Blend low and high frequency phase together
1078 retcode = blend_functions(*freq, *phase, freq_lo, phase_lo, freq_hi, phase_hi, f_hyb_lo, f_hyb_hi);
1079 if (retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1080
1081 // We output the constant phase shift and time shift for phase_hi - phase_lo
1082 // Convention for alignment quantities:
1083 // phase_hi ~= phase_lo + Deltaphi_align + 2pi*f*Deltat_align
1084 *Deltat_align = Deltat;
1085 *Deltaphi_align = Deltaphi;
1086
1087 return XLAL_SUCCESS;
1088}
1089
1090// This is a general purpose function that hybridizes two phase arrays over frequency.
1092 gsl_vector **freq, // Output: hybrid frequency array
1093 gsl_vector **phase, // Output: hybrid phase
1094 gsl_vector *phase_lo, // low frequency phase
1095 gsl_vector *phase_hi, // high frequency phase
1096 gsl_vector *freq_lo, // frequency array for low frequency phase
1097 gsl_vector *freq_hi, // frequency array for high frequency phase
1098 double f_hyb_lo, // start frequency of hybridization window
1099 double f_hyb_hi, // end frequency of hybridization window
1100 REAL8 Deltat_22_align, // Input: 22 time alignment
1101 REAL8 Deltaphi_22_align, // Input: 22 phase alignment
1102 INT4 modeM // Input: m mode number
1103){
1104 INT8 i_max_LF = 0;
1105 INT8 i_min_HF = 0;
1106
1107 // Initialize arrays for hybridized phase
1108
1109 // Find indices of f_hyb_lo in freq_lo and freq_hi s. t.
1110 // freq_lo[i_max_LF] < f_hyb_lo
1111 // freq_hi[i_min_HF] >= f_hyb_lo
1112 UINT4 retcode = compute_i_max_LF_i_min_HF(&i_max_LF, &i_min_HF, freq_lo, freq_hi, f_hyb_lo);
1113 if (retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1114
1115 // Compute length of hybridized frequency array
1116 *freq = gsl_vector_alloc(i_max_LF + freq_hi->size - i_min_HF + 1);
1117
1118 // Fill the hybridized frequency array
1119 for(unsigned int i=0; i <= i_max_LF; i++)
1120 (*freq)->data[i] = freq_lo->data[i];
1121 for(unsigned int i=i_min_HF; i < freq_hi->size; i++)
1122 (*freq)->data[i_max_LF+i-i_min_HF+1] = freq_hi->data[i];
1123
1124 // Initialize phase array with zeros
1125 *phase = gsl_vector_calloc((*freq)->size);
1126
1127 // Align low and high frequency phase over window [f_hyb_lo, f_hyb_hi]
1128 retcode = align_wfs_window_from_22(freq_lo, freq_hi, phase_lo, phase_hi, f_hyb_lo, f_hyb_hi, Deltat_22_align, Deltaphi_22_align, modeM);
1129 if (retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1130
1131 // Blend low and high frequency phase together
1132 retcode = blend_functions(*freq, *phase, freq_lo, phase_lo, freq_hi, phase_hi, f_hyb_lo, f_hyb_hi);
1133 if (retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1134
1135 return XLAL_SUCCESS;
1136}
1137
1138// This is a general purpose function that hybridizes two amplitude arrays over frequency.
1140 gsl_vector **freq, // Output: hybrid frequency array
1141 gsl_vector **amp, // Output: hybrid amplitude
1142 gsl_vector *amp_lo, // low frequency amplitude
1143 gsl_vector *amp_hi, // high frequency amplitude
1144 gsl_vector *freq_lo, // frequency array for low frequency amplitude
1145 gsl_vector *freq_hi, // frequency array for high frequency amplitude
1146 double f_hyb_lo, // start frequency of hybridization window
1147 double f_hyb_hi // end frequency of hybridization window
1148){
1149 INT8 i_max_LF = 0;
1150 INT8 i_min_HF = 0;
1151
1152 // Initialize arrays for hybridized phase
1153 // Find indices of f_hyb_lo in freq_lo and freq_hi s. t.
1154 // freq_lo[i_max_LF] < f_hyb_lo
1155 // freq_hi[i_min_HF] >= f_hyb_lo
1156 UINT4 retcode = compute_i_max_LF_i_min_HF(&i_max_LF, &i_min_HF, freq_lo, freq_hi, f_hyb_lo);
1157 if (retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1158
1159 // Compute length of hybridized frequency array
1160 *freq = gsl_vector_alloc(i_max_LF + freq_hi->size - i_min_HF + 1);
1161
1162 // Fill the hybridized frequency array
1163 for(unsigned int i=0; i <= i_max_LF; i++)
1164 (*freq)->data[i] = freq_lo->data[i];
1165 for(unsigned int i=i_min_HF; i < freq_hi->size; i++)
1166 (*freq)->data[i_max_LF+i-i_min_HF+1] = freq_hi->data[i];
1167
1168 // Initialize phase array with zeros
1169 *amp = gsl_vector_calloc((*freq)->size);
1170
1171 // Blend low and high frequency phase together
1172 retcode = blend_functions(*freq, *amp, freq_lo, amp_lo, freq_hi, amp_hi, f_hyb_lo, f_hyb_hi);
1173 if (retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1174
1175 return XLAL_SUCCESS;
1176}
1177
1178
1179/* This function returns the frequency and approximate phase of nMode in a sparse grid hybridized between LF and HF ROM. */
1180/* Here we linearly extend the phase outside the regime where we have data */
1181/* The derivation of this approximate phase is in https://git.ligo.org/waveforms/reviews/SEOBNRv5HM_ROM/blob/master/documents/notes.pdf */
1182/* In this document this phase is the argument of the exponential in Eq.(22) */
1183UNUSED static UINT8 SEOBNRv5HMROM_approx_phi_lm(gsl_vector* freq_mode_lm, gsl_vector* phase_approx_lm, gsl_vector* freq_carrier_hyb, gsl_vector* phase_carrier_hyb, UINT4 nMode){
1184
1185 UINT8 modeM = lmModes_v5hm[nMode][1];
1186
1187 REAL8 f_max_carrier = freq_carrier_hyb->data[freq_carrier_hyb->size -1];
1188 REAL8 phase_carrier_at_f_max = phase_carrier_hyb->data[phase_carrier_hyb->size -1];
1189
1190 gsl_interp_accel *acc = gsl_interp_accel_alloc ();
1191 gsl_spline *spline = gsl_spline_alloc (gsl_interp_cspline, phase_carrier_hyb->size);
1192
1193 gsl_spline_init (spline, freq_carrier_hyb->data, phase_carrier_hyb->data, phase_carrier_hyb->size);
1194 REAL8 der_phase_carrier_at_f_max = gsl_spline_eval_deriv(spline, f_max_carrier, acc);
1195
1196 /* Constant shift between modes */
1197 REAL8 const_phase_shift = const_phaseshift_lm_v5hm[nMode] + (1.-(REAL8)modeM)*LAL_PI/4.;
1198
1199 for(unsigned int i = 0; i < freq_mode_lm->size; i++){
1200 if(freq_mode_lm->data[i]/(REAL8)modeM < f_max_carrier){
1201 /* Compute the approximate phase of lm mode from the carrier phase */
1202 phase_approx_lm->data[i] = (REAL8)modeM*gsl_spline_eval (spline, freq_mode_lm->data[i]/(REAL8)modeM, acc) + const_phase_shift;
1203 }
1204 else{
1205 /* Linear extrapolation for frequency at which we don't have data for the orbital phase */
1206 phase_approx_lm->data[i] = modeM*(phase_carrier_at_f_max + der_phase_carrier_at_f_max*(freq_mode_lm->data[i]/modeM-f_max_carrier)) + const_phase_shift;
1207 }
1208 }
1209
1210 /* Cleanup */
1211 gsl_spline_free(spline);
1212 gsl_interp_accel_free(acc);
1213
1214 return XLAL_SUCCESS;
1215}
1216
1217/* This function returns the Re/Im part of c-modes in a sparse grid. Re/Im of c-modes are hybridized between LF and HF ROM. */
1218/* The derivation of the c-modes is in https://git.ligo.org/waveforms/reviews/SEOBNRv5HM_ROM/blob/master/documents/notes.pdf Eq. (22)*/
1219UNUSED static UINT8 SEOBNRv5HMROM_cmode_sparse_grid_hybrid(gsl_vector* freq, gsl_vector* cmode_real, gsl_vector* cmode_imag, gsl_vector* freq_lo, gsl_vector* freq_hi, REAL8 q, REAL8 chi1,REAL8 chi2, UINT8 nMode,INT8 nk_max,SEOBNRROMdataDS *romdataset){
1220
1221 gsl_vector* real_f_lo = gsl_vector_alloc(freq_lo->size);
1222 gsl_vector* imag_f_lo = gsl_vector_alloc(freq_lo->size);
1223 gsl_vector* real_f_hi = gsl_vector_alloc(freq_hi->size);
1224 gsl_vector* imag_f_hi = gsl_vector_alloc(freq_hi->size);
1225
1226 /* Generate LF and HF cmode */
1227 UNUSED UINT8 retcode = SEOBNRv5HMROM_cmode_sparse_grid(real_f_lo, imag_f_lo,q,chi1,chi2,"LF",nMode,nk_max,romdataset);
1228 retcode = SEOBNRv5HMROM_cmode_sparse_grid(real_f_hi, imag_f_hi,q,chi1,chi2,"HF",nMode,nk_max,romdataset);
1229
1230 UINT8 modeM = lmModes_v5hm[nMode][1];
1231
1232 /* Blend LF and HF cmode together */
1233 retcode = blend_functions(freq,cmode_real,freq_lo,real_f_lo,freq_hi,real_f_hi,f_hyb_ini*(REAL8)modeM,f_hyb_end*(REAL8)modeM);
1234 retcode = blend_functions(freq,cmode_imag,freq_lo,imag_f_lo,freq_hi,imag_f_hi,f_hyb_ini*(REAL8)modeM,f_hyb_end*(REAL8)modeM);
1235
1236 /* Cleanup */
1237 gsl_vector_free(real_f_lo);
1238 gsl_vector_free(imag_f_lo);
1239 gsl_vector_free(real_f_hi);
1240 gsl_vector_free(imag_f_hi);
1241
1242 return XLAL_SUCCESS;
1243
1244}
1245
1246/* This function returns the frequency and Re/Im part of c-modes in a sparse grid. The frequency and Re/Im of c-modes are hybridized between LF and HF ROM. */
1247UNUSED static UINT8 SEOBNRv5HMROM_freq_cmode_sparse_grid_hybrid(gsl_vector** freq_cmode, gsl_vector** cmode_real, gsl_vector** cmode_imag, REAL8 q, REAL8 chi1,REAL8 chi2, UINT8 nMode,INT8 nk_max, SEOBNRROMdataDS *romdataset){
1248
1249 UINT8 modeL = lmModes_v5hm[nMode][0];
1250 UINT8 modeM = lmModes_v5hm[nMode][1];
1251
1252 /* We point to the data for computing the cmode. */
1253 SEOBNRROMdataDS *romdata=&romdataset[nMode];
1254 if (!SEOBNRv5HMROM_IsSetup(nMode,romdataset)) {
1256 "Error setting up SEOBNRv5ROM data - check your $LAL_DATA_PATH\n");
1257 }
1258
1259 UNUSED UINT4 retcode=0;
1260
1261 /* We always need to glue two submodels together for this ROM */
1262 SEOBNRROMdataDS_submodel *submodel_hi; // high frequency ROM
1263 SEOBNRROMdataDS_submodel *submodel_lo; // low frequency ROM
1264
1265 /* Select ROM patches */
1266 //There is only one LF patch
1267 submodel_lo = romdata->lowf;
1268 //There is only one HF patch
1269 submodel_hi = romdata->highf;
1270
1271 /* Compute hybridized Re/Im part c-mode */
1272
1273 INT8 i_max_LF = 0;
1274 INT8 i_min_HF = 0;
1275
1276 // Get sparse frequency array for the LF orbital phase
1277 gsl_vector* freq_lo = gsl_vector_alloc(submodel_lo->nk_cmode);
1278 gsl_vector* freq_hi = gsl_vector_alloc(submodel_hi->nk_cmode);
1279
1280 for(unsigned int i = 0; i < freq_lo->size; i++){
1281 freq_lo->data[i] = submodel_lo->gCMode->data[i];
1282 }
1283
1284 //Compute prefactor for rescaling the frequency.
1285 //The HF ROM is build using freq/RD_freq, we need to compute RD_freq and undo the scaling
1286 REAL8 omegaQNM = Get_omegaQNM_SEOBNRv5(q,chi1,chi2,modeL,modeM);
1287 REAL8 inv_scaling = 1. / (2*LAL_PI/omegaQNM);
1288 for(unsigned int i = 0; i < freq_hi->size; i++){
1289 freq_hi->data[i] = inv_scaling*submodel_hi->gCMode->data[i];
1290 }
1291
1292 //Initialize arrays for Re/Im part c-mode
1293 // Compute length of hybridized frequency array
1294 retcode = compute_i_max_LF_i_min_HF(&i_max_LF, &i_min_HF,freq_lo,freq_hi,f_hyb_ini*(REAL8)modeM);
1295
1296 *freq_cmode = gsl_vector_alloc(i_max_LF + freq_hi->size - i_min_HF + 1);
1297 // It's important to initialize this function to 0
1298
1299 for(unsigned int i = 0; i <= i_max_LF;i++){
1300 (*freq_cmode)->data[i] = freq_lo->data[i];
1301 };
1302 for(unsigned int i = i_min_HF; i < freq_hi->size;i++){
1303 (*freq_cmode)->data[i_max_LF+i-i_min_HF+1] = freq_hi->data[i];
1304 };
1305
1306 *cmode_real = gsl_vector_calloc((*freq_cmode)->size);
1307 *cmode_imag = gsl_vector_calloc((*freq_cmode)->size);
1308
1309
1310 // Generate the hybridized Re/Im part of c-mode
1311
1312 retcode = SEOBNRv5HMROM_cmode_sparse_grid_hybrid(*freq_cmode,*cmode_real,*cmode_imag,freq_lo, freq_hi,q,chi1,chi2,nMode,nk_max,romdataset);
1313
1314
1315 /* Cleanup */
1316 gsl_vector_free(freq_lo);
1317 gsl_vector_free(freq_hi);
1318
1319 return XLAL_SUCCESS;
1320}
1321
1322
1323
1324/* This function returns the orbital phase in a sparse grid */
1325UNUSED static UINT8 SEOBNRv5HMROM_phase_sparse_grid(gsl_vector* phase_f, REAL8 q, REAL8 chi1,REAL8 chi2, const char* freq_range,INT4 nk_max, SEOBNRROMdataDS *romdataset){
1326
1327 /* Loading the data for the 22 mode, the informations about orbital phase are stored here */
1328 SEOBNRROMdataDS *romdata=&romdataset[0];
1329 if (!SEOBNRv5HMROM_IsSetup(0,romdataset)) {
1331 "Error setting up SEOBNRv5ROM data - check your $LAL_DATA_PATH\n");
1332 }
1333 /* Check whether you are asking for the LF or the HF ROM */
1334 SEOBNRROMdataDS_submodel *submodel;
1335 if(strcmp("LF",freq_range) == 0){
1336 // In the case of LF there is only one patch
1337 submodel = romdata->lowf;
1338 }
1339 else{
1340 submodel = romdata->highf;
1341 }
1342 /* Create and initialize the structure for interpolating the orbital phase */
1343 SEOBNRROMdataDS_coeff *romdata_coeff=NULL;
1344 SEOBNRROMdataDS_coeff_Init(&romdata_coeff, submodel->nk_cmode, submodel->nk_phase);
1345
1346 /* Interpolating projection coefficients for orbital phase */
1348 q, // Input: q-value for which projection coefficients should be evaluated
1349 chi1, // Input: chi1-value for which projection coefficients should be evaluated
1350 chi2, // Input: chi2-value for which projection coefficients should be evaluated
1351 submodel->cvec_phase, // Input: data for spline coefficients for amplitude
1352 submodel->nk_phase, // number of SVD-modes == number of basis functions for amplitude
1353 nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1354 submodel->ncx, // Number of points in q + 2
1355 submodel->ncy, // Number of points in chi1 + 2
1356 submodel->ncz, // Number of points in chi2 + 2
1357 gsl_vector_const_ptr(submodel->qvec, 0), // B-spline knots in q
1358 gsl_vector_const_ptr(submodel->chi1vec, 0), // B-spline knots in chi1
1359 gsl_vector_const_ptr(submodel->chi2vec, 0), // B-spline knots in chi2
1360 romdata_coeff->c_phase // Output: interpolated projection coefficients for orbital phase
1361 );
1362
1363 if(retcode!=0) {
1364 SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff);
1365 XLAL_ERROR(retcode);
1366 }
1367
1368 /* Project interpolation coefficients onto the basis */
1369 /* phase_pts = B_A^T . c_A */
1370 gsl_blas_dgemv(CblasTrans, 1.0, submodel->Bphase, romdata_coeff->c_phase, 0.0, phase_f);
1371
1372 /* Cleanup */
1373 SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff);
1374
1375 return XLAL_SUCCESS;
1376}
1377
1378/* This function returns the Re/Im part of c-modes in a sparse grid. */
1379UNUSED static UINT8 SEOBNRv5HMROM_cmode_sparse_grid(gsl_vector* creal_f, gsl_vector* cimag_f, REAL8 q, REAL8 chi1,REAL8 chi2, const char* freq_range,UINT8 nMode,INT4 nk_max, SEOBNRROMdataDS *romdataset){
1380
1381 /* Loading the data for the lm mode */
1382 SEOBNRROMdataDS *romdata=&romdataset[nMode];
1383 if (!SEOBNRv5HMROM_IsSetup(nMode,romdataset)) {
1385 "Error setting up SEOBNRv5ROM data - check your $LAL_DATA_PATH\n");
1386 }
1387 /* Check whether you are asking for the LF or the HF ROM */
1388 SEOBNRROMdataDS_submodel *submodel;
1389 if(strcmp("LF",freq_range) == 0){
1390 /* In the case of LF there is only one patch */
1391 submodel = romdata->lowf;
1392 }
1393 else{
1394 /* In the case of HF there is only one patch */
1395 submodel = romdata->highf;
1396 }
1397 /* Create and initialize the structure for interpolating the coorbital mode */
1398 SEOBNRROMdataDS_coeff *romdata_coeff=NULL;
1399 SEOBNRROMdataDS_coeff_Init(&romdata_coeff, submodel->nk_cmode, submodel->nk_cmode);
1400
1401 /* Interpolating projection coefficients for Re(cmode) */
1403 q, // Input: eta-value for which projection coefficients should be evaluated
1404 chi1, // Input: chi1-value for which projection coefficients should be evaluated
1405 chi2, // Input: chi2-value for which projection coefficients should be evaluated
1406 submodel->cvec_real, // Input: data for spline coefficients for amplitude
1407 submodel->nk_cmode, // number of SVD-modes == number of basis functions for amplitude
1408 nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1409 submodel->ncx, // Number of points in q + 2
1410 submodel->ncy, // Number of points in chi1 + 2
1411 submodel->ncz, // Number of points in chi2 + 2
1412 gsl_vector_const_ptr(submodel->qvec, 0), // B-spline knots in eta
1413 gsl_vector_const_ptr(submodel->chi1vec, 0), // B-spline knots in chi1
1414 gsl_vector_const_ptr(submodel->chi2vec, 0), // B-spline knots in chi2
1415 romdata_coeff->c_real // Output: interpolated projection coefficients for Re(c-mode)
1416 );
1417
1418 /* Interpolating projection coefficients for Im(cmode) */
1420 q, // Input: eta-value for which projection coefficients should be evaluated
1421 chi1, // Input: chi1-value for which projection coefficients should be evaluated
1422 chi2, // Input: chi2-value for which projection coefficients should be evaluated
1423 submodel->cvec_imag, // Input: data for spline coefficients for amplitude
1424 submodel->nk_cmode, // number of SVD-modes == number of basis functions for amplitude
1425 nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1426 submodel->ncx, // Number of points in q + 2
1427 submodel->ncy, // Number of points in chi1 + 2
1428 submodel->ncz, // Number of points in chi2 + 2
1429 gsl_vector_const_ptr(submodel->qvec, 0), // B-spline knots in eta
1430 gsl_vector_const_ptr(submodel->chi1vec, 0), // B-spline knots in chi1
1431 gsl_vector_const_ptr(submodel->chi2vec, 0), // B-spline knots in chi2
1432 romdata_coeff->c_imag // Output: interpolated projection coefficients for Im(c-mode)
1433 );
1434
1435 if(retcode!=0) {
1436 SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff);
1437 XLAL_ERROR(retcode);
1438 }
1439
1440 /* Project interpolation coefficients onto the basis */
1441 /* creal_pts = B_A^T . c_A */
1442 /* cimag_pts = B_A^T . c_A */
1443 gsl_blas_dgemv(CblasTrans, 1.0, submodel->Breal, romdata_coeff->c_real, 0.0, creal_f);
1444 gsl_blas_dgemv(CblasTrans, 1.0, submodel->Bimag, romdata_coeff->c_imag, 0.0, cimag_f);
1445
1446 /* Cleanup */
1447 SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff);
1448
1449 return XLAL_SUCCESS;
1450}
1451
1452
1453/* Select high frequency ROM submodel */
1455 if ((q> 3.) && (chi1<=0.8)){
1456 return 0;
1457 }
1458 else if ((q> 3.) && (chi1>0.8)){
1459 return 1;
1460 }
1461 else if ((q<= 3.) && (chi1<=0.8)){
1462 return 2;
1463 }
1464 else if ((q<= 3.) && (chi1>0.8)){
1465 return 3;
1466 }
1467 else{
1469 XLALPrintError("XLAL Error - %s: SEOBNRv5HM not currently available in this region!\n",
1470 __func__);
1471 }
1472}
1473
1474
1475/* Internal function to compute amplitudes and phases for each mode of the ROM and return these as gsl splines. */
1477 UNUSED AmpPhaseSplineData **ampPhaseSplineData, /**<< Output: amplitude and phase splines for the modes */
1478 UNUSED REAL8 q, /**< Mass ratio **/
1479 UNUSED REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1480 UNUSED REAL8 chi2, /**< Dimensionless aligned component spin 2 */
1481 UNUSED INT4 nk_max, /**< truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 (default). */
1482 UNUSED UINT4 nModes, /**< Number of modes to generate */
1483 UNUSED SEOBNRROMdataDS *romdataset /**< Dataset for the 22 or HM ROM */
1484)
1485{
1486 /* Check output structure */
1487 if(!ampPhaseSplineData)
1489
1490 /* 'Nudge' parameter values to allowed boundary values if close by */
1491 if (q < 1.0) nudge(&q, 1.0, 1e-6);
1492 if (q > 100.0) nudge(&q, 100.0, 1e-6);
1493
1494 if ( chi1 < -0.998 - 1e-6 || chi2 < -0.998 - 1e-6 || chi1 > 0.998 + 1e-6|| chi2 > 0.998 + 1e-6) {
1495 XLALPrintError("XLAL Error - %s: chi1 or chi2 smaller than -0.998 or larger than 0.998!\n"
1496 "SEOBNRv5HMROM is only available for spins in the range -0.998 <= a/M <= 0.998.\n",
1497 __func__);
1499 }
1500
1501 if (q<1.0 || q > 100.0) {
1502 XLALPrintError("XLAL Error - %s: q (%f) bigger than 100.0 or unphysical!\n"
1503 "SEOBNRv5HMROM is only available for q in the range 1 <= q <= 100.\n",
1504 __func__, q);
1506 }
1507
1508 gsl_vector *freq_carrier_hyb = NULL;
1509 gsl_vector *phase_carrier_hyb = NULL;
1510
1511 /* Compute the orbital phase, this is the same for every mode. */
1512 /* For this reason it's outside the for loop over modes below. */
1514 &freq_carrier_hyb, &phase_carrier_hyb, q, chi1, chi2, nk_max,romdataset);
1515 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1516
1517 /* Put the phase into the right convention */
1518 for(unsigned int i=0; i < phase_carrier_hyb->size; i++) {
1519 phase_carrier_hyb->data[i] = -phase_carrier_hyb->data[i];
1520 }
1521
1522 /* Looping over modes: compute complex mode and from that amplitude and phase */
1523 for(unsigned int nMode=0; nMode < nModes; nMode++){
1524
1525 gsl_vector *freq_cmode_hyb = NULL;
1526 gsl_vector *cmode_real_hyb = NULL;
1527 gsl_vector *cmode_imag_hyb = NULL;
1528
1529 UNUSED UINT4 modeL = lmModes_v5hm[nMode][0];
1530 UNUSED UINT4 modeM = lmModes_v5hm[nMode][1];
1531
1532 /* Compute Re and Im c-modes */
1534 &freq_cmode_hyb, &cmode_real_hyb, &cmode_imag_hyb,
1535 q, chi1, chi2, nMode, nk_max, romdataset);
1536
1537 gsl_vector *phase_approx_lm = gsl_vector_alloc(freq_cmode_hyb->size);
1538 /* Compute approximated phase from orbital phase */
1539 retcode = SEOBNRv5HMROM_approx_phi_lm(freq_cmode_hyb, phase_approx_lm,
1540 freq_carrier_hyb, phase_carrier_hyb, nMode);
1541
1542 /* Compute the phase contribution coming from the cmode and unwrap it */
1543 COMPLEX16 complex_mode;
1544 gsl_vector *phase_cmode = gsl_vector_alloc(freq_cmode_hyb->size);
1545 for(unsigned int i=0; i < freq_cmode_hyb->size; i++) {
1546 complex_mode = cmode_real_hyb->data[i] + I*cmode_imag_hyb->data[i];
1547 phase_cmode->data[i] = carg(complex_mode);
1548 }
1549
1550 /* Unwrap the phase contribution from the cmode */
1551 gsl_vector *unwrapped_phase_cmode = gsl_vector_alloc(freq_cmode_hyb->size);
1552 retcode = unwrap_phase(unwrapped_phase_cmode,phase_cmode);
1553
1554 /* Reconstruct amplitude and phase */
1555 gsl_vector *reconstructed_phase = gsl_vector_alloc(freq_cmode_hyb->size);
1556 gsl_vector *reconstructed_amplitude = gsl_vector_alloc(freq_cmode_hyb->size);
1557 for(unsigned int i=0; i < freq_cmode_hyb->size; i++) {
1558 complex_mode = cmode_real_hyb->data[i] + I*cmode_imag_hyb->data[i];
1559 reconstructed_amplitude->data[i] = cabs(complex_mode);
1560 reconstructed_phase->data[i] = unwrapped_phase_cmode->data[i] - phase_approx_lm->data[i];
1561 }
1562
1563 // Build amplitude and phase splines
1564 gsl_interp_accel *acc_amp = gsl_interp_accel_alloc();
1565 gsl_spline *spline_amp = gsl_spline_alloc (gsl_interp_cspline, reconstructed_amplitude->size);
1566 gsl_spline_init(spline_amp, freq_cmode_hyb->data, reconstructed_amplitude->data, reconstructed_amplitude->size);
1567
1568 gsl_interp_accel *acc_phase = gsl_interp_accel_alloc();
1569 gsl_spline *spline_phase = gsl_spline_alloc (gsl_interp_cspline, reconstructed_phase->size);
1570 gsl_spline_init(spline_phase, freq_cmode_hyb->data, reconstructed_phase->data, reconstructed_phase->size);
1571
1572 // Store amplitude and phase spline data for this mode.
1573 // We return this information. The caller is responsible for freeing it.
1574 ampPhaseSplineData[nMode]->spline_amp = spline_amp;
1575 ampPhaseSplineData[nMode]->spline_phi = spline_phase;
1576 ampPhaseSplineData[nMode]->acc_amp = acc_amp;
1577 ampPhaseSplineData[nMode]->acc_phi = acc_phase;
1578 ampPhaseSplineData[nMode]->f = freq_cmode_hyb;
1579
1580 /* Cleanup inside of loop over modes */
1581 gsl_vector_free(cmode_real_hyb);
1582 gsl_vector_free(cmode_imag_hyb);
1583 gsl_vector_free(phase_approx_lm);
1584 gsl_vector_free(phase_cmode);
1585 gsl_vector_free(unwrapped_phase_cmode);
1586 gsl_vector_free(reconstructed_amplitude);
1587 gsl_vector_free(reconstructed_phase);
1588 }
1589
1590 /* Cleanup outside of loop over modes */
1591 gsl_vector_free(freq_carrier_hyb);
1592 gsl_vector_free(phase_carrier_hyb);
1593
1594 return(XLAL_SUCCESS);
1595}
1596
1597
1598/**
1599* Core function for computing the ROM modes.
1600* It rebuilds the modes starting from "orbital phase" and co-orbital modes.
1601* This function returns SphHarmFrequencySeries
1602*/
1603UNUSED static int SEOBNRv5HMROMCoreModes(
1604 UNUSED SphHarmFrequencySeries **hlm_list, /**<< Spherical modes frequency series for the waveform */
1605 UNUSED REAL8 phiRef, /**<< orbital reference phase */
1606 UNUSED REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
1607 UNUSED REAL8 distance, /**< Distance of source (m) */
1608 UNUSED REAL8 Mtot_sec, /**< Total mass in seconds **/
1609 UNUSED REAL8 q, /**< Mass ratio **/
1610 UNUSED REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1611 UNUSED REAL8 chi2, /**< Dimensionless aligned component spin 2 */
1612 UNUSED const REAL8Sequence *freqs_in, /**< Frequency points at which to evaluate the waveform (Hz) */
1613 UNUSED REAL8 deltaF, /**< Frequency points at which to evaluate the waveform (Hz) */
1614 UNUSED INT4 nk_max, /**< truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1. We
1615 *nk_max == -1 is the default setting */
1616 UNUSED UINT4 nModes, /**< Number of modes to generate */
1617 REAL8 sign_odd_modes, /**< Sign of the odd-m modes, used when swapping the two bodies */
1618 UNUSED SEOBNRROMdataDS *romdataset /**< Dataset for the 22 or HM ROM */
1619 )
1620{
1621 /* Check output structure */
1622 if(!hlm_list)
1624
1625 /* 'Nudge' parameter values to allowed boundary values if close by */
1626 if (q < 1.0) nudge(&q, 1.0, 1e-6);
1627 if (q > 100.0) nudge(&q, 100.0, 1e-6);
1628
1629 if ( chi1 < -0.998 || chi2 < -0.998 || chi1 > 0.998 || chi2 > 0.998) {
1630 XLALPrintError("XLAL Error - %s: chi1 or chi2 smaller than -0.998 or larger than 0.998!\n"
1631 "SEOBNRv5HMROM is only available for spins in the range -0.998 <= a/M <= 0.998.\n",
1632 __func__);
1634 }
1635
1636 if (q<1.0 || q > 100.0) {
1637 XLALPrintError("XLAL Error - %s: q (%f) bigger than 100.0 or unphysical!\n"
1638 "SEOBNRv5HMROM is only available for q in the range 1 <= q <= 100.\n",
1639 __func__, q);
1641 }
1642
1643
1644 // Get the splines for the amplitude and phase of the modes
1645 AmpPhaseSplineData **ampPhaseSplineData = NULL;
1646 AmpPhaseSplineData_Init(&ampPhaseSplineData, nModes);
1647 UNUSED int retcode = SEOBNRv5HMROMCoreModeAmpPhaseSplines(
1648 ampPhaseSplineData, q, chi1, chi2, nk_max, nModes,romdataset);
1649 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
1650
1651
1652 /* Find frequency bounds */
1653 if (!freqs_in) XLAL_ERROR(XLAL_EFAULT);
1654 REAL8 fLow = freqs_in->data[0];
1655 REAL8 fHigh = freqs_in->data[freqs_in->length - 1];
1656
1657 UNUSED REAL8 fLow_geom = fLow * Mtot_sec;
1658 REAL8 fHigh_geom = fHigh * Mtot_sec;
1659 REAL8 deltaF_geom = deltaF * Mtot_sec;
1660 // The maximum available frequency is the one associated with the 55 mode
1661 REAL8 Mf_ROM_max = const_fmax_lm_v5hm[4] * Get_omegaQNM_SEOBNRv5(q, chi1, chi2, 5, 5) / (2.*LAL_PI);
1662
1663 // Enforce allowed geometric frequency range
1664 if (fHigh_geom == 0)
1665 fHigh_geom = Mf_ROM_max;
1666 if (fLow_geom < Mf_low_22)
1667 XLAL_ERROR(XLAL_EDOM, "Starting frequency Mflow=%g is smaller than lowest frequency in ROM Mf=%g.\n", fLow_geom, Mf_low_22);
1668 if (fHigh_geom < Mf_low_22)
1669 XLAL_ERROR(XLAL_EDOM, "End frequency %g is smaller than ROM starting frequency %g!\n", fHigh_geom, Mf_low_22);
1670 if (fHigh_geom <= fLow_geom)
1671 XLAL_ERROR(XLAL_EDOM, "End frequency %g is smaller than (or equal to) starting frequency %g!\n", fHigh_geom, fLow_geom);
1672
1673 /* Looping over cmodes */
1674 for(unsigned int nMode=0; nMode < nModes; nMode++){
1675 UNUSED UINT4 modeL = lmModes_v5hm[nMode][0];
1676 UNUSED UINT4 modeM = lmModes_v5hm[nMode][1];
1677
1678 size_t npts = 0;
1679 LIGOTimeGPS tC = {0, 0};
1680 UINT4 offset = 0; // Index shift between freqs and the frequency series
1681 REAL8Sequence *freqs = NULL;
1682 // freqs contains uniform frequency grid with spacing deltaF; we start at frequency 0
1683 // I removed the if statement for the time being
1684 /* Set up output array with size closest power of 2 */
1685 npts = NextPow2(fHigh_geom / deltaF_geom) + 1;
1686 if (fHigh_geom < fHigh * Mtot_sec) /* Resize waveform if user wants f_max larger than cutoff frequency */
1687 npts = NextPow2(fHigh * Mtot_sec / deltaF_geom) + 1;
1688
1689 XLALGPSAdd(&tC, -1. / deltaF); /* coalesce at t=0 */
1690 COMPLEX16FrequencySeries *hlmtilde = XLALCreateCOMPLEX16FrequencySeries("hlmtilde: FD mode", &tC, 0.0, deltaF, &lalStrainUnit, npts);
1691
1692
1693 // Recreate freqs using only the lower and upper bounds
1694 // Use fLow, fHigh and deltaF rather than geometric frequencies for numerical accuracy
1695 double fHigh_temp = fHigh_geom / Mtot_sec;
1696 UINT4 iStart = (UINT4) ceil(fLow / deltaF);
1697 UINT4 iStop = (UINT4) ceil(fHigh_temp / deltaF);
1698 freqs = XLALCreateREAL8Sequence(iStop - iStart);
1699 if (!freqs) XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
1700 for (UINT4 i=iStart; i<iStop; i++)
1701 freqs->data[i-iStart] = i*deltaF_geom;
1702
1703 offset = iStart;
1704
1705
1706 if (!hlmtilde) {
1709 }
1710
1711 memset(hlmtilde->data->data, 0, npts * sizeof(COMPLEX16));
1712 XLALUnitMultiply(&(hlmtilde->sampleUnits), &(hlmtilde->sampleUnits), &lalSecondUnit);
1713 COMPLEX16 *hlmdata=hlmtilde->data->data;
1714
1715 REAL8 Mtot = Mtot_sec / LAL_MTSUN_SI;
1716 // Correct overall amplitude to undo mass-dependent scaling used in ROM
1717 REAL8 amp0 = Mtot * Mtot_sec * LAL_MRSUN_SI / (distance);
1718
1719 // ROM was build with t = 0 at t_peak^22 -1000. To agree with LAL convention, we undo this shift to get t_peak^22 = 0
1720 REAL8 t_corr = 1000.;
1721
1722 // Shorthands for amplitude and phase splines for this mode
1723 gsl_spline *spline_amp = ampPhaseSplineData[nMode]->spline_amp;
1724 gsl_spline *spline_phase = ampPhaseSplineData[nMode]->spline_phi;
1725 gsl_interp_accel *acc_amp = ampPhaseSplineData[nMode]->acc_amp;
1726 gsl_interp_accel *acc_phase = ampPhaseSplineData[nMode]->acc_phi;
1727 // gsl_vector *freq_cmode_hyb = ampPhaseSplineData[nMode]->f;
1728
1729 // Maximum frequency at which we have data for the ROM
1730 REAL8 Mf_max_mode = const_fmax_lm_v5hm[nMode] * Get_omegaQNM_SEOBNRv5(q, chi1, chi2, modeL, modeM) / (2.*LAL_PI);
1731
1732 // Assemble modes from amplitude and phase
1733 for (UINT4 i=0; i<freqs->length; i++) { // loop over frequency points in sequence
1734 REAL8 f = freqs->data[i];
1735 if (f > Mf_max_mode) continue; // We're beyond the highest allowed frequency; since freqs may not be ordered, we'll just skip the current frequency and leave zero in the buffer
1736 if (f <= Mf_low_22 * modeM/2.) continue; // We're above the lowest allowed frequency; since freqs may not be ordered, we'll just skip the current frequency and leave zero in the buffer
1737 int j = i + offset; // shift index for frequency series if needed
1738 REAL8 A = gsl_spline_eval(spline_amp, f, acc_amp);
1739 REAL8 phase = gsl_spline_eval(spline_phase, f, acc_phase);
1740 hlmdata[j] = amp0*A * (cos(phase) + I*sin(phase));//cexp(I*phase);
1741 REAL8 phase_factor = -2.*LAL_PI*f*t_corr;
1742 COMPLEX16 t_factor = cos(phase_factor) + I*sin(phase_factor);
1743 hlmdata[j] *= t_factor;
1744 // We now return the (l,-m) mode that in the LAL convention has support for f > 0
1745 // We use the equation h(l,-m)(f) = (-1)^l h(l,m)*(-f) with f > 0
1746 hlmdata[j] = pow(-1.,modeL)*conj(hlmdata[j]);
1747 if(modeM%2 != 0){
1748 // This is changing the sign of the odd-m modes in the case m1 < m2,
1749 // if m1>m2 sign_odd_modes = 1 and nothing changes.
1750 hlmdata[j] = hlmdata[j]*sign_odd_modes;
1751 }
1752 }
1753 /* Save the mode (l,-m) in the SphHarmFrequencySeries structure */
1754 *hlm_list = XLALSphHarmFrequencySeriesAddMode(*hlm_list, hlmtilde, modeL, -modeM);
1755
1756 /* Cleanup inside of loop over modes */
1759 }
1760
1761 AmpPhaseSplineData_Destroy(ampPhaseSplineData, nModes);
1762
1763 return(XLAL_SUCCESS);
1764}
1765
1766/**
1767 * ModeArray is a structure which allows to select the modes to include
1768 * in the waveform.
1769 * This function will create a structure with the default modes for every model
1770 */
1772 LALValue *ModeArray, UINT4 nModes)
1773{
1774
1775 /* setup ModeArray */
1776
1777 if (nModes == 7) {
1778 /* Adding all the modes of SEOBNRv5HM
1779 * i.e. [(2,2),(2,1),(3,3),(4,4),(5,5),(3,2),(4,3)]
1780 the relative -m modes are added automatically*/
1781 XLALSimInspiralModeArrayActivateMode(ModeArray, 2, -2);
1782 XLALSimInspiralModeArrayActivateMode(ModeArray, 2, -1);
1783 XLALSimInspiralModeArrayActivateMode(ModeArray, 3, -3);
1784 XLALSimInspiralModeArrayActivateMode(ModeArray, 4, -4);
1785 XLALSimInspiralModeArrayActivateMode(ModeArray, 5, -5);
1786 XLALSimInspiralModeArrayActivateMode(ModeArray, 3, -2);
1787 XLALSimInspiralModeArrayActivateMode(ModeArray, 4, -3);
1788 }
1789 else{
1790 /*All the other spin aligned model so far only have the 22 mode
1791 */
1792 XLALSimInspiralModeArrayActivateMode(ModeArray, 2, -2);
1793 }
1794
1795
1796 return XLAL_SUCCESS;
1797}
1798
1799/**
1800 * ModeArray is a structure which allows to select the modes to include
1801 * in the waveform.
1802 * This function check if the selected modes are available
1803 */
1805 LALValue *ModeArray, UINT4 nModes)
1806{
1807 INT4 flagTrue = 0;
1808 UINT4 modeL;
1809 INT4 modeM;
1810 const char *model_name;
1811 if (nModes == 1){
1812 model_name="SEOBNRv5_ROM";
1813 }
1814 else{
1815 model_name="SEOBNRv5HM_ROM";
1816 }
1817 /*Loop over all the possible modes
1818 *we only check -m modes, in the waveform both + and - m
1819 *modes will be included
1820 */
1821 for (UINT4 ELL = 2; ELL <= LAL_SIM_L_MAX_MODE_ARRAY; ELL++) {
1822 for (INT4 EMM = -ELL; EMM <= (INT4)ELL; EMM++) {
1823 if (XLALSimInspiralModeArrayIsModeActive(ModeArray, ELL, EMM) == 1) {
1824 for (UINT4 k = 0; k < nModes; k++) {
1825 modeL = lmModes_v5hm[k][0];
1826 //lmModes_v5hm includes the modes in EOB convention for which hlm(f) is non 0 for f>0
1827 //for this reason we need the - sign here
1828 modeM = -lmModes_v5hm[k][1];
1829 if ((modeL == ELL)&&(modeM == EMM)) {
1830 flagTrue=1;
1831 }
1832 if ((modeL == ELL)&&(modeM == -EMM)) {
1833 flagTrue=2;
1834 }
1835 }
1836 /*For each active mode check if is available for the selected model
1837 */
1838 if (flagTrue == 0) {
1839 XLALPrintError ("Mode (%d,%d) is not available by the model %s\n", ELL,
1840 EMM, model_name);
1841 return XLAL_FAILURE;
1842 }
1843 if (flagTrue == 2) {
1844 XLALPrintError ("Mode (%d,%d) is not available by the model %s.\n"
1845 "In this function you can only select (l,-|m|) modes that are directly modeled in the ROM.\n"
1846 "The (l,+|m|) mode will be automatically added to the waveform using symmetry arguments.\n", ELL,
1847 EMM, model_name);
1848 return XLAL_FAILURE;
1849 }
1850 flagTrue = 0;
1851 }
1852 }
1853 }
1854
1855 return XLAL_SUCCESS;
1856}
1857
1858/**
1859 * This function combines the modes hlm frequencyseries with the sYlm to produce the
1860 * polarizations hplus, hcross.
1861 */
1862// NOTE: azimuthal angle of the observer entering the -2Ylm is pi/2-phi according to LAL conventions
1865 *hplusFS, /**<< Output: frequency series for hplus, already created */
1867 *hcrossFS, /**<< Output: frequency series for hplus, already created */
1868 LALValue *ModeArray, /**<< Input: ModeArray structure with the modes to include */
1870 *hlm, /**<< Input: list with frequency series for each mode hlm */
1871 REAL8 inc, /**<< Input: inclination */
1872 UNUSED REAL8 phi /**<< Input: phase */
1873) {
1874 /* Loop over modes */
1875 SphHarmFrequencySeries *hlms_temp = hlm;
1876 while ( hlms_temp ) {
1877 if (XLALSimInspiralModeArrayIsModeActive(ModeArray, hlms_temp->l, hlms_temp->m) == 1) {
1878 /* Here we check if the mode generated is in the ModeArray structure */
1879 XLALSimAddModeFD(hplusFS, hcrossFS, hlms_temp->mode, inc, LAL_PI/2. - phi,
1880 hlms_temp->l, hlms_temp->m, 1);
1881 }
1882 hlms_temp = hlms_temp->next;
1883 }
1884
1885 return XLAL_SUCCESS;
1886}
1887
1888/*
1889 * Auxiliary function for TF2 phasing HM: phase constants for different modes
1890 */
1891static double GetDeltaphilm(int l, int m)
1892{
1893 if (l==2 && m==2) return 0.;
1894 else if (l==2 && m==1) return LAL_PI/2;
1895 else if (l==3 && m==3) return -LAL_PI/2;
1896 else if (l==4 && m==4) return LAL_PI;
1897 else if (l==5 && m==5) return LAL_PI/2;
1898 else if (l==3 && m==2) return 0;
1899 else if (l==4 && m==3) return -LAL_PI/2;
1900 else XLAL_ERROR(XLAL_EINVAL, "Mode indices (l,m) not recognized.");
1901}
1902/*
1903 * Auxiliary function for TF2 amplitude HM: mode amplitude factors
1904 * Leading Newtonian order + 0.5PN relative spin terms for 21
1905 * Here only the 21 has a 0.5PN spin contribution (important for 0-crossings)
1906 * The leading +-1 or +-i factor is supposed to be treated as a phase
1907 * (see GetDeltaphilm)
1908 * Reference: Eqs (4.17) of arXiv:0810.5336 [Phys.Rev.D79:104023,2009]
1909 * (reference for precessing modes, take iota=0)
1910 */
1911static double GetModeAmpFactor(double eta, double delta, double chi1, double chi2, int l, int m, double v)
1912{
1913 double chis = 1./2 * (chi1 + chi2);
1914 double chia = 1./2 * (chi1 - chi2);
1915 if (l==2 && m==2) return 1.;
1916 else if (l==2 && m==1) return v * (delta/3 - 1./2*v*(chia + delta*chis));
1917 else if (l==3 && m==3) return v * 3./4 * sqrt(15./14) * delta;
1918 else if (l==4 && m==4) return v*v * 8*sqrt(35.)/63 * (1 - 3*eta);
1919 else if (l==5 && m==5) return v*v*v * 625*sqrt(66.)/6336 * delta * (1 - 2*eta);
1920 else if (l==3 && m==2) return v*v * 9./8 *sqrt(5./7) * (8./27 * (-1 + 3*eta));
1921 else if (l==4 && m==3) return v*v*v * 8./9*sqrt(10./7)*81./320 * delta * (-1 + 2*eta);
1922 // LP: look at hybridized 32 and 43 modes to double check
1923 else XLAL_ERROR(XLAL_EINVAL, "Mode indices (l,m) not recognized.");
1924}
1925
1926/*
1927 * Function computing the TaylorF2 phasing for the mode (l,m)
1928 * Arbitrary (set to 0) time and phase constants
1929 * The relative alignment between modes is consistent
1930 * We should evaluate XLALSimInspiralTaylorF2AlignedPhasing once for all modes;
1931 * however, this is presumably negligible for performance.
1932 * Reference for SPA scaling between modes:
1933 * Section III A 2) of arxiv:2003.12079 [Phys. Rev. D 101, 124040 (2020)]
1934 */
1936 double Mtot, // Total mass in solar masses
1937 double q, // Mass-ration m1/m2 >= 1
1938 double chi1, // Dimensionless aligned spin of body 1
1939 double chi2, // Dimensionless aligned spin of body 2
1940 int l, // First mode number l
1941 int m, // Second mode number m
1942 gsl_vector *Mfs, // Input geometric frequencies
1943 gsl_vector **PNphase // Output: TaylorF2 phase at frequencies Mfs
1944) {
1945 XLAL_CHECK(PNphase != NULL, XLAL_EFAULT);
1946 XLAL_CHECK(*PNphase == NULL, XLAL_EFAULT);
1947 *PNphase = gsl_vector_alloc(Mfs->size);
1948
1949 PNPhasingSeries *pn = NULL;
1950 LALDict *extraParams = XLALCreateDict();
1951 // Should we go for max order or set it explicitly?
1953
1954 double m1OverM = q / (1.0+q);
1955 double m2OverM = 1.0 / (1.0+q);
1956 double m1 = Mtot * m1OverM * LAL_MSUN_SI;
1957 double m2 = Mtot * m2OverM * LAL_MSUN_SI;
1958 // This function is a thin wrapper around XLALSimInspiralPNPhasing_F2
1959 XLALSimInspiralTaylorF2AlignedPhasing(&pn, m1, m2, chi1, chi2, extraParams);
1960
1961 /* Constant phase for the mode (l,m) */
1962 double Deltaphilm = GetDeltaphilm(l, m);
1963
1964 /* Note: we rescale the frequency (and v) according to the mode number m */
1965 for (size_t i=0; i < Mfs->size; i++) {
1966 const double Mf = gsl_vector_get(Mfs, i);
1967 const double Mf_m = 2./m * Mf;
1968 const double v = cbrt(LAL_PI * Mf_m);
1969 const double logv = log(v);
1970 const double v2 = v * v;
1971 const double v3 = v * v2;
1972 const double v4 = v * v3;
1973 const double v5 = v * v4;
1974 const double v6 = v * v5;
1975 const double v7 = v * v6;
1976 double phasing = 0.0;
1977
1978 phasing += pn->v[7] * v7;
1979 phasing += (pn->v[6] + pn->vlogv[6] * logv) * v6;
1980 phasing += (pn->v[5] + pn->vlogv[5] * logv) * v5;
1981 phasing += pn->v[4] * v4;
1982 phasing += pn->v[3] * v3;
1983 phasing += pn->v[2] * v2;
1984 phasing += pn->v[1] * v;
1985 phasing += pn->v[0];
1986
1987 /* Leading prefactor v^-5 */
1988 phasing /= v5;
1989
1990 /* Rescaling by m/2 going from the 22 mode to the lm mode */
1991 phasing *= m/2.;
1992
1993 /* Constant phase for the mode (l,m), and pi/4 from the SPA formula */
1994 phasing += Deltaphilm - LAL_PI/4.;
1995
1996 gsl_vector_set(*PNphase, i, phasing);
1997 }
1998
1999 XLALDestroyDict(extraParams);
2000 XLALFree(pn);
2001
2002 return XLAL_SUCCESS;
2003}
2004
2005/*
2006 * Function computing the TaylorF2 amplitude for the mode (l,m)
2007 * In geometric units.
2008 * Reference for SPA scaling between modes:
2009 * Section III A 2) of arxiv:2003.12079 [Phys. Rev. D 101, 124040 (2020)]
2010 */
2012 const double q, // Mass-ration m1/m2 >= 1
2013 const double chi1, // Dimensionless aligned spin of body 1
2014 const double chi2, // Dimensionless aligned spin of body 2
2015 const int l, // First mode number l
2016 const int m, // Second mode number m
2017 gsl_vector *Mfs, // Input geometric frequencies
2018 gsl_vector **PNamp // Output: TaylorF2 amplitude at frequencies Mfs
2019) {
2020
2021 XLAL_CHECK(PNamp != NULL, XLAL_EFAULT);
2022 XLAL_CHECK(*PNamp == NULL, XLAL_EFAULT);
2023 *PNamp = gsl_vector_alloc(Mfs->size);
2024 double eta = q / ((1.+q)*(1.+q));
2025 // Modify delta to be tiny but nonzero for q=1 to avoid problems when hybridizing
2026 // with the ROM modes which do not vanish at q=1. This will make the modes
2027 // which should vanish exactly at q=1 about 15 orders of magnitude smaller than the (2,2) mode.
2028 double delta = (q-1. + DBL_EPSILON) / (1.+q);
2029 /* Prefactor of the Newtonian amplitude FD: A_N = G^2 M^2/r/c^5 pi sqrt(2nu/3) v^(-7/2) */
2030 /* Here in geometric units, simply pi sqrt(2nu/3) */
2031 double ampN = LAL_PI * sqrt(2*eta/3.);
2032
2033 /* Note: we rescale the frequency (and v) according to the mode number m */
2034 for (size_t i=0; i < Mfs->size; i++) {
2035 const double Mf = gsl_vector_get(Mfs, i);
2036 const double Mf_m = 2./m * Mf;
2037 const double v = cbrt(LAL_PI * Mf_m);
2038 const double vm72 = pow(v, -7./2);
2039
2040 /* Take into account 0.5PN (1PN for 22) amplitude of different modes */
2041 double mode_amp_factor = GetModeAmpFactor(eta, delta, chi1, chi2, l, m, v);
2042 double amp = ampN * vm72 * sqrt(2./m) * mode_amp_factor;
2043 gsl_vector_set(*PNamp, i, amp);
2044 }
2045
2046 return XLAL_SUCCESS;
2047}
2048
2049/* Build geom frequency grid suitable for waveform amp/phase interpolation */
2050/* Based on cubic spline error bound for leading-order power-law phase */
2051/* Reference: Section (5.1) of arXiv:1402.4146 [CQG 31 195010, 2014] */
2053 gsl_vector** Mfreq, /* Output: pointer to real vector */
2054 const double Mfmin, /* Starting geometric frequency */
2055 const double Mfmax, /* Ending geometric frequency */
2056 const double q, /* Mass ratio */
2057 const double acc) /* Desired phase interpolation error for inspiral */
2058{
2059 /* Checks */
2060 if (*Mfreq != NULL)
2061 XLAL_ERROR(XLAL_EFAULT, "Input pointer for frequencies is not NULL.");
2062 if (!(Mfmin < Mfmax))
2063 XLAL_ERROR(XLAL_EINVAL, "Input Mf limits do not verify Mfmin < Mfmax.");
2064
2065 double eta = q / ((1.+q) * (1.+q));
2066
2067 /* Inspiral sampling */
2068 /* Dimensionless number so that DeltaMf = Lambda * Mf^(17/12) */
2069 /* Factor 3.8 tuned empirically */
2070 double Lambda = 3.8 * pow(acc * eta, 1./4) * pow(LAL_PI, 5./12);
2071
2072 /* Number of samples with inspiral sampling */
2073 int N = 0;
2074 double Lambdatilde = 0.;
2075 N = 1 + ceil(12./5/Lambda * (pow(Mfmin, -5./12) - pow(Mfmax, -5./12)));
2076 /* Adjust a bit Lambda so that the last point at N_insp-1 is Mfmax */
2077 Lambdatilde = 12./5/(N-1) * (pow(Mfmin, -5./12) - pow(Mfmax, -5./12));
2078
2079 /* Allocate output */
2080 *Mfreq = gsl_vector_alloc(N);
2081
2082 /* Fill in values */
2083 double Mfmin_m512 = pow(Mfmin, -5./12);
2084 for (int i=0; i<N; i++) { /* includes Mfmin and Mfmax, will be readjusted */
2085 (*Mfreq)->data[i] = pow(Mfmin_m512 - 5./12*Lambdatilde*i, -12./5);
2086 }
2087
2088 /* Eliminate rounding errors at both ends */
2089 (*Mfreq)->data[0] = Mfmin;
2090 (*Mfreq)->data[N-1] = Mfmax;
2091
2092 return XLAL_SUCCESS;
2093}
2094
2096 gsl_spline **hyb_spline, /**< Output: spline */
2097 AmpPhaseSplineData *ampPhaseSplineData_for_mode, /**< ROM spline data */
2098 gsl_vector *PN_freq, /**< PN frequency */
2099 gsl_vector *PN_amp, /**< PN amplitude */
2100 double f_hyb_win_lo, /**< hybridization window start */
2101 double f_hyb_win_hi /**< hybridization window end */
2102)
2103{
2104 gsl_vector *ROM_amp_f = NULL;
2105 gsl_vector *ROM_amp = NULL;
2106 spline_to_gsl_vectors(ampPhaseSplineData_for_mode->spline_amp, &ROM_amp_f, &ROM_amp);
2107
2108 // Sanity checks
2109 if ( (gsl_vector_get(PN_freq, 0) > f_hyb_win_lo)
2110 || (gsl_vector_get(PN_freq, PN_freq->size - 1) < f_hyb_win_lo)
2111 || (gsl_vector_get(PN_freq, 0) > f_hyb_win_hi)
2112 || (gsl_vector_get(PN_freq, PN_freq->size - 1) < f_hyb_win_hi) )
2113 XLAL_ERROR(XLAL_EFUNC, "Hybridization window for amplitude is not contained in PN frequencies.");
2114 if ( (gsl_vector_get(ROM_amp_f, 0) > f_hyb_win_lo)
2115 || (gsl_vector_get(ROM_amp_f, ROM_amp_f->size - 1) < f_hyb_win_lo)
2116 || (gsl_vector_get(ROM_amp_f, 0) > f_hyb_win_hi)
2117 || (gsl_vector_get(ROM_amp_f, ROM_amp_f->size - 1) < f_hyb_win_hi) )
2118 XLAL_ERROR(XLAL_EFUNC, "Hybridization window for amplitude is not contained in ROM frequencies.");
2119
2120 // Scale PN amplitude to match ROM amplitude
2121 gsl_spline *PN_amp_spline = gsl_spline_alloc(gsl_interp_cspline, PN_amp->size);
2122 gsl_spline_init(PN_amp_spline, PN_freq->data, PN_amp->data, PN_amp->size);
2123 gsl_interp_accel *acc_PN = gsl_interp_accel_alloc();
2124 gsl_interp_accel *acc_ROM = gsl_interp_accel_alloc();
2125 double PN_amp_i = gsl_spline_eval(PN_amp_spline, f_hyb_win_lo, acc_PN);
2126 double ROM_amp_i = gsl_spline_eval(ampPhaseSplineData_for_mode->spline_amp, f_hyb_win_lo, acc_ROM);
2127 double fac = ROM_amp_i / PN_amp_i;
2128 for (size_t i=0; i < PN_amp->size; i++) {
2129 double A = gsl_vector_get(PN_amp, i);
2130 gsl_vector_set(PN_amp, i, A * fac);
2131 }
2132 gsl_spline_free(PN_amp_spline);
2133 gsl_interp_accel_free(acc_PN);
2134 gsl_interp_accel_free(acc_ROM);
2135
2136 gsl_vector *hyb_freq = NULL;
2137 gsl_vector *hyb_amp = NULL;
2139 &hyb_freq, &hyb_amp,
2140 PN_amp, ROM_amp,
2141 PN_freq, ROM_amp_f,
2142 f_hyb_win_lo, f_hyb_win_hi
2143 );
2144 // Make a spline from the hybridized data
2145 *hyb_spline = gsl_spline_alloc(gsl_interp_cspline, hyb_amp->size);
2146 gsl_spline_init(*hyb_spline, hyb_freq->data, hyb_amp->data, hyb_amp->size);
2147
2148 gsl_vector_free(ROM_amp_f);
2149 gsl_vector_free(ROM_amp);
2150 gsl_vector_free(hyb_freq);
2151 gsl_vector_free(hyb_amp);
2152
2153 return XLAL_SUCCESS;
2154}
2155
2157 gsl_spline **hyb_spline, /**< Output: spline */
2158 REAL8* Deltat_align, /**< Output: time alignment */
2159 REAL8* Deltaphi_align, /**< Output: phase alignment */
2160 AmpPhaseSplineData *ampPhaseSplineData_for_mode, /**< ROM spline data */
2161 gsl_vector *PN_freq, /**< PN frequency */
2162 gsl_vector *PN_phase, /**< PN phase */
2163 double f_hyb_win_lo, /**< hybridization window start */
2164 double f_hyb_win_hi /**< hybridization window end */
2165)
2166{
2167 gsl_vector *ROM_phi_f = NULL;
2168 gsl_vector *ROM_phi = NULL;
2169 spline_to_gsl_vectors(ampPhaseSplineData_for_mode->spline_phi, &ROM_phi_f, &ROM_phi);
2170
2171 // Sanity checks
2172 if ( (gsl_vector_get(PN_freq, 0) > f_hyb_win_lo)
2173 || (gsl_vector_get(PN_freq, PN_freq->size - 1) < f_hyb_win_lo)
2174 || (gsl_vector_get(PN_freq, 0) > f_hyb_win_hi)
2175 || (gsl_vector_get(PN_freq, PN_freq->size - 1) < f_hyb_win_hi) )
2176 XLAL_ERROR(XLAL_EFUNC, "Hybridization window for phase is not contained in PN frequencies.");
2177 if ( (gsl_vector_get(ROM_phi_f, 0) > f_hyb_win_lo)
2178 || (gsl_vector_get(ROM_phi_f, ROM_phi_f->size - 1) < f_hyb_win_lo)
2179 || (gsl_vector_get(ROM_phi_f, 0) > f_hyb_win_hi)
2180 || (gsl_vector_get(ROM_phi_f, ROM_phi_f->size - 1) < f_hyb_win_hi) )
2181 XLAL_ERROR(XLAL_EFUNC, "Hybridization window for phase is not contained in ROM frequencies.");
2182
2183 gsl_vector *hyb_freq = NULL;
2184 gsl_vector *hyb_phase = NULL;
2186 &hyb_freq, &hyb_phase, Deltat_align, Deltaphi_align,
2187 PN_phase, ROM_phi,
2188 PN_freq, ROM_phi_f,
2189 f_hyb_win_lo, f_hyb_win_hi
2190 );
2191
2192 // Make a spline from the hybridized data
2193 *hyb_spline = gsl_spline_alloc(gsl_interp_cspline, hyb_phase->size);
2194 gsl_spline_init(*hyb_spline, hyb_freq->data, hyb_phase->data, hyb_phase->size);
2195
2196 gsl_vector_free(ROM_phi_f);
2197 gsl_vector_free(ROM_phi);
2198 gsl_vector_free(hyb_freq);
2199 gsl_vector_free(hyb_phase);
2200
2201 return XLAL_SUCCESS;
2202}
2203
2205 gsl_spline **hyb_spline, /**< Output: spline */
2206 AmpPhaseSplineData *ampPhaseSplineData_for_mode, /**< ROM spline data */
2207 gsl_vector *PN_freq, /**< PN frequency */
2208 gsl_vector *PN_phase, /**< PN phase */
2209 double f_hyb_win_lo, /**< hybridization window start */
2210 double f_hyb_win_hi, /**< hybridization window end */
2211 REAL8 Deltat_22_align, /**< Input: 22 time alignment */
2212 REAL8 Deltaphi_22_align, /**< Input: 22 phase alignment */
2213 INT4 modeM /**< Input: m mode number */
2214)
2215{
2216 gsl_vector *ROM_phi_f = NULL;
2217 gsl_vector *ROM_phi = NULL;
2218 spline_to_gsl_vectors(ampPhaseSplineData_for_mode->spline_phi, &ROM_phi_f, &ROM_phi);
2219
2220 // Sanity checks
2221 if ( (gsl_vector_get(PN_freq, 0) > f_hyb_win_lo)
2222 || (gsl_vector_get(PN_freq, PN_freq->size - 1) < f_hyb_win_lo)
2223 || (gsl_vector_get(PN_freq, 0) > f_hyb_win_hi)
2224 || (gsl_vector_get(PN_freq, PN_freq->size - 1) < f_hyb_win_hi) )
2225 XLAL_ERROR(XLAL_EFUNC, "Hybridization window for phase is not contained in PN frequencies.");
2226 if ( (gsl_vector_get(ROM_phi_f, 0) > f_hyb_win_lo)
2227 || (gsl_vector_get(ROM_phi_f, ROM_phi_f->size - 1) < f_hyb_win_lo)
2228 || (gsl_vector_get(ROM_phi_f, 0) > f_hyb_win_hi)
2229 || (gsl_vector_get(ROM_phi_f, ROM_phi_f->size - 1) < f_hyb_win_hi) )
2230 XLAL_ERROR(XLAL_EFUNC, "Hybridization window for phase is not contained in ROM frequencies.");
2231
2232 gsl_vector *hyb_freq = NULL;
2233 gsl_vector *hyb_phase = NULL;
2235 &hyb_freq, &hyb_phase,
2236 PN_phase, ROM_phi,
2237 PN_freq, ROM_phi_f,
2238 f_hyb_win_lo, f_hyb_win_hi,
2239 Deltat_22_align, Deltaphi_22_align, modeM
2240 );
2241
2242 // Make a spline from the hybridized data
2243 *hyb_spline = gsl_spline_alloc(gsl_interp_cspline, hyb_phase->size);
2244 gsl_spline_init(*hyb_spline, hyb_freq->data, hyb_phase->data, hyb_phase->size);
2245
2246 gsl_vector_free(ROM_phi_f);
2247 gsl_vector_free(ROM_phi);
2248 gsl_vector_free(hyb_freq);
2249 gsl_vector_free(hyb_phase);
2250
2251 return XLAL_SUCCESS;
2252}
2253
2254
2255// Compute spherical harmonic modes similar to SEOBNRv5HMROMCoreModes() but here the ROM modes
2256// are hybridized with PN in the early inspiral.
2258 UNUSED SphHarmFrequencySeries **hlm_list, /**<< Output: Spherical modes frequency series for the waveform */
2259 UNUSED REAL8 phiRef, /**<< orbital reference phase */
2260 UNUSED REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
2261 UNUSED REAL8 distance, /**< Distance of source (m) */
2262 UNUSED REAL8 Mtot_sec, /**< Total mass in seconds **/
2263 UNUSED REAL8 q, /**< Mass ratio **/
2264 UNUSED REAL8 chi1, /**< Dimensionless aligned component spin 1 */
2265 UNUSED REAL8 chi2, /**< Dimensionless aligned component spin 2 */
2266 UNUSED const REAL8Sequence *freqs_in, /**< Frequency points at which to evaluate the waveform (Hz) */
2267 UNUSED REAL8 deltaF, /**< Sampling frequency (Hz).
2268 * If deltaF > 0, the frequency points given in freqs are uniformly spaced with
2269 * spacing deltaF. Otherwise, the frequency points can be spaced non-uniformly.
2270 * Then we will use deltaF = 0 to create the frequency series we return. */
2271 UNUSED INT4 nk_max, /**< truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1. We
2272 *nk_max == -1 is the default setting */
2273 UNUSED UINT4 nModes, /**< Number of modes to generate */
2274 REAL8 sign_odd_modes, /**< Sign of the odd-m modes, used when swapping the two bodies */
2275 UNUSED SEOBNRROMdataDS *romdataset, /**< Dataset for the 22 or HM ROM */
2276 LALDict *LALParams, /**< Additional lal parameters including the tidal deformability*/
2277 NRTidal_version_type NRTidal_version /**< only NRTidalv3_V or NoNRT_V in case of BBH baseline */
2278)
2279{
2280 /* Find frequency bounds */
2281 if (!freqs_in) XLAL_ERROR(XLAL_EFAULT);
2282 REAL8 fLow = freqs_in->data[0];
2283 REAL8 fHigh = freqs_in->data[freqs_in->length - 1];
2284
2285 REAL8 fLow_geom = fLow * Mtot_sec;
2286 REAL8 fHigh_geom = fHigh * Mtot_sec;
2287 REAL8 deltaF_geom = deltaF * Mtot_sec;
2288
2289 // The maximum available frequency is the one associated with the 55 mode
2290 REAL8 Mf_ROM_max = const_fmax_lm_v5hm[4] * Get_omegaQNM_SEOBNRv5(q, chi1, chi2, 5, 5) / (2.*LAL_PI);
2291
2292 if (fHigh_geom == 0)
2293 fHigh_geom = Mf_ROM_max;
2294 // Enforce allowed geometric frequency range
2295 // ROM starting frequency
2296 // Note: Mf_low_55 is 5/2 times Mf_low_22 and about 1e-3; this is the shortest mode
2297 // (Could make a distinction of whether we just want the 22 mode or all the modes.)
2298 if (fLow_geom <= Mf_low_22)
2299 XLAL_PRINT_INFO("Starting frequency %g is smaller than (or equal to) ROM starting frequency for (2,2) mode %g!\nUsing hybridization with TaylorF2.\n", fLow_geom, Mf_low_22);
2300 if (fLow_geom <= Mf_low_55)
2301 XLAL_PRINT_INFO("Starting frequency %g is smaller than (or equal to) ROM starting frequency for (5,5) mode %g!\nUsing hybridization with TaylorF2.\n", fLow_geom, Mf_low_55);
2302 if (fLow_geom <= 0.0) {
2303 XLALPrintError("XLAL Error - %s: Starting frequency in REAL8Sequence must be positive.\n", __func__);
2305 }
2306 // Get the splines for the amplitude and phase of the modes
2307 AmpPhaseSplineData **ampPhaseSplineData = NULL;
2308 AmpPhaseSplineData_Init(&ampPhaseSplineData, nModes);
2309 UNUSED int retcode = SEOBNRv5HMROMCoreModeAmpPhaseSplines(
2310 ampPhaseSplineData, q, chi1, chi2, nk_max, nModes,romdataset);
2311 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
2312
2313 // Safety factors for defining the hybridization window
2314 // See the definition of f_hyb_win_lo and f_hyb_win_hi below.
2315 const double f_hyb_win_lo_fac = 1.01; // Slightly above 1, so that f_hyb_win_lo will be slightly above Mf_low_lm_v5hm[k] to avoid issues with floating point comparisons
2316 const double f_hyb_win_hi_fac = 2.0;
2317 // Empirically this factor of 2 is a good choice for the length of the hybridization window with PN.
2318 // If the window is too short then there may be an abrupt change in the slope of the phase.
2319 // If the window is too wide then the linear fit will be thrown off by nonlinear contributions.
2320 // For hybridization we need the PN frequencies to contain [Mf_low_21 * f_hyb_win_lo_fac, Mf_low_55 * f_hyb_win_hi_fac] to be safe for all modes.
2321
2322 /* Generate PN amplitudes and phases */
2323 double Mtot = Mtot_sec / LAL_MTSUN_SI;
2324
2325 double PN_Mf_low = fmin(fLow_geom / 2.0, Mf_low_21); // Start at half of the starting frequency to avoid gsl interpolation errors in the (2,1) mode; at least starting frequency of the 22 mode.
2326 double PN_Mf_high = 1.1 * f_hyb_win_hi_fac * Mf_low_55; // to have some overlap with the shortest ROM mode
2327
2328 gsl_vector *Mfs = NULL;
2329 const double acc = 1e-4; // Hardcoded spline interpolation accuracy target
2330 retcode = BuildInspiralGeomFrequencyGrid(&Mfs, PN_Mf_low, PN_Mf_high, q, acc);
2331 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
2332
2333 // Arrays for storing amplitude and phase splines for each mode
2334 gsl_spline *hybrid_spline_amp[nModes];
2335 gsl_spline *hybrid_spline_phi[nModes];
2336 for (UINT4 i=0; i < nModes; i++) {
2337 hybrid_spline_amp[i] = NULL;
2338 hybrid_spline_phi[i] = NULL;
2339 }
2340
2341 // Alignment constants, time and phase shift
2342 // Computed once for (2,-2), then propagated to other modes (with possible pi)
2343 REAL8 Deltat_22_align = 0.;
2344 REAL8 Deltaphi_22_align = 0.;
2345
2346 // Loop over all modes, hybridize, and compute modes
2347 // For the amplitude hybridize_ROM_with_PN_amplitude() will make them continuous, but the slope will be wrong
2348 for (UINT4 k=0; k < nModes; k++) {
2349 double f_hyb_win_lo = Mf_low_lm_v5hm[k] * f_hyb_win_lo_fac;
2350 double f_hyb_win_hi = Mf_low_lm_v5hm[k] * f_hyb_win_hi_fac;
2351 if (k==0)
2352 XLALPrintInfo("%s : SEOBNRv5HM_ROM hybridization window for (2,2) mode: Mf in [%g, %g]\n",
2353 __func__, f_hyb_win_lo, f_hyb_win_hi);
2354 int modeL = lmModes_v5hm[k][0];
2355 int modeM = lmModes_v5hm[k][1];
2356
2357 gsl_vector *PNamp = NULL;
2358 gsl_vector *PNphase = NULL;
2359
2360 // We get q >= 1 which is consistent with the TF2 wrapper functions
2361 retcode |= TaylorF2Amplitude(q, chi1, chi2, modeL, modeM, Mfs, &PNamp);
2362 retcode |= TaylorF2Phasing(Mtot, q, chi1, chi2, modeL, modeM, Mfs, &PNphase);
2363 if(retcode != 0) {
2364 gsl_vector_free(PNamp);
2365 gsl_vector_free(PNphase);
2366 gsl_vector_free(Mfs);
2367 XLAL_ERROR(retcode);
2368 }
2369
2370 // Hybridize phase
2371 // Distinguish (2,2) (time and phase alignment output) from others (input)
2372 // NOTE: sensitive to the ordering of the modes ! We check k=0 is (2,-2)
2373 // NOTE: convention in error message, (2,2) or (2,-2) ?
2374 if (k==0) {
2375 if (!(modeL==2 && modeM==2)) {
2376 XLALPrintError ("The first mode in the loop must be (2,2).\n");
2378 }
2379 // Hybridize (2,-2) and output time and phase shift
2381 &hybrid_spline_phi[k],
2382 &Deltat_22_align,
2383 &Deltaphi_22_align,
2384 ampPhaseSplineData[k],
2385 Mfs, PNphase,
2386 f_hyb_win_lo, f_hyb_win_hi
2387 );
2388 }
2389 else {
2390 // Hybridize with time and phase shift determined from (2,-2) as input
2392 &hybrid_spline_phi[k],
2393 ampPhaseSplineData[k],
2394 Mfs, PNphase,
2395 f_hyb_win_lo, f_hyb_win_hi,
2396 Deltat_22_align, Deltaphi_22_align, modeM
2397 );
2398 }
2399
2400 // Hybridize amplitude
2402 &hybrid_spline_amp[k],
2403 ampPhaseSplineData[k],
2404 Mfs, PNamp,
2405 f_hyb_win_lo, f_hyb_win_hi
2406 );
2407
2408 // Set up accelerators for spline evaluation in the loop below
2409 gsl_interp_accel *acc_amp = gsl_interp_accel_alloc();
2410 gsl_interp_accel *acc_phase = gsl_interp_accel_alloc();
2411
2412 // Set up data structure for current mode for Nyquist or user specified frequency grids
2413 size_t npts = 0;
2414 LIGOTimeGPS tC = {0, 0};
2415 UINT4 offset = 0; // Index shift between freqs and the frequency series
2416 REAL8Sequence *freqs = NULL;
2417 REAL8Sequence *amp_tidal = NULL; /* Tidal amplitude series; for SEOBNRv5_ROM_NRTidalv3 */
2418 COMPLEX16FrequencySeries *hlmtilde = NULL;
2419
2420 if (deltaF > 0) {
2421 // uniform frequency grid with spacing deltaF; we start at frequency 0
2422
2423 npts = NextPow2(fHigh_geom / deltaF_geom) + 1;
2424 if (fHigh_geom < fHigh * Mtot_sec) /* Resize waveform if user wants f_max larger than cutoff frequency */
2425 npts = NextPow2(fHigh * Mtot_sec / deltaF_geom) + 1;
2426 XLALGPSAdd(&tC, -1. / deltaF); /* coalesce at t=0 */
2427
2428 // Allocate space for the current mode
2430 "hlmtilde: FD mode", &tC, 0.0, deltaF, &lalStrainUnit, npts);
2431
2432 // Recreate freqs using only the lower and upper bounds
2433 // Use fLow, fHigh and deltaF rather than geometric frequencies for numerical accuracy
2434 double fHigh_temp = fHigh_geom / Mtot_sec;
2435 UINT4 iStart = (UINT4) ceil(fLow / deltaF);
2436 UINT4 iStop = (UINT4) ceil(fHigh_temp / deltaF);
2437 freqs = XLALCreateREAL8Sequence(iStop - iStart);
2438 if (!freqs) XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
2439 for (UINT4 i=iStart; i<iStop; i++)
2440 freqs->data[i-iStart] = i*deltaF_geom;
2441
2442 offset = iStart;
2443 } else {
2444 // freqs contains frequencies with non-uniform spacing; we start at lowest given frequency
2445 npts = freqs_in->length;
2446
2447 // Allocate space for the current mode
2449 "hlmtilde: FD mode", &tC, fLow, deltaF, &lalStrainUnit, npts);
2450
2451 freqs = XLALCreateREAL8Sequence(freqs_in->length);
2452 if (!freqs) XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
2453 for (UINT4 i=0; i<freqs_in->length; i++)
2454 freqs->data[i] = freqs_in->data[i] * Mtot_sec;
2455
2456 offset = 0;
2457 }
2458
2459 memset(hlmtilde->data->data, 0, npts * sizeof(COMPLEX16));
2460 COMPLEX16 *hlmdata=hlmtilde->data->data;
2461
2462 // Correct overall amplitude to undo mass-dependent scaling used in ROM
2463 double amp0 = Mtot * Mtot_sec * LAL_MRSUN_SI / (distance);
2464
2465 // The ROM was build with t = 0 at t_peak^22 -1000.
2466 // To agree with the LAL convention, we undo this shift to get t_peak^22 = 0.
2467 double t_corr = 1000.0;
2468
2469 // Maximum frequency at which we have data for the ROM
2470 double Mf_max_mode = const_fmax_lm_v5hm[k] * Get_omegaQNM_SEOBNRv5(
2471 q, chi1, chi2, modeL, modeM) / (2.0*LAL_PI);
2472
2473
2474 int ret = XLAL_SUCCESS;
2475 if (NRTidal_version == NRTidalv3_V && k == 0) { // k == 0 is the 22-mode, where NRTidalv3 is applicable
2476 const REAL8 sh = 4.0*sqrt(5.0/64.0/LAL_PI); // polarization factor for Y_{22} spherical harmonic mode
2477 /* get component masses (in solar masses) from mtotal and eta! */
2478 const REAL8 m1 = Mtot*q/(1 + q);
2479 const REAL8 m2 = Mtot/(1 + q);
2480 /* Generate the tidal amplitude (Eq. 24 of arxiv: 1905.06011) to add to BBH baseline; only for NRTidalv3 */
2481 amp_tidal = XLALCreateREAL8Sequence(freqs->length);
2484
2485 ret = XLALSimNRTunedTidesFDTidalAmplitudeFrequencySeries(amp_tidal, freqs, m1, m2, l1, l2);
2486 XLAL_CHECK(XLAL_SUCCESS == ret, ret, "Failed to generate tidal amplitude series to construct SEOBNRv5_ROM_NRTidalv3 waveform.");
2487 /* Generated tidal amplitude corrections */
2488 for (UINT4 i=0; i<freqs->length; i++) { // loop over frequency points in sequence
2489 REAL8 f = freqs->data[i];
2490 REAL8 ampT = amp_tidal->data[i];
2491
2492 if (f > Mf_max_mode) continue; // We're beyond the highest allowed frequency; since freqs may not be ordered, we'll just skip the current frequency and leave zero in the buffer
2493 int j = i + offset; // shift index for frequency series if needed
2494
2495 // Assemble mode from amplitude and phase
2496 REAL8 A = gsl_spline_eval(hybrid_spline_amp[k], f, acc_amp);
2497 REAL8 phase = gsl_spline_eval(hybrid_spline_phi[k], f, acc_phase);
2498 hlmdata[j] = amp0*(A+ampT/sh) * (cos(phase) + I*sin(phase));
2499 REAL8 phase_factor = -2.0*LAL_PI * f * t_corr;
2500 COMPLEX16 t_factor = cos(phase_factor) + I*sin(phase_factor);
2501 hlmdata[j] *= t_factor;
2502 // We now return the (l,-m) mode that in the LAL convention has support for f > 0
2503 // We use the equation h(l,-m)(f) = (-1)^l h(l,m)*(-f) with f > 0
2504 hlmdata[j] = pow(-1.0, modeL) * conj(hlmdata[j]);
2505 if(modeM%2 != 0){
2506 // This is changing the sign of the odd-m modes in the case m1 < m2.
2507 // If m1>m2 sign_odd_modes = 1 and nothing changes
2508 hlmdata[j] = hlmdata[j] * sign_odd_modes;
2509 }
2510 }
2511 }
2512 else {
2513 // Loop over frequency points in sequence
2514 for (UINT4 i=0; i < freqs->length; i++) {
2515 double f = freqs->data[i];
2516 if (f > Mf_max_mode) continue; // We're beyond the highest allowed frequency; since freqs may not be ordered, we'll just skip the current frequency and leave zero in the buffer
2517 int j = i + offset; // shift index for frequency series if needed
2518
2519 // Assemble mode from amplitude and phase
2520 REAL8 A = gsl_spline_eval(hybrid_spline_amp[k], f, acc_amp);
2521 REAL8 phase = gsl_spline_eval(hybrid_spline_phi[k], f, acc_phase);
2522 hlmdata[j] = amp0*A * (cos(phase) + I*sin(phase));
2523 REAL8 phase_factor = -2.0*LAL_PI * f * t_corr;
2524 COMPLEX16 t_factor = cos(phase_factor) + I*sin(phase_factor);
2525 hlmdata[j] *= t_factor;
2526 // We now return the (l,-m) mode that in the LAL convention has support for f > 0
2527 // We use the equation h(l,-m)(f) = (-1)^l h(l,m)*(-f) with f > 0
2528 hlmdata[j] = pow(-1.0, modeL) * conj(hlmdata[j]);
2529 if(modeM%2 != 0){
2530 // This is changing the sign of the odd-m modes in the case m1 < m2.
2531 // If m1>m2 sign_odd_modes = 1 and nothing changes
2532 hlmdata[j] = hlmdata[j] * sign_odd_modes;
2533 }
2534 }
2535 }
2536 /* Save the mode (l,-m) in the SphHarmFrequencySeries structure */
2537 *hlm_list = XLALSphHarmFrequencySeriesAddMode(*hlm_list, hlmtilde, modeL, -modeM);
2538
2539 // Cleanup
2540 XLALDestroyREAL8Sequence(amp_tidal);
2542 gsl_interp_accel_free(acc_amp);
2543 gsl_interp_accel_free(acc_phase);
2544 gsl_vector_free(PNamp);
2545 gsl_vector_free(PNphase);
2547 }
2548
2549 // Cleanup outside of loop
2550 gsl_vector_free(Mfs);
2551 AmpPhaseSplineData_Destroy(ampPhaseSplineData, nModes);
2552
2553 for (UINT4 k=0; k < nModes; k++) {
2554 gsl_spline_free(hybrid_spline_amp[k]);
2555 gsl_spline_free(hybrid_spline_phi[k]);
2556 }
2557 return(XLAL_SUCCESS);
2558}
2559
2560/**
2561 * @addtogroup LALSimIMRSEOBNRv5HMROM_c
2562 *
2563 * \author Lorenzo Pompili, Roberto Cotesta, Sylvain Marsat, Michael Puerrer
2564 *
2565 * \brief C code for SEOBNRv5HM reduced order model *
2566 *
2567 * This is a frequency domain model that approximates the time domain SEOBNRv5HM model
2568 * described in Phys.Rev.D 108 (2023) 12, 124035, https://arxiv.org/abs/2303.18039.
2569 *
2570 * The binary data files (SEOBNRv5ROM_v1.0.hdf5, SEOBNRv5HMROM_v1.0.hdf5)
2571 * are available at:
2572 * https://git.ligo.org/waveforms/software/lalsuite-waveform-data
2573 * and on Zenodo at: https://zenodo.org/records/14999310.
2574 * Get the lalsuite-waveform-data repo or put the data into a location in your
2575 * LAL_DATA_PATH.
2576 * The data is also available on CIT at /home/lalsimulation_data and via CVMFS
2577 * at /cvmfs/shared.storage.igwn.org/igwn/shared/auxiliary/obs_sci/cbc/waveform/lalsimulation_data
2578 * Make sure the files are in your LAL_DATA_PATH.
2579 * Data can also be downloaded from https://dcc.ligo.org/LIGO-T2300128, https://dcc.ligo.org/LIGO-T2400174.
2580 *
2581 * @note Parameter ranges:
2582 * * 1 <= q <= 100
2583 * * -0.998 <= chi_i <= 0.998
2584 * * no limitations on f_low as the mode is hybridized with Taylor F2 at low frequencies
2585 *
2586 * Aligned component spins chi1, chi2.
2587 * Mass-ratio q = m1/m2 >= 1
2588 *
2589 *
2590 * @{
2591 */
2592/**
2593 * Compute waveform in LAL format for the SEOBNRv5HM_ROM model.
2594 *
2595 * Returns the plus and cross polarizations as a complex frequency series with
2596 * equal spacing deltaF and contains zeros from zero frequency to the starting
2597 * frequency fLow and zeros beyond the cutoff frequency in the ringdown.
2598 */
2600 UNUSED struct tagCOMPLEX16FrequencySeries **hptilde, /**< Output: Frequency-domain waveform h+ */
2601 UNUSED struct tagCOMPLEX16FrequencySeries **hctilde, /**< Output: Frequency-domain waveform hx */
2602 UNUSED REAL8 phiRef, /**< Phase at reference time */
2603 UNUSED REAL8 deltaF, /**< Sampling frequency (Hz) */
2604 REAL8 fLow, /**< Starting GW frequency (Hz) */
2605 UNUSED REAL8 fHigh, /**< End frequency */
2606 UNUSED REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
2607 UNUSED REAL8 distance, /**< Distance of source (m) */
2608 UNUSED REAL8 inclination, /**< Inclination of source (rad) */
2609 REAL8 m1SI, /**< Mass of companion 1 (kg) */
2610 REAL8 m2SI, /**< Mass of companion 2 (kg) */
2611 REAL8 chi1, /**< Dimensionless aligned component spin 1 */
2612 REAL8 chi2, /**< Dimensionless aligned component spin 2 */
2613 UNUSED INT4 nk_max, /**< Truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
2614 UNUSED UINT4 nModes, /**< Number of modes to use. This should be 1 for SEOBNRv5_ROM and 7 for SEOBNRv5HM_ROM */
2615 bool use_hybridization, /**< Whether the ROM should be hybridized */
2616 LALDict *LALParams, /**< Additional lal parameters including the tidal deformability*/
2617 NRTidal_version_type NRTidal_version /**< only NRTidalv3_V or NoNRT_V in case of BBH baseline */
2618)
2619{
2620 REAL8 sign_odd_modes = 1.;
2621 /* Internally we need m1 > m2, so change around if this is not the case */
2622 if (m1SI < m2SI) {
2623 // Swap m1 and m2
2624 REAL8 m1temp = m1SI;
2625 REAL8 chi1temp = chi1;
2626 m1SI = m2SI;
2627 chi1 = chi2;
2628 m2SI = m1temp;
2629 chi2 = chi1temp;
2630 sign_odd_modes = -1.; // When we swap m1 with m2 odd-m modes should get - sign because they depend on dm
2631 }
2632
2633 /* Get masses in terms of solar mass */
2634 REAL8 mass1 = m1SI / LAL_MSUN_SI;
2635 REAL8 mass2 = m2SI / LAL_MSUN_SI;
2636 REAL8 Mtot = mass1+mass2;
2637 REAL8 q = mass1/mass2;
2638 UNUSED REAL8 Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
2639
2640 /* Select modes to include in the waveform */
2641 LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(LALParams);
2642 //ModeArray includes the modes chosen by the user
2643 if (ModeArray == NULL) {
2644 //If the user doesn't choose the modes, use the standard modes
2645 ModeArray = XLALSimInspiralCreateModeArray();
2647 }
2648 //Check that the modes chosen are available for the model
2650 XLALPrintError ("Not available mode chosen.\n");
2652 }
2653
2654 // Use fLow, fHigh, deltaF to compute freqs sequence
2655 // Instead of building a full sequency we only transfer the boundaries and let
2656 // the internal core function do the rest (and properly take care of corner cases).
2658 freqs->data[0] = fLow;
2659 freqs->data[1] = fHigh;
2660
2661 SEOBNRROMdataDS *romdataset;
2662
2663 /* Load ROM data if not loaded already */
2664#ifdef LAL_PTHREAD_LOCK
2665 if (nModes == 1){
2666 romdataset=__lalsim_SEOBNRv5ROMDS_data;
2667 (void) pthread_once(&SEOBNRv5ROM_is_initialized, SEOBNRv5ROM_Init_LALDATA);
2668 }
2669 else{
2671 (void) pthread_once(&SEOBNRv5HMROM_is_initialized, SEOBNRv5HMROM_Init_LALDATA);
2672 }
2673#else
2674 if (nModes == 1){
2676 }
2677 else{
2679 }
2680#endif
2681 SphHarmFrequencySeries *hlm = NULL;
2682
2683 /* Generate modes */
2684 UINT8 retcode = 0;
2685 if (use_hybridization) {
2686 retcode = SEOBNRv5HMROMCoreModesHybridized(&hlm,
2687 phiRef, fRef, distance, Mtot_sec, q, chi1, chi2, freqs, deltaF, nk_max,
2688 nModes, sign_odd_modes, romdataset, LALParams, NRTidal_version);
2689 }
2690 else {
2691 retcode = SEOBNRv5HMROMCoreModes(&hlm,
2692 phiRef, fRef, distance, Mtot_sec, q, chi1, chi2, freqs, deltaF, nk_max,
2693 nModes, sign_odd_modes, romdataset);
2694 }
2695 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
2696
2697
2698 /* GPS time for output frequency series and modes */
2700 LIGOTimeGPS tGPS = hlm_for_hphc->epoch;
2701 size_t npts = hlm_for_hphc->data->length;
2702
2703 /* Create output frequencyseries for hplus, hcross */
2704 UNUSED COMPLEX16FrequencySeries *hPlustilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &tGPS, 0.0, deltaF,&lalStrainUnit, npts);
2705 UNUSED COMPLEX16FrequencySeries *hCrosstilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &tGPS, 0.0, deltaF,&lalStrainUnit, npts);
2706 memset(hPlustilde->data->data, 0, npts * sizeof(COMPLEX16));
2707 memset(hCrosstilde->data->data, 0, npts * sizeof(COMPLEX16));
2708 XLALUnitMultiply(&hPlustilde->sampleUnits, &hPlustilde->sampleUnits, &lalSecondUnit);
2709 XLALUnitMultiply(&hCrosstilde->sampleUnits, &hCrosstilde->sampleUnits, &lalSecondUnit);
2710 retcode = SEOBROMComputehplushcrossFromhlm(hPlustilde,hCrosstilde,ModeArray,hlm,inclination,phiRef);
2711 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
2712
2713 /* Point the output pointers to the relevant time series and return */
2714 (*hptilde) = hPlustilde;
2715 (*hctilde) = hCrosstilde;
2716
2719 XLALDestroyValue(ModeArray);
2720
2721 // This is a debug enviromental variable, when activated is deleting the ROM dataset from memory
2722 const char* s = getenv("FREE_MEMORY_SEOBNRv5HMROM");
2723 if (s!=NULL){
2724 for(unsigned int index_mode = 0; index_mode < nModes;index_mode++){
2725 SEOBNRROMdataDS_Cleanup(&romdataset[index_mode]);
2726 }
2727 }
2728
2729 return(XLAL_SUCCESS);
2730}
2731
2732/**
2733* Compute waveform polarizations at user specified frequencies for the SEOBNRv5HM_ROM model
2734*
2735* This function is designed as an entry point for reduced order quadratures.
2736* Since ROQs are most effective for long waveforms the hybridized ROM is used.
2737*
2738*/
2740 UNUSED struct tagCOMPLEX16FrequencySeries **hptilde, /**< Output: Frequency-domain waveform h+ */
2741 UNUSED struct tagCOMPLEX16FrequencySeries **hctilde, /**< Output: Frequency-domain waveform hx */
2742 const REAL8Sequence *freqs, /**< Frequency points at which to evaluate the waveform (Hz) */
2743 UNUSED REAL8 phiRef, /**< Phase at reference time */
2744 UNUSED REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
2745 UNUSED REAL8 distance, /**< Distance of source (m) */
2746 UNUSED REAL8 inclination, /**< Inclination of source (rad) */
2747 REAL8 m1SI, /**< Mass of companion 1 (kg) */
2748 REAL8 m2SI, /**< Mass of companion 2 (kg) */
2749 REAL8 chi1, /**< Dimensionless aligned component spin 1 */
2750 REAL8 chi2, /**< Dimensionless aligned component spin 2 */
2751 UNUSED INT4 nk_max, /**< Truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
2752 UNUSED UINT4 nModes, /**< Number of modes to use. This should be 1 for SEOBNRv5_ROM and 7 for SEOBNRv5HM_ROM */
2753 LALDict *LALParams, /**< Additional lal parameters including the tidal deformability*/
2754 NRTidal_version_type NRTidal_version /**< only NRTidalv3_V or NoNRT_V in case of BBH baseline */
2755)
2756{
2757 REAL8 sign_odd_modes = 1.;
2758 /* Internally we need m1 > m2, so change around if this is not the case */
2759 if (m1SI < m2SI) {
2760 // Swap m1 and m2
2761 REAL8 m1temp = m1SI;
2762 REAL8 chi1temp = chi1;
2763 m1SI = m2SI;
2764 chi1 = chi2;
2765 m2SI = m1temp;
2766 chi2 = chi1temp;
2767 sign_odd_modes = -1.; // When we swap m1 with m2 odd-m modes should get - sign because they depend on dm
2768 }
2769
2770 /* Get masses in terms of solar mass */
2771 REAL8 mass1 = m1SI / LAL_MSUN_SI;
2772 REAL8 mass2 = m2SI / LAL_MSUN_SI;
2773 REAL8 Mtot = mass1+mass2;
2774 REAL8 q = mass1/mass2;
2775 UNUSED REAL8 Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
2776
2777 /* Select modes to include in the waveform */
2778 LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(LALParams);
2779 //ModeArray includes the modes chosen by the user
2780 if (ModeArray == NULL) {
2781 //If the user doesn't choose the modes, use the standard modes
2782 ModeArray = XLALSimInspiralCreateModeArray();
2784 }
2785 //Check that the modes chosen are available for the model
2787 XLALPrintError ("Not available mode chosen.\n");
2789 }
2790
2791 SEOBNRROMdataDS *romdataset;
2792
2793 /* Load ROM data if not loaded already */
2794#ifdef LAL_PTHREAD_LOCK
2795 if (nModes == 1){
2796 romdataset=__lalsim_SEOBNRv5ROMDS_data;
2797 (void) pthread_once(&SEOBNRv5ROM_is_initialized, SEOBNRv5ROM_Init_LALDATA);
2798 }
2799 else{
2801 (void) pthread_once(&SEOBNRv5HMROM_is_initialized, SEOBNRv5HMROM_Init_LALDATA);
2802 }
2803#else
2804 if (nModes == 1){
2806 }
2807 else{
2809 }
2810#endif
2811 SphHarmFrequencySeries *hlm = NULL;
2812
2813 // Call the internal core function with deltaF = 0 to indicate that freqs is non-uniformly
2814 // spaced and we want the strain only at these frequencies
2815 // The FS interface is only implemented for the hybridized ROM
2816 double deltaF = 0.0;
2817
2818 /* Generate modes */
2819 UINT8 retcode = 0;
2820 retcode = SEOBNRv5HMROMCoreModesHybridized(&hlm,
2821 phiRef, fRef, distance, Mtot_sec, q, chi1, chi2, freqs, deltaF, nk_max,
2822 nModes, sign_odd_modes, romdataset, LALParams, NRTidal_version);
2823 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
2824
2825
2826 /* GPS time for output frequency series and modes */
2828 LIGOTimeGPS tGPS = hlm_for_hphc->epoch;
2829 size_t npts = hlm_for_hphc->data->length;
2830
2831 double fLow = freqs->data[0];
2832 /* Create output frequencyseries for hplus, hcross */
2834 "hptilde: FD waveform", &tGPS, fLow, deltaF, &lalStrainUnit, npts);
2836 "hctilde: FD waveform", &tGPS, fLow, deltaF, &lalStrainUnit, npts);
2837 memset(hPlustilde->data->data, 0, npts * sizeof(COMPLEX16));
2838 memset(hCrosstilde->data->data, 0, npts * sizeof(COMPLEX16));
2839 XLALUnitDivide(&hPlustilde->sampleUnits, &hPlustilde->sampleUnits, &lalSecondUnit);
2840 XLALUnitDivide(&hCrosstilde->sampleUnits, &hCrosstilde->sampleUnits, &lalSecondUnit);
2841 retcode = SEOBROMComputehplushcrossFromhlm(hPlustilde, hCrosstilde, ModeArray, hlm, inclination, phiRef);
2842 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
2843
2844 /* Point the output pointers to the relevant time series and return */
2845 (*hptilde) = hPlustilde;
2846 (*hctilde) = hCrosstilde;
2847
2849 XLALDestroyValue(ModeArray);
2850
2851 // This is a debug enviromental variable; when activated it deletes the ROM dataset from memory.
2852 const char* s = getenv("FREE_MEMORY_SEOBNRv5HMROM");
2853 if (s!=NULL){
2854 for(unsigned int index_mode = 0; index_mode < nModes;index_mode++){
2855 SEOBNRROMdataDS_Cleanup(&romdataset[index_mode]);
2856 }
2857 }
2858
2859 return(XLAL_SUCCESS);
2860}
2861
2862/**
2863 * Compute modes in LAL format for the SEOBNRv5HM_ROM model.
2864 *
2865 * Returns hlm modes as a SphHarmFrequencySeries with
2866 * equal spacing deltaF and contains zeros from zero frequency to the starting
2867 * frequency fLow and zeros beyond the cutoff frequency in the ringdown.
2868 */
2870 SphHarmFrequencySeries **hlm, /**< Output: Frequency-domain hlm */
2871 UNUSED REAL8 phiRef, /**< Phase at reference time */
2872 UNUSED REAL8 deltaF, /**< Sampling frequency (Hz) */
2873 REAL8 fLow, /**< Starting GW frequency (Hz) */
2874 UNUSED REAL8 fHigh, /**< End frequency; 0 defaults to Mf=0.14 */
2875 REAL8 fRef, /**< Reference frequency unused here */
2876 REAL8 distance, /**< Reference frequency (Hz); 0 defaults to fLow */
2877 REAL8 m1SI, /**< Mass of companion 1 (kg) */
2878 REAL8 m2SI, /**< Mass of companion 2 (kg) */
2879 REAL8 chi1, /**< Dimensionless aligned component spin 1 */
2880 REAL8 chi2, /**< Dimensionless aligned component spin 2 */
2881 UNUSED INT4 nk_max, /**< Truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
2882 UNUSED UINT4 nModes, /**< Number of modes to use. This should be 1 for SEOBNRv5_ROM and 7 for SEOBNRv5HM_ROM */
2883 bool use_hybridization, /**< Whether the ROM should be hybridized */
2884 LALDict *LALParams, /**< Additional lal parameters including the tidal deformability*/
2885 NRTidal_version_type NRTidal_version /**< only NRTidalv3_V or NoNRT_V in case of BBH baseline */
2886 )
2887{
2888 REAL8 sign_odd_modes = 1.;
2889 /* Internally we need m1 > m2, so change around if this is not the case */
2890 if (m1SI < m2SI) {
2891 // Swap m1 and m2
2892 REAL8 m1temp = m1SI;
2893 REAL8 chi1temp = chi1;
2894 m1SI = m2SI;
2895 chi1 = chi2;
2896 m2SI = m1temp;
2897 chi2 = chi1temp;
2898 sign_odd_modes = -1.; // When we swap m1 with m2 odd-m modes should get - sign because they depend on dm
2899 }
2900 /* Check if the number of modes makes sense */
2901
2902 if(nModes >7){
2903 XLAL_PRINT_ERROR("Requested number of modes not available. Set nModes = 0 to get all the available modes.\n");
2905 }
2906
2907 /* Get masses in terms of solar mass */
2908 REAL8 mass1 = m1SI / LAL_MSUN_SI;
2909 REAL8 mass2 = m2SI / LAL_MSUN_SI;
2910 REAL8 Mtot = mass1+mass2;
2911 REAL8 q = mass1/mass2;
2912 UNUSED REAL8 Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
2913
2914 if(fRef==0.0)
2915 fRef=fLow;
2916
2917 // Use fLow, fHigh, deltaF to compute freqs sequence
2918 // Instead of building a full sequency we only transfer the boundaries and let
2919 // the internal core function do the rest (and properly take care of corner cases).
2921 freqs->data[0] = fLow;
2922 freqs->data[1] = fHigh;
2923
2924 SEOBNRROMdataDS *romdataset;
2925
2926 /* Load ROM data if not loaded already */
2927#ifdef LAL_PTHREAD_LOCK
2928 if (nModes == 1){
2929 romdataset=__lalsim_SEOBNRv5ROMDS_data;
2930 (void) pthread_once(&SEOBNRv5ROM_is_initialized, SEOBNRv5ROM_Init_LALDATA);
2931 }
2932 else{
2934 (void) pthread_once(&SEOBNRv5HMROM_is_initialized, SEOBNRv5HMROM_Init_LALDATA);
2935 }
2936#else
2937 if (nModes == 1){
2939 }
2940 else{
2942 }
2943#endif
2944
2945 /* Generate modes */
2946 UNUSED UINT8 retcode;
2947
2948 if (use_hybridization) {
2949 if(nModes == 0) // Return all modes by default
2950 retcode = SEOBNRv5HMROMCoreModesHybridized(hlm, phiRef, fRef, distance,
2951 Mtot_sec, q, chi1, chi2, freqs,
2952 deltaF, nk_max, 7, sign_odd_modes, romdataset, LALParams, NRTidal_version);
2953 else
2954 retcode = SEOBNRv5HMROMCoreModesHybridized(hlm, phiRef, fRef, distance,
2955 Mtot_sec, q, chi1, chi2, freqs,
2956 deltaF, nk_max, nModes, sign_odd_modes, romdataset, LALParams, NRTidal_version);
2957 }
2958 else { // No hybridization
2959 if(nModes == 0) // Return all modes by default
2960 retcode = SEOBNRv5HMROMCoreModes(hlm, phiRef, fRef, distance,
2961 Mtot_sec, q, chi1, chi2, freqs,
2962 deltaF, nk_max, 7, sign_odd_modes, romdataset);
2963 else
2964 retcode = SEOBNRv5HMROMCoreModes(hlm, phiRef, fRef, distance,
2965 Mtot_sec, q, chi1, chi2, freqs,
2966 deltaF, nk_max, nModes, sign_odd_modes, romdataset);
2967}
2968
2970
2971 return(XLAL_SUCCESS);
2972}
2973
2974/**
2975* Compute waveform modes at user specified frequencies for the SEOBNRv5HM_ROM model
2976*
2977* This is a convenience function. It uses the hybridized ROM.
2978*
2979*/
2981 SphHarmFrequencySeries **hlm, /**< Output: Frequency-domain hlm */
2982 const REAL8Sequence *freqs, /**< Frequency points at which to evaluate the waveform (Hz) */
2983 UNUSED REAL8 phiRef, /**< Phase at reference time */
2984 UNUSED REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
2985 UNUSED REAL8 distance, /**< Distance of source (m) */
2986 UNUSED REAL8 inclination, /**< Inclination of source (rad) */
2987 REAL8 m1SI, /**< Mass of companion 1 (kg) */
2988 REAL8 m2SI, /**< Mass of companion 2 (kg) */
2989 REAL8 chi1, /**< Dimensionless aligned component spin 1 */
2990 REAL8 chi2, /**< Dimensionless aligned component spin 2 */
2991 UNUSED INT4 nk_max, /**< Truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
2992 UNUSED UINT4 nModes, /**< Number of modes to use. This should be 1 for SEOBNRv5_ROM and 7 for SEOBNRv5HM_ROM */
2993 LALDict *LALParams, /**< Additional lal parameters including the tidal deformability*/
2994 NRTidal_version_type NRTidal_version /**< NRTidal version; only NRTidalv3_V or NoNRT_V in case of BBH baseline */
2995 )
2996{
2997 REAL8 sign_odd_modes = 1.;
2998 /* Internally we need m1 > m2, so change around if this is not the case */
2999 if (m1SI < m2SI) {
3000 // Swap m1 and m2
3001 REAL8 m1temp = m1SI;
3002 REAL8 chi1temp = chi1;
3003 m1SI = m2SI;
3004 chi1 = chi2;
3005 m2SI = m1temp;
3006 chi2 = chi1temp;
3007 sign_odd_modes = -1.; // When we swap m1 with m2 odd-m modes should get - sign because they depend on dm
3008 }
3009
3010 /* Get masses in terms of solar mass */
3011 REAL8 mass1 = m1SI / LAL_MSUN_SI;
3012 REAL8 mass2 = m2SI / LAL_MSUN_SI;
3013 REAL8 Mtot = mass1+mass2;
3014 REAL8 q = mass1/mass2;
3015 UNUSED REAL8 Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
3016
3017 /* Select modes to include in the waveform */
3018 LALValue *ModeArray = XLALSimInspiralWaveformParamsLookupModeArray(LALParams);
3019 //ModeArray includes the modes chosen by the user
3020 if (ModeArray == NULL) {
3021 //If the user doesn't choose the modes, use the standard modes
3022 ModeArray = XLALSimInspiralCreateModeArray();
3024 }
3025 //Check that the modes chosen are available for the model
3027 XLALPrintError ("Not available mode chosen.\n");
3029 }
3030
3031 SEOBNRROMdataDS *romdataset;
3032
3033 /* Load ROM data if not loaded already */
3034#ifdef LAL_PTHREAD_LOCK
3035 if (nModes == 1){
3036 romdataset=__lalsim_SEOBNRv5ROMDS_data;
3037 (void) pthread_once(&SEOBNRv5ROM_is_initialized, SEOBNRv5ROM_Init_LALDATA);
3038 }
3039 else{
3041 (void) pthread_once(&SEOBNRv5HMROM_is_initialized, SEOBNRv5HMROM_Init_LALDATA);
3042 }
3043#else
3044 if (nModes == 1){
3046 }
3047 else{
3049 }
3050#endif
3051
3052 // Call the internal core function with deltaF = 0 to indicate that freqs is non-uniformly
3053 // spaced and we want the modes only at these frequencies
3054 // The FS interface is only implemented for the hybridized ROM
3055 double deltaF = 0.0;
3056
3057 /* Generate modes */
3058 UINT8 retcode = 0;
3060 phiRef, fRef, distance, Mtot_sec, q, chi1, chi2, freqs, deltaF, nk_max,
3061 nModes, sign_odd_modes, romdataset, LALParams, NRTidal_version);
3062 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
3063
3064 XLALDestroyValue(ModeArray);
3065
3066 // This is a debug enviromental variable; when activated it deletes the ROM dataset from memory.
3067 const char* s = getenv("FREE_MEMORY_SEOBNRv5HMROM");
3068 if (s!=NULL){
3069 for(unsigned int index_mode = 0; index_mode < nModes;index_mode++){
3070 SEOBNRROMdataDS_Cleanup(&romdataset[index_mode]);
3071 }
3072 }
3073
3074 return(retcode);
3075}
3076
3077// Auxiliary function to perform setup of phase spline for t(f) and f(t) functions
3079 gsl_spline **spline_phi, // phase spline
3080 gsl_interp_accel **acc_phi, // phase spline accelerator
3081 REAL8 *Mf_final, // ringdown frequency in Mf
3082 REAL8 *Mtot_sec, // total mass in seconds
3083 REAL8 m1SI, // Mass of companion 1 (kg)
3084 REAL8 m2SI, // Mass of companion 2 (kg)
3085 REAL8 chi1, // Aligned spin of companion 1
3086 REAL8 chi2, // Aligned spin of companion 2
3087 REAL8 Mf_start, // Starting geometric frequency
3088 REAL8 *Mf_ROM_min, // Lowest geometric frequency for ROM
3089 REAL8 *Mf_ROM_max // Highest geometric frequency for ROM
3090)
3091{
3092 /* Get masses in terms of solar mass */
3093 double mass1 = m1SI / LAL_MSUN_SI;
3094 double mass2 = m2SI / LAL_MSUN_SI;
3095 double Mtot = mass1 + mass2;
3096 double q = mass1 / mass2;
3097 double eta = mass1 * mass2 / (Mtot*Mtot); /* Symmetric mass-ratio */
3098 *Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
3099
3100 /* 'Nudge' parameter values to allowed boundary values if close by */
3101 if (q < 1.0) nudge(&q, 1.0, 1e-6);
3102 if (q > 100.0) nudge(&q, 100.0, 1e-6);
3103
3104 if ( chi1 < -0.998 || chi2 < -0.998 || chi1 > 0.998 || chi2 > 0.998) {
3105 XLALPrintError("XLAL Error - %s: chi1 or chi2 smaller than -0.998 or larger than 0.998!\n"
3106 "SEOBNRv5HMROM is only available for spins in the range -0.998 <= a/M <= 0.998.\n",
3107 __func__);
3109 }
3110
3111 if (q<1.0 || q > 100.0) {
3112 XLALPrintError("XLAL Error - %s: q (%f) bigger than 100.0 or unphysical!\n"
3113 "SEOBNRv5HMROM is only available for q in the range 1 <= q <= 100.\n",
3114 __func__, q);
3116 }
3117
3118 SEOBNRROMdataDS *romdataset;
3119
3120 /* Load ROM data if not loaded already */
3121#ifdef LAL_PTHREAD_LOCK
3122 romdataset=__lalsim_SEOBNRv5ROMDS_data;
3123 (void) pthread_once(&SEOBNRv5ROM_is_initialized, SEOBNRv5ROM_Init_LALDATA);
3124#else
3126#endif
3127
3128 UINT4 nModes = 1; /* 22 mode only */
3129 UINT4 nk_max = -1; /* Default value */
3130 // Get the splines for the amplitude and phase of the modes
3131 AmpPhaseSplineData **ampPhaseSplineData = NULL;
3132 AmpPhaseSplineData_Init(&ampPhaseSplineData, nModes);
3133 UNUSED int retcode = SEOBNRv5HMROMCoreModeAmpPhaseSplines(
3134 ampPhaseSplineData, q, chi1, chi2, nk_max, nModes,romdataset);
3135 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
3136
3137 // Setup hybridized phase splines as in SEOBNRv5HMROMCoreModesHybridized
3138
3139 // Safety factors for defining the hybridization window
3140 const double f_hyb_win_lo_fac = 1.01;
3141 const double f_hyb_win_hi_fac = 2.0;
3142
3143 /* Generate PN phases */
3144 double PN_Mf_low = fmin(Mf_start / 2.0, Mf_low_21); // Start at half of the starting frequency to avoid gsl interpolation errors in the (2,1) mode; at least starting frequency of the 22 mode.
3145 double PN_Mf_high = 1.1 * f_hyb_win_hi_fac * Mf_low_55; // to have some overlap with the shortest ROM mode
3146
3147 gsl_vector *Mfs = NULL;
3148 const double acc = 1e-4; // Hardcoded spline interpolation accuracy target
3149 retcode = BuildInspiralGeomFrequencyGrid(&Mfs, PN_Mf_low, PN_Mf_high, q, acc);
3150 if(retcode != XLAL_SUCCESS) XLAL_ERROR(retcode);
3151
3152 // Arrays for storing phase spline for each mode
3153 gsl_spline *hybrid_spline_phi[nModes];
3154 for (UINT4 i=0; i < nModes; i++) {
3155 hybrid_spline_phi[i] = NULL;
3156 }
3157
3158 // Alignment constants, time and phase shift
3159 // Computed once for (2,-2), then propagated to other modes (with possible pi)
3160 REAL8 Deltat_22_align = 0.;
3161 REAL8 Deltaphi_22_align = 0.;
3162
3163 // Loop over all modes and hybridize
3164 for (UINT4 k=0; k < nModes; k++) {
3165 double f_hyb_win_lo = Mf_low_lm_v5hm[k] * f_hyb_win_lo_fac;
3166 double f_hyb_win_hi = Mf_low_lm_v5hm[k] * f_hyb_win_hi_fac;
3167 if (k==0)
3168 XLALPrintInfo("%s : SEOBNRv5HM_ROM hybridization window for (2,2) mode: Mf in [%g, %g]\n",
3169 __func__, f_hyb_win_lo, f_hyb_win_hi);
3170 int modeL = lmModes_v5hm[k][0];
3171 int modeM = lmModes_v5hm[k][1];
3172
3173 gsl_vector *PNphase = NULL;
3174
3175 // We get q >= 1 which is consistent with the TF2 wrapper functions
3176 retcode |= TaylorF2Phasing(Mtot, q, chi1, chi2, modeL, modeM, Mfs, &PNphase);
3177 if(retcode != 0) {
3178 gsl_vector_free(PNphase);
3179 gsl_vector_free(Mfs);
3180 XLAL_ERROR(retcode);
3181 }
3182
3183 // Hybridize phase
3184 if (!(modeL==2 && modeM==2)) {
3185 XLALPrintError ("Only the (2,2) mode is used.\n");
3187 }
3188 // Hybridize (2,-2) and output time and phase shift
3190 &hybrid_spline_phi[k],
3191 &Deltat_22_align,
3192 &Deltaphi_22_align,
3193 ampPhaseSplineData[k],
3194 Mfs, PNphase,
3195 f_hyb_win_lo, f_hyb_win_hi
3196 );
3197 }
3198
3199 *spline_phi = hybrid_spline_phi[0];
3200 *acc_phi = ampPhaseSplineData[0]->acc_phi;
3201
3202 // lowest allowed geometric frequency for ROM
3204 // highest allowed geometric frequency for ROM is the one associated with the 55 mode
3205 *Mf_ROM_max = const_fmax_lm_v5hm[4] * Get_omegaQNM_SEOBNRv5(q, chi1, chi2, 5, 5) / (2.*LAL_PI);
3206
3207 // Get SEOBNRv5_ROM ringdown frequency for 22 mode
3208 *Mf_final = SEOBNRROM_Ringdown_Mf_From_Mtot_Eta(*Mtot_sec, eta, chi1, chi2,
3209 SEOBNRv5_ROM);
3210
3211 return(XLAL_SUCCESS);
3212}
3213
3214/**
3215 * Compute the 'time' elapsed in the ROM waveform from a given starting frequency until the ringdown.
3216 * Analogous of XLALSimIMRSEOBNRv4ROMTimeOfFrequency from LALSimIMRSEOBNRv4ROM.c
3217 *
3218 * The notion of elapsed 'time' (in seconds) is defined here as the difference of the
3219 * frequency derivative of the frequency domain phase between the ringdown frequency
3220 * and the starting frequency ('frequency' argument). This notion of time is similar to the
3221 * chirp time, but it includes both the inspiral and the merger ringdown part of SEOBNRv5_ROM.
3222 *
3223 * The SEOBNRv5_ROM ringdown frequency can be obtained by calling XLALSimInspiralGetFinalFreq().
3224 *
3225 */
3227 REAL8 *t, /**< Output: time (s) elapsed from starting frequency to ringdown */
3228 REAL8 frequency, /**< Starting frequency (Hz) */
3229 REAL8 m1SI, /**< Mass of companion 1 (kg) */
3230 REAL8 m2SI, /**< Mass of companion 2 (kg) */
3231 REAL8 chi1, /**< Dimensionless aligned component spin 1 */
3232 REAL8 chi2 /**< Dimensionless aligned component spin 2 */
3233)
3234{
3235 /* Internally we need m1 > m2, so change around if this is not the case */
3236 if (m1SI < m2SI) {
3237 // Swap m1 and m2
3238 double m1temp = m1SI;
3239 double chi1temp = chi1;
3240 m1SI = m2SI;
3241 chi1 = chi2;
3242 m2SI = m1temp;
3243 chi2 = chi1temp;
3244 }
3245
3246 // Set up phase spline
3247 gsl_spline *spline_phi;
3248 gsl_interp_accel *acc_phi;
3249 double Mf_final, Mtot_sec;
3250 double Mf_ROM_min, Mf_ROM_max;
3251 double Mf = frequency * (m1SI + m2SI) * LAL_MTSUN_SI / LAL_MSUN_SI;
3252
3253 int ret = SEOBNRv5ROMTimeFrequencySetup(&spline_phi, &acc_phi, &Mf_final,
3254 &Mtot_sec, m1SI, m2SI, chi1, chi2, Mf,
3256 if(ret != 0)
3257 XLAL_ERROR(ret);
3258
3259 // Time correction is t(f_final) = 1/(2pi) dphi/df (f_final)
3260 double t_corr = gsl_spline_eval_deriv(spline_phi, Mf_final, acc_phi) / (2*LAL_PI); // t_corr / M
3261 //XLAL_PRINT_INFO("t_corr[s] = %g\n", t_corr * Mtot_sec);
3262
3263 if (Mf > Mf_ROM_max || Mf > Mf_final) {
3264 gsl_spline_free(spline_phi);
3265 gsl_interp_accel_free(acc_phi);
3266 XLAL_ERROR(XLAL_EDOM, "Frequency %g Hz (Mf=%g) is outside allowed range.\n"
3267 "Min / max / final Mf values are %g, %g, %g\n", frequency, Mf, Mf_ROM_min, Mf_ROM_max, Mf_final);
3268 }
3269
3270 // Compute time relative to origin at merger
3271 double time_M = gsl_spline_eval_deriv(spline_phi, frequency * Mtot_sec, acc_phi) / (2*LAL_PI) - t_corr;
3272 // Add a minus factor to match SEOBNRv4_ROM conventions
3273 *t = -1. * time_M * Mtot_sec;
3274
3275 gsl_spline_free(spline_phi);
3276 gsl_interp_accel_free(acc_phi);
3277
3278 return(XLAL_SUCCESS);
3279}
3280
3281/** @} */
struct tagLALH5File LALH5File
void XLALDestroyDict(LALDict *dict)
LALDict * XLALCreateDict(void)
int XLALSimNRTunedTidesFDTidalAmplitudeFrequencySeries(const REAL8Sequence *amp_tidal, const REAL8Sequence *fHz, REAL8 m1_SI, REAL8 m2_SI, REAL8 lambda1, REAL8 lambda2)
Function to call amplitude tidal series only; done for convenience to use for PhenomD_NRTidalv2 and S...
Auxiliary functions related to HDF5 waveform data files.
static const REAL8 Mf_ROM_max
static const REAL8 Mf_ROM_min
#define nModes
static void nudge(REAL8 *x, REAL8 X, REAL8 epsilon)
static double double delta
Auxiliary functions for SEOBNRv1/v2 reduced order modeling codes in LALSimIMRSEOBNRv2ROMDoubleSpin....
static UNUSED UINT4 align_wfs_window_from_22(gsl_vector *f_array_1, gsl_vector *f_array_2, gsl_vector *phase_1, gsl_vector *phase_2, REAL8 f_align_start, REAL8 f_align_end, REAL8 Deltat22, REAL8 Deltaphi22, INT4 modeM)
static UNUSED REAL8 Get_omegaQNM_SEOBNRv5(REAL8 q, REAL8 chi1z, REAL8 chi2z, UINT4 l, UINT4 m)
static UNUSED UINT4 compute_i_max_LF_i_min_HF(INT8 *i_max_LF, INT8 *i_min_LF, gsl_vector *freqs_in_1, gsl_vector *freqs_in_2, REAL8 freq_1)
static UNUSED REAL8 Interpolate_Coefficent_Tensor(gsl_vector *v, REAL8 eta, REAL8 chi1, REAL8 chi2, int ncy, int ncz, gsl_bspline_workspace *bwx, gsl_bspline_workspace *bwy, gsl_bspline_workspace *bwz)
static UNUSED UINT4 unwrap_phase(gsl_vector *phaseout, gsl_vector *phasein)
static UNUSED UINT4 align_wfs_window(gsl_vector *f_array_1, gsl_vector *f_array_2, gsl_vector *phase_1, gsl_vector *phase_2, REAL8 *Deltat, REAL8 *Deltaphi, REAL8 f_align_start, REAL8 f_align_end)
static UNUSED double SEOBNRROM_Ringdown_Mf_From_Mtot_Eta(const double Mtot_sec, const double eta, const double chi1, const double chi2, Approximant apx)
static UNUSED char * concatenate_strings(int count,...)
static UNUSED UINT4 blend_functions(gsl_vector *freqs_out, gsl_vector *out_fun, gsl_vector *freq_in_1, gsl_vector *fun_in_1, gsl_vector *freq_in_2, gsl_vector *fun_in_2, REAL8 freq_1, REAL8 freq_2)
const REAL8 const_fmax_lm_v5hm[NMODES]
static UNUSED UINT8 SEOBNRv5HMROM_Select_HF_patch(REAL8 q, REAL8 chi1)
static UNUSED void AmpPhaseSplineData_Destroy(AmpPhaseSplineData **data, const int num_modes)
static SEOBNRROMdataDS __lalsim_SEOBNRv5ROMDS_data[1]
static UNUSED UINT8 SEOBNRv5HMROM_freq_phase_sparse_grid_hybrid(UNUSED gsl_vector **freq, UNUSED gsl_vector **phase, REAL8 q, REAL8 chi1, REAL8 chi2, INT4 nk_max, SEOBNRROMdataDS *romdataset)
static INT8 Check_EOBROM_mode_array_structure(LALValue *ModeArray, UINT4 nModes)
ModeArray is a structure which allows to select the modes to include in the waveform.
static UNUSED UINT8 SEOBNRv5HMROM_cmode_sparse_grid(gsl_vector *creal_f, gsl_vector *cimag_f, REAL8 q, REAL8 chi1, REAL8 chi2, const char *freq_range, UINT8 nMode, INT4 nk_max, SEOBNRROMdataDS *romdataset)
static UNUSED int SEOBNRROMdataDS_Init(SEOBNRROMdataDS *romdata, const char dir[], UINT4, bool)
static UNUSED void SEOBNRv5ROM_Init_LALDATA(void)
Setup SEOBNRv5ROM model using data files installed in $LAL_DATA_PATH.
static UNUSED int TaylorF2Phasing(double Mtot, double q, double chi1, double chi2, int l, int m, gsl_vector *Mfs, gsl_vector **PNphase)
int(* load_dataPtr)(const char *, gsl_vector *, gsl_vector *, gsl_matrix *, gsl_matrix *, gsl_vector *)
static UNUSED double GetDeltaphilm(int l, int m)
static UNUSED int SEOBNRv5HMROMCoreModesHybridized(UNUSED SphHarmFrequencySeries **hlm_list, UNUSED REAL8 phiRef, UNUSED REAL8 fRef, UNUSED REAL8 distance, UNUSED REAL8 Mtot_sec, UNUSED REAL8 q, UNUSED REAL8 chi1, UNUSED REAL8 chi2, UNUSED const REAL8Sequence *freqs_in, UNUSED REAL8 deltaF, UNUSED INT4 nk_max, UNUSED UINT4 nModes, REAL8 sign_odd_modes, UNUSED SEOBNRROMdataDS *romdataset, LALDict *LALParams, NRTidal_version_type NRTidal_version)
static UNUSED void SplineData_Init(SplineData **splinedata, int ncx, int ncy, int ncz, const double *qvec, const double *chi1vec, const double *chi2vec)
static UNUSED int hybridize_ROM_with_PN_phase_output_align(gsl_spline **hyb_spline, REAL8 *Deltat_align, REAL8 *Deltaphi_align, AmpPhaseSplineData *ampPhaseSplineData_for_mode, gsl_vector *PN_freq, gsl_vector *PN_phase, double f_hyb_win_lo, double f_hyb_win_hi)
static UNUSED int SEOBNRv5HMROM_Init(const char dir[], UINT4, bool, SEOBNRROMdataDS *romdataset)
Setup SEOBNRv5HMROM mode using data files installed in dir.
const REAL8 Mf_low_lm_v5hm[NMODES]
static UNUSED void SplineData_Destroy(SplineData *splinedata)
#define Mf_low_43
#define f_hyb_ini
static UNUSED UINT8 SEOBNRv5HMROM_phase_sparse_grid_hybrid_output_align(gsl_vector **freq, gsl_vector **phase, REAL8 *Deltat_align, REAL8 *Deltaphi_align, gsl_vector *phase_lo, gsl_vector *phase_hi, gsl_vector *freq_lo, gsl_vector *freq_hi, double f_hyb_lo, double f_hyb_hi)
static UNUSED bool SEOBNRv5HMROM_IsSetup(UINT4, SEOBNRROMdataDS *romdataset)
Helper function to check if the SEOBNRv5HMROM model has been initialised.
static UNUSED void SEOBNRROMdataDS_coeff_Init(SEOBNRROMdataDS_coeff **romdatacoeff, int nk_cmode, int nk_phase)
static SEOBNRROMdataDS __lalsim_SEOBNRv5HMROMDS_data[NMODES]
static UNUSED int TaylorF2Amplitude(const double q, const double chi1, const double chi2, const int l, const int m, gsl_vector *Mfs, gsl_vector **PNamp)
static UNUSED int SEOBNRv5HMROMCoreModes(SphHarmFrequencySeries **hlm_list, REAL8 phiRef, REAL8 fRef, REAL8 distance, REAL8 Mtot_sec, REAL8 q, REAL8 chi1, REAL8 chi2, const REAL8Sequence *freqs, REAL8 deltaF, INT4 nk_max, UINT4 nModes, REAL8 sign_odd_modes, SEOBNRROMdataDS *romdataset)
Core function for computing the ROM waveform.
const REAL8 const_phaseshift_lm_v5hm[NMODES]
static int SEOBROMComputehplushcrossFromhlm(COMPLEX16FrequencySeries *hplusFS, COMPLEX16FrequencySeries *hcrossFS, LALValue *ModeArray, SphHarmFrequencySeries *hlm, REAL8 inc, REAL8 phi)
static UNUSED int SEOBNRROMdataDS_Init_submodel(UNUSED SEOBNRROMdataDS_submodel **submodel, UNUSED const char dir[], UNUSED const char grp_name[], UNUSED UINT4 index_mode, UNUSED bool use_hm)
static UNUSED void SEOBNRROMdataDS_Cleanup_submodel(SEOBNRROMdataDS_submodel *submodel)
#define Mf_low_33
static UNUSED void AmpPhaseSplineData_Init(AmpPhaseSplineData ***data, const int num_modes)
static UNUSED void SEOBNRv5HMROM_Init_LALDATA(void)
Setup SEOBNRv5HMROM model using data files installed in $LAL_DATA_PATH.
static UNUSED UINT8 SEOBNRv5HMROM_phase_sparse_grid(gsl_vector *phase_f, REAL8 q, REAL8 chi1, REAL8 chi2, const char *freq_range, INT4 nk_max, SEOBNRROMdataDS *romdataset)
static UNUSED void SEOBNRROMdataDS_coeff_Cleanup(SEOBNRROMdataDS_coeff *romdatacoeff)
const char mode_array_v5hm[NMODES][3]
#define Mf_low_22
static UNUSED UINT8 SEOBNRv5HMROM_cmode_sparse_grid_hybrid(gsl_vector *freq, gsl_vector *cmode_real, gsl_vector *cmode_imag, gsl_vector *freq_lo, gsl_vector *freq_hi, REAL8 q, REAL8 chi1, REAL8 chi2, UINT8 nMode, INT8 nk_max, SEOBNRROMdataDS *romdataset)
#define Mf_low_21
static UNUSED UINT8 SEOBNRv5HMROM_approx_phi_lm(gsl_vector *freq_mode_lm, gsl_vector *phase_approx_lm, gsl_vector *freq_carrier_hyb, gsl_vector *phase_carrier_hyb, UINT4 nMode)
#define NMODES
#define f_hyb_end
static UNUSED UINT8 SEOBNRv5HMROM_phase_sparse_grid_hybrid(gsl_vector *freq, gsl_vector *phase, gsl_vector *freq_lo, gsl_vector *freq_hi, REAL8 q, REAL8 chi1, REAL8 chi2, INT4 nk_max, SEOBNRROMdataDS *romdataset)
static UNUSED void SEOBNRROMdataDS_Cleanup(SEOBNRROMdataDS *romdata)
#define Mf_low_44
static UNUSED int hybridize_ROM_with_PN_amplitude(gsl_spline **hyb_spline, AmpPhaseSplineData *ampPhaseSplineData_for_mode, gsl_vector *PN_freq, gsl_vector *PN_amp, double f_hyb_win_lo, double f_hyb_win_hi)
static UNUSED double GetModeAmpFactor(double eta, double delta, double chi1, double chi2, int l, int m, double v)
static UNUSED UINT8 SEOBNRv5HMROM_freq_cmode_sparse_grid_hybrid(gsl_vector **freq_cmode, gsl_vector **cmode_real, gsl_vector **cmode_imag, REAL8 q, REAL8 chi1, REAL8 chi2, UINT8 nMode, INT8 nk_max, SEOBNRROMdataDS *romdataset)
static size_t NextPow2(const size_t n)
static UNUSED int BuildInspiralGeomFrequencyGrid(gsl_vector **Mfreq, const double Mfmin, const double Mfmax, const double q, const double acc)
static int TP_Spline_interpolation_3d(REAL8 q, REAL8 chi1, REAL8 chi2, gsl_vector *cvec, int nk, int nk_max, int ncx, int ncy, int ncz, const double *qvec, const double *chi1vec, const double *chi2vec, gsl_vector *c_out)
static UNUSED UINT8 SEOBNRv5HMROM_amplitude_sparse_grid_hybrid_general(gsl_vector **freq, gsl_vector **amp, gsl_vector *amp_lo, gsl_vector *amp_hi, gsl_vector *freq_lo, gsl_vector *freq_hi, double f_hyb_lo, double f_hyb_hi)
static UNUSED UINT8 SEOBNRv5HMROM_phase_sparse_grid_hybrid_input_align(gsl_vector **freq, gsl_vector **phase, gsl_vector *phase_lo, gsl_vector *phase_hi, gsl_vector *freq_lo, gsl_vector *freq_hi, double f_hyb_lo, double f_hyb_hi, REAL8 Deltat_22_align, REAL8 Deltaphi_22_align, INT4 modeM)
#define Mf_low_32
#define Mf_low_55
static UNUSED int hybridize_ROM_with_PN_phase_input_align(gsl_spline **hyb_spline, AmpPhaseSplineData *ampPhaseSplineData_for_mode, gsl_vector *PN_freq, gsl_vector *PN_phase, double f_hyb_win_lo, double f_hyb_win_hi, REAL8 Deltat_22_align, REAL8 Deltaphi_22_align, INT4 modeM)
const UINT4 lmModes_v5hm[NMODES][2]
static UNUSED int SEOBNRv5HMROMCoreModeAmpPhaseSplines(UNUSED AmpPhaseSplineData **ampPhaseSplineData, UNUSED REAL8 q, UNUSED REAL8 chi1, UNUSED REAL8 chi2, UNUSED INT4 nk_max, UNUSED UINT4 nModes, UNUSED SEOBNRROMdataDS *romdataset)
static UINT8 Setup_EOBROM__std_mode_array_structure(LALValue *ModeArray, UINT4 nModes)
ModeArray is a structure which allows to select the modes to include in the waveform.
static UNUSED int spline_to_gsl_vectors(gsl_spline *s, gsl_vector **x, gsl_vector **y)
LALValue * XLALSimInspiralWaveformParamsLookupModeArray(LALDict *params)
REAL8 XLALSimInspiralWaveformParamsLookupTidalLambda2(LALDict *params)
REAL8 XLALSimInspiralWaveformParamsLookupTidalLambda1(LALDict *params)
int XLALSimInspiralWaveformParamsInsertPNSpinOrder(LALDict *params, INT4 value)
void XLALDestroyValue(LALValue *value)
int s
Definition: bh_qnmode.c:137
int l
Definition: bh_qnmode.c:135
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
const double pn
sigmaKerr data[0]
const double Lambda
#define XLAL_FILE_RESOLVE_PATH(fname)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
void XLALH5FileClose(LALH5File UNUSED *file)
LALH5File * XLALH5GroupOpen(LALH5File UNUSED *file, const char UNUSED *name)
LALH5File * XLALH5FileOpen(const char UNUSED *path, const char UNUSED *mode)
#define LAL_MSUN_SI
#define LAL_PI
#define LAL_MTSUN_SI
#define LAL_MRSUN_SI
uint64_t UINT8
double complex COMPLEX16
double REAL8
int64_t INT8
uint32_t UINT4
int32_t INT4
void * XLALMalloc(size_t n)
void * XLALCalloc(size_t m, size_t n)
void XLALFree(void *p)
NRTidal_version_type
Definition: LALSimIMR.h:80
@ NRTidalv3_V
version NRTidalv3
Definition: LALSimIMR.h:83
static UNUSED int SEOBNRv5ROMTimeFrequencySetup(gsl_spline **spline_phi, gsl_interp_accel **acc_phi, REAL8 *Mf_final, REAL8 *Mtot_sec, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, REAL8 Mf_start, REAL8 *Mf_ROM_min, REAL8 *Mf_ROM_max)
int XLALSimIMRSEOBNRv5HMROM_Modes(SphHarmFrequencySeries **hlm, UNUSED REAL8 phiRef, UNUSED REAL8 deltaF, REAL8 fLow, UNUSED REAL8 fHigh, REAL8 fRef, REAL8 distance, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, UNUSED INT4 nk_max, UNUSED UINT4 nModes, bool use_hybridization, LALDict *LALParams, NRTidal_version_type NRTidal_version)
Compute modes in LAL format for the SEOBNRv5HM_ROM model.
int XLALSimIMRSEOBNRv5HMROMFrequencySequence(UNUSED struct tagCOMPLEX16FrequencySeries **hptilde, UNUSED struct tagCOMPLEX16FrequencySeries **hctilde, const REAL8Sequence *freqs, UNUSED REAL8 phiRef, UNUSED REAL8 fRef, UNUSED REAL8 distance, UNUSED REAL8 inclination, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, UNUSED INT4 nk_max, UNUSED UINT4 nModes, LALDict *LALParams, NRTidal_version_type NRTidal_version)
Compute waveform polarizations at user specified frequencies for the SEOBNRv5HM_ROM model.
int XLALSimIMRSEOBNRv5HMROMFrequencySequence_Modes(SphHarmFrequencySeries **hlm, const REAL8Sequence *freqs, UNUSED REAL8 phiRef, UNUSED REAL8 fRef, UNUSED REAL8 distance, UNUSED REAL8 inclination, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, UNUSED INT4 nk_max, UNUSED UINT4 nModes, LALDict *LALParams, NRTidal_version_type NRTidal_version)
Compute waveform modes at user specified frequencies for the SEOBNRv5HM_ROM model.
int XLALSimIMRSEOBNRv5HMROM(UNUSED struct tagCOMPLEX16FrequencySeries **hptilde, UNUSED struct tagCOMPLEX16FrequencySeries **hctilde, UNUSED REAL8 phiRef, UNUSED REAL8 deltaF, REAL8 fLow, UNUSED REAL8 fHigh, UNUSED REAL8 fRef, UNUSED REAL8 distance, UNUSED REAL8 inclination, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, UNUSED INT4 nk_max, UNUSED UINT4 nModes, bool use_hybridization, LALDict *LALParams, NRTidal_version_type NRTidal_version)
Compute waveform in LAL format for the SEOBNRv5HM_ROM model.
int XLALSimIMRSEOBNRv5ROMTimeOfFrequency(REAL8 *t, REAL8 frequency, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2)
Compute the 'time' elapsed in the ROM waveform from a given starting frequency until the ringdown.
#define LAL_SIM_L_MAX_MODE_ARRAY
Maximum L spherical harmonic mode that is supported in Mode Array.
@ LAL_SIM_INSPIRAL_SPIN_ORDER_35PN
@ SEOBNRv5_ROM
Time domain, precessing phenomenological IMR waveform model with subdominant modes ([arXiv: 20XY....
int XLALSimInspiralTaylorF2AlignedPhasing(PNPhasingSeries **pfa, const REAL8 m1, const REAL8 m2, const REAL8 chi1, const REAL8 chi2, LALDict *extraPars)
Returns structure containing TaylorF2 phasing coefficients for given physical parameters.
int XLALSimInspiralModeArrayIsModeActive(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralModeArrayActivateMode(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralCreateModeArray(void)
Create a LALValue pointer to store the mode array.
int XLALSimAddModeFD(COMPLEX16FrequencySeries *hptilde, COMPLEX16FrequencySeries *hctilde, COMPLEX16FrequencySeries *hlmtilde, REAL8 theta, REAL8 phi, INT4 l, INT4 m, INT4 sym)
Helper function to add a mode to hplus, hcross in Fourier domain copies the function XLALSimAddMode,...
COMPLEX16FrequencySeries * XLALSphHarmFrequencySeriesGetMode(SphHarmFrequencySeries *ts, UINT4 l, INT4 m)
Get the time series of a waveform's (l,m) spherical harmonic mode from a SphHarmFrequencySeries linke...
SphHarmFrequencySeries * XLALSphHarmFrequencySeriesAddMode(SphHarmFrequencySeries *appended, const COMPLEX16FrequencySeries *inmode, UINT4 l, INT4 m)
Prepend a node to a linked list of SphHarmFrequencySeries, or create a new head.
void XLALDestroySphHarmFrequencySeries(SphHarmFrequencySeries *ts)
Delete list from current pointer to the end of the list.
static const INT4 m
static const INT4 q
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
const LALUnit lalStrainUnit
const LALUnit lalSecondUnit
LALUnit * XLALUnitDivide(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
LALUnit * XLALUnitMultiply(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
#define XLAL_ERROR_VOID(...)
#define XLAL_PRINT_INFO(...)
int int int XLALPrintInfo(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_PRINT_ERROR(...)
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EDOM
XLAL_EIO
XLAL_EINVAL
XLAL_EFAILED
XLAL_FAILURE
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
list y
path
int N
x
gsl_interp_accel * acc_amp
gsl_interp_accel * acc_phi
COMPLEX16Sequence * data
COMPLEX16 * data
REAL8 * data
struct tagSphHarmFrequencySeries * next
next pointer
COMPLEX16FrequencySeries * mode
The sequences of sampled data.
gsl_bspline_workspace * bwx
gsl_bspline_workspace * bwz
gsl_bspline_workspace * bwy
SEOBNRROMdataDS_submodel * lowf
SEOBNRROMdataDS_submodel * highf