LALSimulation  5.4.0.1-fe68b98
LALSimIMRSEOBNRv2ROMDoubleSpinHI.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014, 2015 Michael Puerrer, John Veitch
3  * Reduced Order Model for SEOBNR
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with with program; see the file COPYING. If not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  * MA 02110-1301 USA
19  */
20 
21 #ifdef __GNUC__
22 #define UNUSED __attribute__ ((unused))
23 #else
24 #define UNUSED
25 #endif
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <math.h>
30 #include <complex.h>
31 #include <time.h>
32 #include <stdbool.h>
33 #include <alloca.h>
34 #include <string.h>
35 #include <libgen.h>
36 
37 #include <gsl/gsl_errno.h>
38 #include <gsl/gsl_bspline.h>
39 #include <gsl/gsl_blas.h>
40 #include <gsl/gsl_min.h>
41 #include <gsl/gsl_spline.h>
42 #include <gsl/gsl_poly.h>
43 #include <lal/Units.h>
44 #include <lal/SeqFactories.h>
45 #include <lal/LALConstants.h>
46 #include <lal/XLALError.h>
47 #include <lal/FrequencySeries.h>
48 #include <lal/Date.h>
49 #include <lal/StringInput.h>
50 #include <lal/Sequence.h>
51 #include <lal/LALStdio.h>
52 #include <lal/FileIO.h>
53 
54 #ifdef LAL_HDF5_ENABLED
55 #include <lal/H5FileIO.h>
56 static const char ROMDataHDF5[] = "SEOBNRv2ROM_DS_HI_v1.0.hdf5";
57 static const INT4 ROMDataHDF5_VERSION_MAJOR = 1;
58 static const INT4 ROMDataHDF5_VERSION_MINOR = 0;
59 static const INT4 ROMDataHDF5_VERSION_MICRO = 0;
60 #endif
61 
62 #include <lal/LALSimInspiral.h>
63 #include <lal/LALSimIMR.h>
64 
65 // Helper functions to read gsl_vector and gsl_matrix data with error checking
66 UNUSED static int read_vector_test(const char dir[], const char fname[], gsl_vector *v) {
67  size_t size = strlen(dir) + strlen(fname) + 2;
68  char *path = XLALMalloc(size);
69  snprintf(path, size, "%s/%s", dir, fname);
70 
71  FILE *f = fopen(path, "rb");
72  if (!f)
73  XLAL_ERROR(XLAL_EIO, "Could not find ROM data file at path `%s'", path);
74  int ret = gsl_vector_fread(f, v);
75  if (ret != 0)
76  XLAL_ERROR(XLAL_EIO, "Error reading data from `%s'", path);
77  fclose(f);
78 
79  XLAL_PRINT_INFO("Sucessfully read data file `%s'", path);
80  XLALFree(path);
81  return(XLAL_SUCCESS);
82 }
83 
85 
86 #include <lal/LALConfig.h>
87 #ifdef LAL_PTHREAD_LOCK
88 #include <pthread.h>
89 #endif
90 
91 
92 // This ROM consists of different submodels and also glues together a low mass and 2 high mass models
93 // Filenames of the submodels; need to load up all 3 of them
94 
95 /******************************************************************
96  * The double-spin SEOBNRv2 ROM consists of a number of submodels *
97  * for subdomains of the whole parameter space: *
98  * *
99  * "Core Low frequency" submodel 1 *
100  * B-spline points: 67x12x12 *
101  * Frequency points: {183, 242} *
102  * *
103  * "High frequency, low chi1" submodel 2 *
104  * B-spline points: 63x37x51 *
105  * Frequency points: {133, 133} *
106  * *
107  * "High frequency, high chi1" submodel 3 *
108  * B-spline points: 97x119x51 *
109  * frequency points: {133, 133} *
110  * *
111  *****************************************************************/
112 
113 
114 /********* Input data for spline basis points **************/
115 
116 // The frequency points are different for all submodels.
117 
118 #define nk_amp_sub1 200 // number of SVD-modes == number of basis functions for amplitude
119 #define nk_phi_sub1 250 // number of SVD-modes == number of basis functions for phase
120 
121 // Frequency points for amplitude and phase
122 
123 // sub1: core low mass ROM
124 static const double gA_sub1[] = {0.0000985, 0.000102213, 0.000106067, 0.000110066, 0.000114215, \
125 0.000118521, 0.000122989, 0.000127626, 0.000132437, 0.00013743, \
126 0.000142611, 0.000147988, 0.000153567, 0.000159357, 0.000165364, \
127 0.000171598, 0.000178068, 0.000184781, 0.000191747, 0.000198976, \
128 0.000206477, 0.000214262, 0.000222339, 0.000230721, 0.00023942, \
129 0.000248446, 0.000257812, 0.000267532, 0.000277618, 0.000288084, \
130 0.000298945, 0.000310215, 0.00032191, 0.000334046, 0.000346639, \
131 0.000359708, 0.000373269, 0.000387341, 0.000401944, 0.000417097, \
132 0.000432822, 0.000449139, 0.000466071, 0.000483642, 0.000501876, \
133 0.000520796, 0.00054043, 0.000560805, 0.000581947, 0.000603886, \
134 0.000626653, 0.000650278, 0.000674793, 0.000700233, 0.000726632, \
135 0.000754026, 0.000782452, 0.000811951, 0.000842561, 0.000874326, \
136 0.000907288, 0.000941493, 0.000976987, 0.00101382, 0.00105204, \
137 0.0010917, 0.00113286, 0.00117557, 0.00121989, 0.00126588, 0.0013136, \
138 0.00136312, 0.00141451, 0.00146784, 0.00152318, 0.0015806, \
139 0.00164019, 0.00170203, 0.00176619, 0.00183278, 0.00190187, \
140 0.00197357, 0.00204798, 0.00212519, 0.00220531, 0.00228845, \
141 0.00237472, 0.00246425, 0.00255715, 0.00265355, 0.00275359, \
142 0.0028574, 0.00296513, 0.00307691, 0.00319291, 0.00331329, 0.0034382, \
143 0.00356782, 0.00370232, 0.0038419, 0.00398674, 0.00413704, \
144 0.00429301, 0.00445485, 0.0046228, 0.00479708, 0.00497793, 0.0051656, \
145 0.00536034, 0.00556243, 0.00577213, 0.00598974, 0.00621555, \
146 0.00644988, 0.00669304, 0.00694537, 0.00720721, 0.00747892, \
147 0.00776087, 0.00805346, 0.00835707, 0.00867214, 0.00899908, \
148 0.00933834, 0.0096904, 0.0100557, 0.0104348, 0.0108282, 0.0112364, \
149 0.0116601, 0.0120996, 0.0125558, 0.0130291, 0.0135203, 0.0140301, \
150 0.014559, 0.0151079, 0.0156774, 0.0162685, 0.0168818, 0.0175182, \
151 0.0181787, 0.018864, 0.0195752, 0.0203132, 0.021079, 0.0218737, \
152 0.0226983, 0.023554, 0.024442, 0.0253635, 0.0263197, 0.0273119, \
153 0.0283416, 0.0294101, 0.0305188, 0.0316694, 0.0328633, 0.0341023, \
154 0.0353879, 0.036722, 0.0381065, 0.0395431, 0.0410339, 0.0425808, \
155 0.0441861, 0.0458519, 0.0475806, 0.0493744, 0.0512358, 0.0531674, \
156 0.0551718, 0.0572517, 0.0594101, 0.0616499, 0.0639741, 0.0663859, \
157 0.0688887, 0.0714858, 0.0741808, 0.0769774, 0.0798794, 0.0828909, \
158 0.0860159, 0.0892587, 0.0926237, 0.0961157, 0.0997392, 0.103499, \
159 0.107401, 0.11145, 0.115652, 0.120012, 0.124537, 0.129232, 0.134104, \
160 0.139159, 0.144406, 0.14985, 0.15};
161 
162 static const double gPhi_sub1[] = {0.0000985, 0.0000996191, 0.000100755, 0.000101908, 0.000103079, \
163 0.000104268, 0.000105476, 0.000106702, 0.000107947, 0.000109211, \
164 0.000110495, 0.000111799, 0.000113124, 0.00011447, 0.000115838, \
165 0.000117227, 0.000118638, 0.000120072, 0.000121529, 0.00012301, \
166 0.000124515, 0.000126045, 0.000127599, 0.00012918, 0.000130786, \
167 0.000132419, 0.000134079, 0.000135768, 0.000137484, 0.00013923, \
168 0.000141005, 0.00014281, 0.000144647, 0.000146515, 0.000148415, \
169 0.000150348, 0.000152314, 0.000154315, 0.000156352, 0.000158424, \
170 0.000160532, 0.000162679, 0.000164863, 0.000167087, 0.000169351, \
171 0.000171656, 0.000174003, 0.000176393, 0.000178826, 0.000181305, \
172 0.000183829, 0.0001864, 0.00018902, 0.000191688, 0.000194407, \
173 0.000197177, 0.000200001, 0.000202878, 0.00020581, 0.0002088, \
174 0.000211847, 0.000214954, 0.000218121, 0.000221351, 0.000224645, \
175 0.000228005, 0.000231431, 0.000234927, 0.000238492, 0.000242131, \
176 0.000245843, 0.000249632, 0.000253499, 0.000257445, 0.000261474, \
177 0.000265587, 0.000269787, 0.000274075, 0.000278455, 0.000282928, \
178 0.000287497, 0.000292165, 0.000296934, 0.000301807, 0.000306787, \
179 0.000311877, 0.00031708, 0.000322399, 0.000327838, 0.000333398, \
180 0.000339086, 0.000344902, 0.000350852, 0.00035694, 0.000363169, \
181 0.000369543, 0.000376066, 0.000382744, 0.00038958, 0.000396579, \
182 0.000403747, 0.000411088, 0.000418607, 0.000426311, 0.000434203, \
183 0.000442292, 0.000450582, 0.000459079, 0.000467791, 0.000476725, \
184 0.000485886, 0.000495283, 0.000504923, 0.000514813, 0.000524964, \
185 0.000535381, 0.000546076, 0.000557056, 0.000568331, 0.000579912, \
186 0.000591808, 0.000604031, 0.000616592, 0.000629502, 0.000642774, \
187 0.00065642, 0.000670454, 0.000684889, 0.000699741, 0.000715023, \
188 0.000730752, 0.000746943, 0.000763615, 0.000780785, 0.000798472, \
189 0.000816695, 0.000835474, 0.000854831, 0.000874789, 0.00089537, \
190 0.0009166, 0.000938503, 0.000961107, 0.000984439, 0.00100853, \
191 0.00103341, 0.00105911, 0.00108567, 0.00111312, 0.0011415, \
192 0.00117085, 0.0012012, 0.00123261, 0.00126513, 0.00129879, \
193 0.00133365, 0.00136976, 0.00140718, 0.00144597, 0.00148619, \
194 0.00152792, 0.00157121, 0.00161614, 0.0016628, 0.00171126, \
195 0.00176161, 0.00181395, 0.00186837, 0.00192498, 0.00198389, \
196 0.00204521, 0.00210908, 0.00217561, 0.00224496, 0.00231727, \
197 0.00239271, 0.00247143, 0.00255363, 0.0026395, 0.00272923, \
198 0.00282306, 0.00292121, 0.00302394, 0.00313151, 0.00324421, \
199 0.00336236, 0.00348627, 0.00361632, 0.00375287, 0.00389633, \
200 0.00404716, 0.00420582, 0.00437283, 0.00454874, 0.00473414, \
201 0.00492969, 0.00513608, 0.00535408, 0.00558449, 0.00582823, \
202 0.00608624, 0.0063596, 0.00664946, 0.00695705, 0.00728377, 0.0076311, \
203 0.00800069, 0.00839433, 0.00881401, 0.0092619, 0.00974038, 0.0102521, \
204 0.0108, 0.0113872, 0.0120175, 0.0126946, 0.0134231, 0.0142079, \
205 0.0150544, 0.0159688, 0.0169581, 0.0180299, 0.0191929, 0.020457, \
206 0.0218334, 0.0233345, 0.0249749, 0.0267707, 0.0287408, 0.0309065, \
207 0.0332925, 0.0359272, 0.0388435, 0.0420796, 0.0456801, 0.0496972, \
208 0.054192, 0.0592368, 0.0649174, 0.0713356, 0.0786136, 0.086898, \
209 0.0963666, 0.107235, 0.115, 0.119768, 0.13, 0.134291, 0.145, 0.15};
210 
211 #define nk_amp_sub2 113
212 #define nk_phi_sub2 113
213 
214 // sub2: high frequency, low chi1
215 static const double g_sub2[] = {0.00739041, 0.00753436, 0.00768207, 0.00783364, 0.00798922, \
216 0.00814894, 0.00831292, 0.00848132, 0.00865428, 0.00883196, \
217 0.00901452, 0.00920213, 0.00939497, 0.00959321, 0.00979705, \
218 0.0100067, 0.0102223, 0.0104442, 0.0106725, 0.0109074, 0.0111493, \
219 0.0113984, 0.0116549, 0.0119192, 0.0121915, 0.012472, 0.0127613, \
220 0.0130595, 0.013367, 0.0136843, 0.0140116, 0.0143494, 0.0146981, \
221 0.0150581, 0.0154299, 0.0158141, 0.016211, 0.0166213, 0.0170455, \
222 0.0174842, 0.017938, 0.0184075, 0.0188935, 0.0193968, 0.0199179, \
223 0.0204578, 0.0210174, 0.0215974, 0.0221988, 0.0228227, 0.0234701, \
224 0.0241421, 0.0248398, 0.0255646, 0.0263177, 0.0271005, 0.0279145, \
225 0.0287613, 0.0296425, 0.0305599, 0.0315153, 0.0325108, 0.0335484, \
226 0.0346304, 0.0357592, 0.0369373, 0.0381675, 0.0394525, 0.0407956, \
227 0.0422, 0.0436692, 0.045207, 0.0468174, 0.0485048, 0.0502737, \
228 0.0521292, 0.0540765, 0.0561214, 0.0582701, 0.0605292, 0.0629058, \
229 0.0654076, 0.0680429, 0.0708208, 0.0737509, 0.0768437, 0.0801107, \
230 0.0835641, 0.0872175, 0.0910854, 0.0951836, 0.0995295, 0.104142, \
231 0.109042, 0.114251, 0.119795, 0.1257, 0.131997, 0.138718, 0.145899, \
232 0.15358, 0.161804, 0.170621, 0.180084, 0.190254, 0.201196, 0.212986, \
233 0.225705, 0.239447, 0.254316, 0.270429, 0.287917, 0.3};
234 
235 #define gA_sub2 g_sub2
236 #define gPhi_sub2 g_sub2
237 
238 // sub3: high frequency, high chi1
239 #define nk_amp_sub3 nk_amp_sub2
240 #define nk_phi_sub3 nk_phi_sub2
241 #define gA_sub3 g_sub2
242 #define gPhi_sub3 g_sub2
243 
244 
245 /******* B-spline knots over the parameter space *******/
246 // These knots are different for all submodels.
247 
248 // sub1: core low frequency ROM
249 static const double etavec_sub1[] = {0.01, 0.011, 0.012, 0.013, 0.015, 0.017, 0.018, 0.02, 0.021, 0.022, \
250 0.023, 0.024, 0.025, 0.027, 0.03, 0.035, 0.037, 0.04, 0.042, 0.045, \
251 0.048, 0.05, 0.055, 0.06, 0.065, 0.07, 0.075, 0.08, 0.085, 0.09, \
252 0.095, 0.1, 0.105, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.18, 0.2, \
253 0.22, 0.23, 0.235, 0.24, 0.241, 0.242, 0.243, 0.244, 0.245, 0.246, \
254 0.247, 0.248, 0.2485, 0.2488, 0.249, 0.2491, 0.2492, 0.2493, 0.2494, \
255 0.2495, 0.2496, 0.2497, 0.2498, 0.2499, 0.24995, 0.25};
256 static const double chi1vec_sub1[] = {-0.9999, -0.8, -0.6, -0.4, -0.2, 0., 0.2, 0.4, 0.6, 0.8, 0.9, \
257 0.98999};
258 static const double chi2vec_sub1[] = {-0.9999, -0.8, -0.6, -0.4, -0.2, 0., 0.2, 0.4, 0.6, 0.8, 0.9, \
259 0.98999};
260 
261 static const int ncx_sub1 = 67+2; // points in eta + 2
262 static const int ncy_sub1 = 12+2; // points in chi1 + 2
263 static const int ncz_sub1 = 12+2; // points in chi2 + 2
264 
265 // sub2: high frequency, low chi1
266 static const double etavec_sub2[] = {0.01, 0.0105, 0.011, 0.0115, 0.0125, 0.015, 0.0175, 0.02, 0.025, \
267 0.03, 0.035, 0.04, 0.045, 0.05, 0.055, 0.06, 0.065, 0.07, 0.075, \
268 0.08, 0.085, 0.09, 0.095, 0.1, 0.105, 0.11, 0.115, 0.12, 0.125, 0.13, \
269 0.135, 0.14, 0.145, 0.15, 0.155, 0.16, 0.165, 0.17, 0.175, 0.18, \
270 0.185, 0.19, 0.195, 0.2, 0.205, 0.21, 0.215, 0.22, 0.225, 0.23, \
271 0.235, 0.24, 0.2425, 0.2435, 0.245, 0.24625, 0.2475, 0.2485, 0.249, \
272 0.2495, 0.2498, 0.2499, 0.25};
273 static const double chi1vec_sub2[] = {-0.9999, -0.96, -0.92, -0.88, -0.84, -0.8, -0.76, -0.72, -0.68, \
274 -0.64, -0.6, -0.56, -0.52, -0.48, -0.44, -0.4, -0.36, -0.32, -0.28, \
275 -0.24, -0.2, -0.16, -0.12, -0.08, -0.04, 0., 0.04, 0.08, 0.12, 0.16, \
276 0.2, 0.24, 0.28, 0.32, 0.36, 0.4, 0.44};
277 static const double chi2vec_sub2[] = {-0.9999, -0.96, -0.92, -0.88, -0.84, -0.8, -0.76, -0.72, -0.68, \
278 -0.64, -0.6, -0.56, -0.52, -0.48, -0.44, -0.4, -0.36, -0.32, -0.28, \
279 -0.24, -0.2, -0.16, -0.12, -0.08, -0.04, 0., 0.04, 0.08, 0.12, 0.16, \
280 0.2, 0.24, 0.28, 0.32, 0.36, 0.4, 0.44, 0.48, 0.52, 0.56, 0.6, 0.64, \
281 0.68, 0.72, 0.76, 0.8, 0.84, 0.88, 0.92, 0.96, 0.98999};
282 
283 static const int ncx_sub2 = 63+2; // points in eta + 2
284 static const int ncy_sub2 = 37+2; // points in chi1 + 2
285 static const int ncz_sub2 = 51+2; // points in chi2 + 2
286 
287 // sub3: high frequency, high chi1
288 static const double etavec_sub3[] = {0.01, 0.0105, 0.011, 0.0115, 0.0125, 0.015, 0.0175, 0.02, 0.0225, \
289 0.025, 0.0275, 0.03, 0.0325, 0.035, 0.0375, 0.04, 0.0425, 0.045, \
290 0.0475, 0.05, 0.0525, 0.055, 0.0575, 0.06, 0.0625, 0.065, 0.0675, \
291 0.07, 0.0725, 0.075, 0.0775, 0.08, 0.0825, 0.085, 0.0875, 0.09, \
292 0.0925, 0.095, 0.0975, 0.1, 0.1025, 0.105, 0.1075, 0.11, 0.1125, \
293 0.115, 0.1175, 0.12, 0.1225, 0.125, 0.1275, 0.13, 0.1325, 0.135, \
294 0.1375, 0.14, 0.1425, 0.145, 0.1475, 0.15, 0.1525, 0.155, 0.1575, \
295 0.16, 0.1625, 0.165, 0.1675, 0.17, 0.1725, 0.175, 0.1775, 0.18, \
296 0.1825, 0.185, 0.1875, 0.19, 0.1925, 0.195, 0.1975, 0.2, 0.2025, \
297 0.205, 0.2075, 0.21, 0.2125, 0.215, 0.2175, 0.22, 0.2225, 0.225, \
298 0.2275, 0.23, 0.2325, 0.235, 0.2375, 0.24, 0.2425, 0.245, 0.24625, \
299 0.2475, 0.2485, 0.249, 0.2495, 0.2498, 0.2499, 0.25};
300 static const double chi1vec_sub3[] = {0.4, 0.405, 0.41, 0.415, 0.42, 0.425, 0.43, 0.435, 0.44, 0.445, \
301 0.45, 0.455, 0.46, 0.465, 0.47, 0.475, 0.48, 0.485, 0.49, 0.495, 0.5, \
302 0.505, 0.51, 0.515, 0.52, 0.525, 0.53, 0.535, 0.54, 0.545, 0.55, \
303 0.555, 0.56, 0.565, 0.57, 0.575, 0.58, 0.585, 0.59, 0.595, 0.6, \
304 0.605, 0.61, 0.615, 0.62, 0.625, 0.63, 0.635, 0.64, 0.645, 0.65, \
305 0.655, 0.66, 0.665, 0.67, 0.675, 0.68, 0.685, 0.69, 0.695, 0.7, \
306 0.705, 0.71, 0.715, 0.72, 0.725, 0.73, 0.735, 0.74, 0.745, 0.75, \
307 0.755, 0.76, 0.765, 0.77, 0.775, 0.78, 0.785, 0.79, 0.795, 0.8, \
308 0.805, 0.81, 0.815, 0.82, 0.825, 0.83, 0.835, 0.84, 0.845, 0.85, \
309 0.855, 0.86, 0.865, 0.87, 0.875, 0.88, 0.885, 0.89, 0.895, 0.9, \
310 0.905, 0.91, 0.915, 0.92, 0.925, 0.93, 0.935, 0.94, 0.945, 0.95, \
311 0.955, 0.96, 0.965, 0.97, 0.975, 0.98, 0.985, 0.98999};
312 static const double chi2vec_sub3[] = {-0.9999, -0.96, -0.92, -0.88, -0.84, -0.8, -0.76, -0.72, -0.68, \
313 -0.64, -0.6, -0.56, -0.52, -0.48, -0.44, -0.4, -0.36, -0.32, -0.28, \
314 -0.24, -0.2, -0.16, -0.12, -0.08, -0.04, 0., 0.04, 0.08, 0.12, 0.16, \
315 0.2, 0.24, 0.28, 0.32, 0.36, 0.4, 0.44, 0.48, 0.52, 0.56, 0.6, 0.64, \
316 0.68, 0.72, 0.76, 0.8, 0.84, 0.88, 0.92, 0.96, 0.98999};
317 
318 static const int ncx_sub3 = 106+2; // points in eta + 2
319 static const int ncy_sub3 = 119+2; // points in chi1 + 2
320 static const int ncz_sub3 = 51+2; // points in chi2 + 2
321 
322 #ifdef LAL_PTHREAD_LOCK
323 static pthread_once_t SEOBNRv2ROMDoubleSpin_is_initialized = PTHREAD_ONCE_INIT;
324 #endif
325 
326 /*************** type definitions ******************/
327 
328 typedef struct tagSEOBNRROMdataDS_coeff
329 {
330  gsl_vector* c_amp;
331  gsl_vector* c_phi;
333 
335 {
336  gsl_vector* cvec_amp; // Flattened amplitude projection coefficients
337  gsl_vector* cvec_phi; // Flattened phase projection coefficients
338  gsl_matrix *Bamp; // Reduced SVD basis for amplitude
339  gsl_matrix *Bphi; // Reduced SVD basis for phase
340  gsl_vector* cvec_amp_pre; // AMplitude prefactor coefficient
341  int nk_amp; // Number frequency points for amplitude
342  int nk_phi; // Number of frequency points for phase
343  const double *gA; // Sparse frequency points for amplitude
344  const double *gPhi; // Sparse frequency points for phase
345  const double *etavec; // B-spline knots in eta
346  const double *chi1vec; // B-spline knots in chi1
347  const double *chi2vec; // B-spline knots in chi2
348  int ncx, ncy, ncz; // Number of points in eta, chi1, chi2
349 };
350 typedef struct tagSEOBNRROMdataDS_submodel SEOBNRROMdataDS_submodel;
351 
352 struct tagSEOBNRROMdataDS
353 {
354  UINT4 setup;
355  SEOBNRROMdataDS_submodel* sub1;
356  SEOBNRROMdataDS_submodel* sub2;
357  SEOBNRROMdataDS_submodel* sub3;
358 };
359 typedef struct tagSEOBNRROMdataDS SEOBNRROMdataDS;
360 
361 static SEOBNRROMdataDS __lalsim_SEOBNRv2ROMDS_data;
362 
363 typedef int (*load_dataPtr)(const char*, gsl_vector *, gsl_vector *, gsl_matrix *, gsl_matrix *, gsl_vector *);
364 
365 typedef struct tagSplineData
366 {
367  gsl_bspline_workspace *bwx;
368  gsl_bspline_workspace *bwy;
369  gsl_bspline_workspace *bwz;
370 } SplineData;
371 
372 /**************** Internal functions **********************/
373 
374 static void SEOBNRv2ROMDoubleSpin_Init_LALDATA(void);
375 static int SEOBNRv2ROMDoubleSpin_Init(const char dir[]);
376 static bool SEOBNRv2ROMDoubleSpin_IsSetup(void);
377 
378 static int SEOBNRROMdataDS_Init(SEOBNRROMdataDS *romdata, const char dir[]);
379 static void SEOBNRROMdataDS_Cleanup(SEOBNRROMdataDS *romdata);
380 
381 static int TP_Spline_interpolation_3d(
382  REAL8 eta, // Input: eta-value for which projection coefficients should be evaluated
383  REAL8 chi1, // Input: chi1-value for which projection coefficients should be evaluated
384  REAL8 chi2, // Input: chi2-value for which projection coefficients should be evaluated
385  gsl_vector *cvec_amp, // Input: data for spline coefficients for amplitude
386  gsl_vector *cvec_phi, // Input: data for spline coefficients for phase
387  gsl_vector *cvec_amp_pre, // Input: data for spline coefficients for amplitude prefactor
388  int nk_amp, // number of SVD-modes == number of basis functions for amplitude
389  int nk_phi, // number of SVD-modes == number of basis functions for phase
390  int nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
391  int ncx, // Number of points in eta + 2
392  int ncy, // Number of points in chi1 + 2
393  int ncz, // Number of points in chi2 + 2
394  const double *etavec, // B-spline knots in eta
395  const double *chi1vec, // B-spline knots in chi1
396  const double *chi2vec, // B-spline knots in chi2
397  gsl_vector *c_amp, // Output: interpolated projection coefficients for amplitude
398  gsl_vector *c_phi, // Output: interpolated projection coefficients for phase
399  REAL8 *amp_pre // Output: interpolated amplitude prefactor
400 );
401 
403  SEOBNRROMdataDS_submodel **submodel,
404  const int nk_amp,
405  const int nk_phi,
406  const double *gA,
407  const double *gPhi,
408  const double *etavec,
409  const double *chi1vec,
410  const double *chi2vec,
411  const int ncx,
412  const int ncy,
413  const int ncz,
414  const char dir[],
416 );
417 
418 static void SEOBNRROMdataDS_Cleanup_submodel(SEOBNRROMdataDS_submodel *submodel);
419 
420 /**
421  * Core function for computing the ROM waveform.
422  * Interpolate projection coefficient data and evaluate coefficients at desired (q, chi).
423  * Construct 1D splines for amplitude and phase.
424  * Compute strain waveform from amplitude and phase.
425 */
426 static int SEOBNRv2ROMDoubleSpinCore(
427  COMPLEX16FrequencySeries **hptilde,
428  COMPLEX16FrequencySeries **hctilde,
429  double phiRef,
430  double fRef,
431  double distance,
432  double inclination,
433  double Mtot_sec,
434  double eta,
435  double chi1,
436  double chi2,
437  const REAL8Sequence *freqs, /* Frequency points at which to evaluate the waveform (Hz) */
438  double deltaF,
439  /* If deltaF > 0, the frequency points given in freqs are uniformly spaced with
440  * spacing deltaF. Otherwise, the frequency points are spaced non-uniformly.
441  * Then we will use deltaF = 0 to create the frequency series we return. */
442  int nk_max // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
443 );
444 
445 static void SEOBNRROMdataDS_coeff_Init(SEOBNRROMdataDS_coeff **romdatacoeff, int nk_amp, int nk_phi);
446 static void SEOBNRROMdataDS_coeff_Cleanup(SEOBNRROMdataDS_coeff *romdatacoeff);
447 
448 static size_t NextPow2(const size_t n);
449 static void SplineData_Destroy(SplineData *splinedata);
450 static void SplineData_Init(
451  SplineData **splinedata,
452  int ncx, // Number of points in eta + 2
453  int ncy, // Number of points in chi1 + 2
454  int ncz, // Number of points in chi2 + 2
455  const double *etavec, // B-spline knots in eta
456  const double *chi1vec, // B-spline knots in chi1
457  const double *chi2vec // B-spline knots in chi2
458 );
459 
460 static int load_data_sub1(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre);
461 static int load_data_sub2(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre);
462 static int load_data_sub3(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre);
463 
465  gsl_spline **spline_phi, // phase spline
466  gsl_interp_accel **acc_phi, // phase spline accelerator
467  REAL8 *Mf_final, // ringdown frequency in Mf
468  REAL8 *Mtot_sec, // total mass in seconds
469  REAL8 m1SI, // Mass of companion 1 (kg)
470  REAL8 m2SI, // Mass of companion 2 (kg)
471  REAL8 chi1, // Aligned spin of companion 1
472  REAL8 chi2 // Aligned spin of companion 2
473 );
474 
476  gsl_vector *v,
477  REAL8 eta,
478  REAL8 chi,
479  int ncx,
480  int ncy,
481  gsl_bspline_workspace *bwx,
482  gsl_bspline_workspace *bwy
483 );
484 
485 static void GluePhasing(
486  // INPUTS
487  SEOBNRROMdataDS_submodel *submodel_lo,
488  SEOBNRROMdataDS_submodel *submodel_hi,
489  gsl_vector* phi_f_lo,
490  gsl_vector* phi_f_hi,
491  const double Mfm,
492  // OUTPUTS
493  gsl_interp_accel **acc_phi_out,
494  gsl_spline **spline_phi_out
495 );
496 
497 
498 /********************* Definitions begin here ********************/
499 
500 /** Setup SEOBNRv2ROMDoubleSpin model using data files installed in dir
501  */
502 static int SEOBNRv2ROMDoubleSpin_Init(const char dir[]) {
503  if(__lalsim_SEOBNRv2ROMDS_data.setup) {
504  XLALPrintError("Error: DSEOBNRROMdata was already set up!");
506  }
507 
509 
510  if(__lalsim_SEOBNRv2ROMDS_data.setup) {
511  return(XLAL_SUCCESS);
512  }
513  else {
514  return(XLAL_EFAILED);
515  }
516 }
517 
518 /** Helper function to check if the SEOBNRv2ROMDoubleSpin model has been initialised */
519 static bool SEOBNRv2ROMDoubleSpin_IsSetup(void) {
521  return true;
522  else
523  return false;
524 }
525 
526 
527 // Read binary ROM data for basis functions and coefficients for submodel 1
528 static int load_data_sub1(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre) {
529  // Load binary data for amplitude and phase spline coefficients and reduced bases as computed in Mathematica
530  // "Core Low frequency" submodel 1
531  // B-spline points: 67x12x12
532  // Frequency points: {183, 242}
533 
534  // Read data into preallocated gsl_vectors and gsl_matrices
535  int ret = XLAL_SUCCESS;
536 #ifdef LAL_HDF5_ENABLED
537  size_t size = strlen(dir) + strlen(ROMDataHDF5) + 2;
538  char *path = XLALMalloc(size);
539  snprintf(path, size, "%s/%s", dir, ROMDataHDF5);
540 
542  LALH5File *sub1 = XLALH5GroupOpen(file, "sub1");
543 
544  // Read ROM coefficients
545  ReadHDF5RealVectorDataset(sub1, "Amp_ciall", &cvec_amp);
546  ReadHDF5RealVectorDataset(sub1, "Phase_ciall", &cvec_phi);
547  ReadHDF5RealVectorDataset(sub1, "AmpPrefac_ci", &cvec_amp_pre);
548  // Read ROM basis functions
549  ReadHDF5RealMatrixDataset(sub1, "Bamp", &Bamp);
550  ReadHDF5RealMatrixDataset(sub1, "Bphase", &Bphi);
551 
552  // Check frequency points
553  CheckVectorFromHDF5(sub1, "Mf_grid_Amp", gA_sub1, nk_amp_sub1);
554  CheckVectorFromHDF5(sub1, "Mf_grid_Phi", gPhi_sub1, nk_phi_sub1);
555  // Check parameter space nodes
556  CheckVectorFromHDF5(sub1, "etavec", etavec_sub1, ncx_sub1-2);
557  CheckVectorFromHDF5(sub1, "chi1vec", chi1vec_sub1, ncy_sub1-2);
558  CheckVectorFromHDF5(sub1, "chi2vec", chi2vec_sub1, ncz_sub1-2);
559 
560  XLALFree(path);
562 #else
563  // fall back to reading gsl binary data
564  ret |= read_vector(dir, "SEOBNRv2ROM_DS_HI_sub1_Amp_ciall.dat", cvec_amp);
565  ret |= read_vector(dir, "SEOBNRv2ROM_DS_HI_sub1_Phase_ciall.dat", cvec_phi);
566  ret |= read_matrix(dir, "SEOBNRv2ROM_DS_HI_sub1_Bamp_bin.dat", Bamp);
567  ret |= read_matrix(dir, "SEOBNRv2ROM_DS_HI_sub1_Bphase_bin.dat", Bphi);
568  ret |= read_vector(dir, "SEOBNRv2ROM_DS_HI_sub1_AmpPrefac_ci.dat", cvec_amp_pre);
569 #endif
570  return(ret);
571 }
572 
573 // Read binary ROM data for basis functions and coefficients for submodel 2
574 static int load_data_sub2(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre) {
575  // Load binary data for amplitude and phase spline coefficients and reduced bases as computed in Mathematica
576  // "High frequency, low chi1" submodel 2
577  // B-spline points: 63x37x51
578  // Frequency points: {113, 113}
579 
580  // Read data into preallocated gsl_vectors and gsl_matrices
581  int ret = XLAL_SUCCESS;
582 #ifdef LAL_HDF5_ENABLED
583  size_t size = strlen(dir) + strlen(ROMDataHDF5) + 2;
584  char *path = XLALMalloc(size);
585  snprintf(path, size, "%s/%s", dir, ROMDataHDF5);
586 
588  LALH5File *sub2 = XLALH5GroupOpen(file, "sub2");
589 
590  // Read ROM coefficients
591  ReadHDF5RealVectorDataset(sub2, "Amp_ciall", &cvec_amp);
592  ReadHDF5RealVectorDataset(sub2, "Phase_ciall", &cvec_phi);
593  ReadHDF5RealVectorDataset(sub2, "AmpPrefac_ci", &cvec_amp_pre);
594  // Read ROM basis functions
595  ReadHDF5RealMatrixDataset(sub2, "Bamp", &Bamp);
596  ReadHDF5RealMatrixDataset(sub2, "Bphase", &Bphi);
597 
598  // Check frequency points
599  CheckVectorFromHDF5(sub2, "Mf_grid_Amp", gA_sub2, nk_amp_sub2);
600  CheckVectorFromHDF5(sub2, "Mf_grid_Phi", gPhi_sub2, nk_phi_sub2);
601  // Check parameter space nodes
602  CheckVectorFromHDF5(sub2, "etavec", etavec_sub2, ncx_sub2-2);
603  CheckVectorFromHDF5(sub2, "chi1vec", chi1vec_sub2, ncy_sub2-2);
604  CheckVectorFromHDF5(sub2, "chi2vec", chi2vec_sub2, ncz_sub2-2);
605 
607  XLALFree(path);
608 #else
609  // fall back to reading gsl binary data
610  ret |= read_vector(dir, "SEOBNRv2ROM_DS_HI_sub2_Amp_ciall.dat", cvec_amp);
611  ret |= read_vector(dir, "SEOBNRv2ROM_DS_HI_sub2_Phase_ciall.dat", cvec_phi);
612  ret |= read_matrix(dir, "SEOBNRv2ROM_DS_HI_sub2_Bamp_bin.dat", Bamp);
613  ret |= read_matrix(dir, "SEOBNRv2ROM_DS_HI_sub2_Bphase_bin.dat", Bphi);
614  ret |= read_vector(dir, "SEOBNRv2ROM_DS_HI_sub2_AmpPrefac_ci.dat", cvec_amp_pre);
615 #endif
616  return(ret);
617 }
618 
619 // Read binary ROM data for basis functions and coefficients for submodel 3
620 static int load_data_sub3(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre) {
621  // Load binary data for amplitude and phase spline coefficients and reduced bases as computed in Mathematica
622  // "High frequency, high chi1" submodel 3
623  // B-spline points: 97x119x51
624  // frequency points: {113, 113}
625 
626  // Read data into preallocated gsl_vectors and gsl_matrices
627  int ret = XLAL_SUCCESS;
628 #ifdef LAL_HDF5_ENABLED
629  size_t size = strlen(dir) + strlen(ROMDataHDF5) + 2;
630  char *path = XLALMalloc(size);
631  snprintf(path, size, "%s/%s", dir, ROMDataHDF5);
632 
634  LALH5File *sub3 = XLALH5GroupOpen(file, "sub3");
635 
636  // Read ROM coefficients
637  ReadHDF5RealVectorDataset(sub3, "Amp_ciall", &cvec_amp);
638  ReadHDF5RealVectorDataset(sub3, "Phase_ciall", &cvec_phi);
639  ReadHDF5RealVectorDataset(sub3, "AmpPrefac_ci", &cvec_amp_pre);
640  // Read ROM basis functions
641  ReadHDF5RealMatrixDataset(sub3, "Bamp", &Bamp);
642  ReadHDF5RealMatrixDataset(sub3, "Bphase", &Bphi);
643 
644  // Check frequency points
645  CheckVectorFromHDF5(sub3, "Mf_grid_Amp", gA_sub3, nk_amp_sub3);
646  CheckVectorFromHDF5(sub3, "Mf_grid_Phi", gPhi_sub3, nk_phi_sub3);
647  // Check parameter space nodes
648  CheckVectorFromHDF5(sub3, "etavec", etavec_sub3, ncx_sub3-2);
649  CheckVectorFromHDF5(sub3, "chi1vec", chi1vec_sub3, ncy_sub3-2);
650  CheckVectorFromHDF5(sub3, "chi2vec", chi2vec_sub3, ncz_sub3-2);
651 
653  XLALFree(path);
654 #else
655  // fall back to reading gsl binary data
656  ret |= read_vector(dir, "SEOBNRv2ROM_DS_HI_sub3_Amp_ciall.dat", cvec_amp);
657  ret |= read_vector(dir, "SEOBNRv2ROM_DS_HI_sub3_Phase_ciall.dat", cvec_phi);
658  ret |= read_matrix(dir, "SEOBNRv2ROM_DS_HI_sub3_Bamp_bin.dat", Bamp);
659  ret |= read_matrix(dir, "SEOBNRv2ROM_DS_HI_sub3_Bphase_bin.dat", Bphi);
660  ret |= read_vector(dir, "SEOBNRv2ROM_DS_HI_sub3_AmpPrefac_ci.dat", cvec_amp_pre);
661 #endif
662  return(ret);
663 }
664 
665 // Setup B-spline basis functions for given points
666 static void SplineData_Init(
667  SplineData **splinedata,
668  int ncx, // Number of points in eta + 2
669  int ncy, // Number of points in chi1 + 2
670  int ncz, // Number of points in chi2 + 2
671  const double *etavec, // B-spline knots in eta
672  const double *chi1vec, // B-spline knots in chi1
673  const double *chi2vec // B-spline knots in chi2
674 )
675 {
676  if(!splinedata) exit(1);
677  if(*splinedata) SplineData_Destroy(*splinedata);
678 
679  (*splinedata)=XLALCalloc(1,sizeof(SplineData));
680 
681  // Set up B-spline basis for desired knots
682  const size_t nbreak_x = ncx-2; // must have nbreak = n-2 for cubic splines
683  const size_t nbreak_y = ncy-2; // must have nbreak = n-2 for cubic splines
684  const size_t nbreak_z = ncz-2; // must have nbreak = n-2 for cubic splines
685 
686  // Allocate a cubic bspline workspace (k = 4)
687  gsl_bspline_workspace *bwx = gsl_bspline_alloc(4, nbreak_x);
688  gsl_bspline_workspace *bwy = gsl_bspline_alloc(4, nbreak_y);
689  gsl_bspline_workspace *bwz = gsl_bspline_alloc(4, nbreak_z);
690 
691  // Set breakpoints (and thus knots by hand)
692  gsl_vector *breakpts_x = gsl_vector_alloc(nbreak_x);
693  gsl_vector *breakpts_y = gsl_vector_alloc(nbreak_y);
694  gsl_vector *breakpts_z = gsl_vector_alloc(nbreak_z);
695  for (UINT4 i=0; i<nbreak_x; i++)
696  gsl_vector_set(breakpts_x, i, etavec[i]);
697  for (UINT4 j=0; j<nbreak_y; j++)
698  gsl_vector_set(breakpts_y, j, chi1vec[j]);
699  for (UINT4 k=0; k<nbreak_z; k++)
700  gsl_vector_set(breakpts_z, k, chi2vec[k]);
701 
702  gsl_bspline_knots(breakpts_x, bwx);
703  gsl_bspline_knots(breakpts_y, bwy);
704  gsl_bspline_knots(breakpts_z, bwz);
705 
706  gsl_vector_free(breakpts_x);
707  gsl_vector_free(breakpts_y);
708  gsl_vector_free(breakpts_z);
709 
710  (*splinedata)->bwx=bwx;
711  (*splinedata)->bwy=bwy;
712  (*splinedata)->bwz=bwz;
713 }
714 
715 static void SplineData_Destroy(SplineData *splinedata)
716 {
717  if(!splinedata) return;
718  if(splinedata->bwx) gsl_bspline_free(splinedata->bwx);
719  if(splinedata->bwy) gsl_bspline_free(splinedata->bwy);
720  if(splinedata->bwz) gsl_bspline_free(splinedata->bwz);
721  XLALFree(splinedata);
722 }
723 
724 // Interpolate projection coefficients for amplitude and phase over the parameter space (q, chi).
725 // The multi-dimensional interpolation is carried out via a tensor product decomposition.
727  REAL8 eta, // Input: eta-value for which projection coefficients should be evaluated
728  REAL8 chi1, // Input: chi1-value for which projection coefficients should be evaluated
729  REAL8 chi2, // Input: chi2-value for which projection coefficients should be evaluated
730  gsl_vector *cvec_amp, // Input: data for spline coefficients for amplitude
731  gsl_vector *cvec_phi, // Input: data for spline coefficients for phase
732  gsl_vector *cvec_amp_pre, // Input: data for spline coefficients for amplitude prefactor
733  int nk_amp, // number of SVD-modes == number of basis functions for amplitude
734  int nk_phi, // number of SVD-modes == number of basis functions for phase
735  int nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
736  int ncx, // Number of points in eta + 2
737  int ncy, // Number of points in chi1 + 2
738  int ncz, // Number of points in chi2 + 2
739  const double *etavec, // B-spline knots in eta
740  const double *chi1vec, // B-spline knots in chi1
741  const double *chi2vec, // B-spline knots in chi2
742  gsl_vector *c_amp, // Output: interpolated projection coefficients for amplitude
743  gsl_vector *c_phi, // Output: interpolated projection coefficients for phase
744  REAL8 *amp_pre // Output: interpolated amplitude prefactor
745 ) {
746  if (nk_max != -1) {
747  if (nk_max > nk_amp || nk_max > nk_phi)
748  XLAL_ERROR(XLAL_EDOM, "Truncation parameter nk_max %d must be smaller or equal to nk_amp %d and nk_phi %d", nk_max, nk_amp, nk_phi);
749  else { // truncate SVD modes
750  nk_amp = nk_max;
751  nk_phi = nk_max;
752  }
753  }
754 
755  SplineData *splinedata=NULL;
756  SplineData_Init(&splinedata, ncx, ncy, ncz, etavec, chi1vec, chi2vec);
757 
758  gsl_bspline_workspace *bwx=splinedata->bwx;
759  gsl_bspline_workspace *bwy=splinedata->bwy;
760  gsl_bspline_workspace *bwz=splinedata->bwz;
761 
762  int N = ncx*ncy*ncz; // Size of the data matrix for one SVD-mode
763  // Evaluate the TP spline for all SVD modes - amplitude
764  for (int k=0; k<nk_amp; k++) { // For each SVD mode
765  gsl_vector v = gsl_vector_subvector(cvec_amp, k*N, N).vector; // Pick out the coefficient matrix corresponding to the k-th SVD mode.
766  REAL8 csum = Interpolate_Coefficent_Tensor(&v, eta, chi1, chi2, ncy, ncz, bwx, bwy, bwz);
767  gsl_vector_set(c_amp, k, csum);
768  }
769 
770  // Evaluate the TP spline for all SVD modes - phase
771  for (int k=0; k<nk_phi; k++) { // For each SVD mode
772  gsl_vector v = gsl_vector_subvector(cvec_phi, k*N, N).vector; // Pick out the coefficient matrix corresponding to the k-th SVD mode.
773  REAL8 csum = Interpolate_Coefficent_Tensor(&v, eta, chi1, chi2, ncy, ncz, bwx, bwy, bwz);
774  gsl_vector_set(c_phi, k, csum);
775  }
776 
777  // Evaluate the TP spline for the amplitude prefactor
778  *amp_pre = Interpolate_Coefficent_Tensor(cvec_amp_pre, eta, chi1, chi2, ncy, ncz, bwx, bwy, bwz);
779 
780  SplineData_Destroy(splinedata);
781 
782  return(0);
783 }
784 
785 /* Set up a new ROM submodel, using data contained in dir */
787  SEOBNRROMdataDS_submodel **submodel,
788  const int nk_amp,
789  const int nk_phi,
790  const double *gA,
791  const double *gPhi,
792  const double *etavec,
793  const double *chi1vec,
794  const double *chi2vec,
795  const int ncx,
796  const int ncy,
797  const int ncz,
798  const char dir[],
800 ) {
801  int ret = XLAL_FAILURE;
802 
803  if(!submodel) exit(1);
804  /* Create storage for submodel structures */
805  if (!*submodel)
806  *submodel = XLALCalloc(1,sizeof(SEOBNRROMdataDS_submodel));
807  else
809 
810  int N = ncx*ncy*ncz; // Total number of points over parameter space = size of the data matrix for one SVD-mode
811 
812  // Initalize actual ROM data
813  // Note: At the moment the sizes of the vectors & matrices are hardwired; they could also be read from HDF5.
814  (*submodel)->cvec_amp = gsl_vector_alloc(N*nk_amp);
815  (*submodel)->cvec_phi = gsl_vector_alloc(N*nk_phi);
816  (*submodel)->Bamp = gsl_matrix_alloc(nk_amp, nk_amp);
817  (*submodel)->Bphi = gsl_matrix_alloc(nk_phi, nk_phi);
818  (*submodel)->cvec_amp_pre = gsl_vector_alloc(N);
819 
820  // Load ROM data for this submodel
821  ret=load_data(dir, (*submodel)->cvec_amp, (*submodel)->cvec_phi, (*submodel)->Bamp, (*submodel)->Bphi, (*submodel)->cvec_amp_pre);
822 
823  // Initialize other members
824  (*submodel)->nk_amp = nk_amp;
825  (*submodel)->nk_phi = nk_phi;
826  (*submodel)->gA = gA;
827  (*submodel)->gPhi = gPhi;
828  (*submodel)->etavec = etavec;
829  (*submodel)->chi1vec = chi1vec;
830  (*submodel)->chi2vec = chi2vec;
831  (*submodel)->ncx = ncx;
832  (*submodel)->ncy = ncy;
833  (*submodel)->ncz = ncz;
834 
835  return ret;
836 }
837 
838 /* Deallocate contents of the given SEOBNRROMdataDS_submodel structure */
839 static void SEOBNRROMdataDS_Cleanup_submodel(SEOBNRROMdataDS_submodel *submodel) {
840  if(submodel->cvec_amp) gsl_vector_free(submodel->cvec_amp);
841  if(submodel->cvec_phi) gsl_vector_free(submodel->cvec_phi);
842  if(submodel->Bamp) gsl_matrix_free(submodel->Bamp);
843  if(submodel->Bphi) gsl_matrix_free(submodel->Bphi);
844  if(submodel->cvec_amp_pre) gsl_vector_free(submodel->cvec_amp_pre);
845 }
846 
847 /* Set up a new ROM model, using data contained in dir */
848 int SEOBNRROMdataDS_Init(SEOBNRROMdataDS *romdata, const char dir[]) {
849  int ret = XLAL_FAILURE;
850 
851  /* Create storage for structures */
852  if(romdata->setup) {
853  XLALPrintError("WARNING: You tried to setup the SEOBNRv2ROMDoubleSpin model that was already initialised. Ignoring\n");
854  return (XLAL_FAILURE);
855  }
856 
857 #ifdef LAL_HDF5_ENABLED
858  // First, check we got the correct version number
859  size_t size = strlen(dir) + strlen(ROMDataHDF5) + 2;
860  char *path = XLALMalloc(size);
861  snprintf(path, size, "%s/%s", dir, ROMDataHDF5);
863 
864  ret = ROM_check_version_number(file, ROMDataHDF5_VERSION_MAJOR, ROMDataHDF5_VERSION_MINOR, ROMDataHDF5_VERSION_MICRO);
865  PrintInfoStringAttribute(file, "Email");
866  PrintInfoStringAttribute(file, "Description");
867 
868  XLALFree(path);
870 #endif
871 
873  ret = SEOBNRROMdataDS_Init_submodel(&(romdata)->sub1, nk_amp_sub1, nk_phi_sub1,
875  if (ret==XLAL_SUCCESS) XLALPrintInfo("%s : submodel 1 loaded sucessfully.\n", __func__);
876 
878  ret |= SEOBNRROMdataDS_Init_submodel(&(romdata)->sub2, nk_amp_sub2, nk_phi_sub2,
880  if (ret==XLAL_SUCCESS) XLALPrintInfo("%s : submodel 2 loaded sucessfully.\n", __func__);
881 
883  ret |= SEOBNRROMdataDS_Init_submodel(&(romdata)->sub3, nk_amp_sub3, nk_phi_sub3,
885  if (ret==XLAL_SUCCESS) XLALPrintInfo("%s : submodel 3 loaded sucessfully.\n", __func__);
886 
887  if(XLAL_SUCCESS==ret)
888  romdata->setup=1;
889  else
890  SEOBNRROMdataDS_Cleanup(romdata);
891 
892  return (ret);
893 }
894 
895 /* Deallocate contents of the given SEOBNRROMdataDS structure */
896 static void SEOBNRROMdataDS_Cleanup(SEOBNRROMdataDS *romdata) {
897  SEOBNRROMdataDS_Cleanup_submodel((romdata)->sub1);
898  XLALFree((romdata)->sub1);
899  (romdata)->sub1 = NULL;
900  romdata->setup=0;
901 }
902 
903 /* Structure for internal use */
904 static void SEOBNRROMdataDS_coeff_Init(SEOBNRROMdataDS_coeff **romdatacoeff, int nk_amp, int nk_phi) {
905  if(!romdatacoeff) exit(1);
906  /* Create storage for structures */
907  if(!*romdatacoeff)
908  *romdatacoeff=XLALCalloc(1,sizeof(SEOBNRROMdataDS_coeff));
909  else
910  SEOBNRROMdataDS_coeff_Cleanup(*romdatacoeff);
911 
912  (*romdatacoeff)->c_amp = gsl_vector_alloc(nk_amp);
913  (*romdatacoeff)->c_phi = gsl_vector_alloc(nk_phi);
914 }
915 
916 /* Deallocate contents of the given SEOBNRROMdataDS_coeff structure */
918  if(romdatacoeff->c_amp) gsl_vector_free(romdatacoeff->c_amp);
919  if(romdatacoeff->c_phi) gsl_vector_free(romdatacoeff->c_phi);
920  XLALFree(romdatacoeff);
921 }
922 
923 /* Return the closest higher power of 2 */
924 // Note: NextPow(2^k) = 2^k for integer values k.
925 static size_t NextPow2(const size_t n) {
926  return 1 << (size_t) ceil(log2(n));
927 }
928 
929 static void GlueAmplitude(
930  // INPUTS
931  SEOBNRROMdataDS_submodel *submodel_lo,
932  SEOBNRROMdataDS_submodel *submodel_hi,
933  gsl_vector* amp_f_lo,
934  gsl_vector* amp_f_hi,
935  double amp_pre_lo,
936  double amp_pre_hi,
937  const double Mfm,
938  // OUTPUTS
939  gsl_interp_accel **acc_amp,
940  gsl_spline **spline_amp
941 );
942 
943 static void GlueAmplitude(
944  // INPUTS
945  SEOBNRROMdataDS_submodel *submodel_lo,
946  SEOBNRROMdataDS_submodel *submodel_hi,
947  gsl_vector* amp_f_lo,
948  gsl_vector* amp_f_hi,
949  double amp_pre_lo,
950  double amp_pre_hi,
951  const double Mfm,
952  // OUTPUTS
953  gsl_interp_accel **acc_amp,
954  gsl_spline **spline_amp
955 ) {
956  // First need to find overlaping frequency interval
957  int jA_lo;
958  // Find index so that Mf < Mfm
959  for (jA_lo=0; jA_lo < submodel_lo->nk_amp; jA_lo++) {
960  if (submodel_lo->gA[jA_lo] > Mfm) {
961  jA_lo--;
962  break;
963  }
964  }
965 
966  int jA_hi;
967  // Find index so that Mf > Mfm
968  for (jA_hi=0; jA_hi < submodel_hi->nk_amp; jA_hi++)
969  if (submodel_hi->gA[jA_hi] > Mfm)
970  break;
971 
972  int nA = 1 + jA_lo + (submodel_hi->nk_amp - jA_hi); // length of the union of frequency points of the low and high frequency models glued at MfM
973 
974  gsl_vector *gAU = gsl_vector_alloc(nA); // glued frequency grid
975  gsl_vector *amp_f = gsl_vector_alloc(nA); // amplitude on glued frequency grid
976  // Note: We don't interpolate the amplitude, but this may already be smooth enough for practical purposes.
977  // To improve this we would evaluate both amplitue splines times the prefactor at the matching frequency and correct with the ratio, so we are C^0.
978  for (int i=0; i<=jA_lo; i++) {
979  gsl_vector_set(gAU, i, submodel_lo->gA[i]);
980  double A = amp_pre_lo * gsl_vector_get(amp_f_lo, i);
981  gsl_vector_set(amp_f, i, A);
982  }
983 
984  for (int i=jA_lo+1; i<nA; i++) {
985  int k = jA_hi - (jA_lo+1) + i;
986  gsl_vector_set(gAU, i, submodel_hi->gA[k]);
987  double A = amp_pre_hi * gsl_vector_get(amp_f_hi, k);
988  gsl_vector_set(amp_f, i, A);
989  }
990 
991  // Setup 1d splines in frequency from glued amplitude grids & data
992  *acc_amp = gsl_interp_accel_alloc();
993  *spline_amp = gsl_spline_alloc(gsl_interp_cspline, nA);
994  //gsl_spline_init(spline_amp, gAU->data, gsl_vector_const_ptr(amp_f,0), nA);
995  gsl_spline_init(*spline_amp, gsl_vector_const_ptr(gAU,0), gsl_vector_const_ptr(amp_f,0), nA);
996 
997  gsl_vector_free(gAU);
998  gsl_vector_free(amp_f);
999  gsl_vector_free(amp_f_lo);
1000  gsl_vector_free(amp_f_hi);
1001 }
1002 
1003 // Glue phasing in frequency to C^1 smoothness
1004 static void GluePhasing(
1005  // INPUTS
1006  SEOBNRROMdataDS_submodel *submodel_lo,
1007  SEOBNRROMdataDS_submodel *submodel_hi,
1008  gsl_vector* phi_f_lo,
1009  gsl_vector* phi_f_hi,
1010  const double Mfm,
1011  // OUTPUTS
1012  gsl_interp_accel **acc_phi,
1013  gsl_spline **spline_phi
1014 ) {
1015  // First need to find overlaping frequency interval
1016  int jP_lo;
1017  // Find index so that Mf < Mfm
1018  for (jP_lo=0; jP_lo < submodel_lo->nk_phi; jP_lo++) {
1019  if (submodel_lo->gPhi[jP_lo] > Mfm) {
1020  jP_lo--;
1021  break;
1022  }
1023  }
1024 
1025  int jP_hi;
1026  // Find index so that Mf > Mfm
1027  for (jP_hi=0; jP_hi < submodel_hi->nk_phi; jP_hi++)
1028  if (submodel_hi->gPhi[jP_hi] > Mfm)
1029  break;
1030 
1031  int nP = 1 + jP_lo + (submodel_hi->nk_phi - jP_hi); // length of the union of frequency points of the low and high frequency models glued at MfM
1032  gsl_vector *gPU = gsl_vector_alloc(nP); // glued frequency grid
1033  gsl_vector *phi_f = gsl_vector_alloc(nP); // phase on glued frequency grid
1034  // We need to do a bit more work to glue the phase with C^1 smoothness
1035  for (int i=0; i<=jP_lo; i++) {
1036  gsl_vector_set(gPU, i, submodel_lo->gPhi[i]);
1037  double P = gsl_vector_get(phi_f_lo, i);
1038  gsl_vector_set(phi_f, i, P);
1039  }
1040 
1041  for (int i=jP_lo+1; i<nP; i++) {
1042  int k = jP_hi - (jP_lo+1) + i;
1043  gsl_vector_set(gPU, i, submodel_hi->gPhi[k]);
1044  double P = gsl_vector_get(phi_f_hi, k);
1045  gsl_vector_set(phi_f, i, P);
1046  }
1047 
1048  // Set up phase data across the gluing frequency Mfm
1049  // We need to set up a spline for the low frequency model and evaluate at the designated points for the high frequency model so that we work with the *same* frequeny interval!
1050 
1051  // We could optimize this further by not constructing the whole spline for
1052  // submodel_lo, but this may be insignificant since the number of points is small anyway.
1053  gsl_interp_accel *acc_phi_lo = gsl_interp_accel_alloc();
1054  gsl_spline *spline_phi_lo = gsl_spline_alloc(gsl_interp_cspline, submodel_lo->nk_phi);
1055  gsl_spline_init(spline_phi_lo, submodel_lo->gPhi, gsl_vector_const_ptr(phi_f_lo,0), submodel_lo->nk_phi);
1056 
1057  const int nn = 15;
1058  gsl_vector_const_view gP_hi_data = gsl_vector_const_view_array(submodel_hi->gPhi + jP_hi - nn, 2*nn+1);
1059  gsl_vector_const_view P_hi_data = gsl_vector_const_subvector(phi_f_hi, jP_hi - nn, 2*nn+1);
1060  gsl_vector *P_lo_data = gsl_vector_alloc(2*nn+1);
1061  for (int i=0; i<2*nn+1; i++) {
1062  double P = gsl_spline_eval(spline_phi_lo, gsl_vector_get(&gP_hi_data.vector, i), acc_phi_lo);
1063  gsl_vector_set(P_lo_data, i, P);
1064  }
1065 
1066  // Fit phase data to cubic polynomial in frequency
1067  gsl_vector *cP_lo = Fit_cubic(&gP_hi_data.vector, P_lo_data);
1068  gsl_vector *cP_hi = Fit_cubic(&gP_hi_data.vector, &P_hi_data.vector);
1069 
1070  double P_lo_derivs[2];
1071  double P_hi_derivs[2];
1072  gsl_poly_eval_derivs(cP_lo->data, 4, Mfm, P_lo_derivs, 2);
1073  gsl_poly_eval_derivs(cP_hi->data, 4, Mfm, P_hi_derivs, 2);
1074 
1075  double delta_omega = P_hi_derivs[1] - P_lo_derivs[1];
1076  double delta_phi = P_hi_derivs[0] - P_lo_derivs[0] - delta_omega * Mfm;
1077 
1078  for (int i=jP_lo+1; i<nP; i++) {
1079  int k = jP_hi - (jP_lo+1) + i;
1080  double f = submodel_hi->gPhi[k];
1081  gsl_vector_set(gPU, i, f);
1082  double P = gsl_vector_get(phi_f_hi, k) - delta_omega * f - delta_phi; // Now correct phase of high frequency submodel
1083  gsl_vector_set(phi_f, i, P);
1084  }
1085 
1086  // free some vectors
1087  gsl_vector_free(P_lo_data);
1088  gsl_vector_free(cP_lo);
1089  gsl_vector_free(cP_hi);
1090  gsl_vector_free(phi_f_lo);
1091  gsl_vector_free(phi_f_hi);
1092 
1093  // Setup 1d splines in frequency from glued phase grids & data
1094  *acc_phi = gsl_interp_accel_alloc();
1095  *spline_phi = gsl_spline_alloc(gsl_interp_cspline, nP);
1096  //gsl_spline_init(spline_phi, gPU->data, gsl_vector_const_ptr(phi_f,0), nP);
1097  gsl_spline_init(*spline_phi, gsl_vector_const_ptr(gPU,0), gsl_vector_const_ptr(phi_f,0), nP);
1098 
1099  /**** Finished gluing ****/
1100 
1101  gsl_vector_free(phi_f);
1102  gsl_vector_free(gPU);
1103  gsl_spline_free(spline_phi_lo);
1104  gsl_interp_accel_free(acc_phi_lo);
1105 }
1106 
1107 
1108 
1109 /**
1110  * Core function for computing the ROM waveform.
1111  * Interpolate projection coefficient data and evaluate coefficients at desired (q, chi1, chi2).
1112  * Construct 1D splines for amplitude and phase.
1113  * Compute strain waveform from amplitude and phase.
1114 */
1116  COMPLEX16FrequencySeries **hptilde,
1117  COMPLEX16FrequencySeries **hctilde,
1118  double phiRef, // orbital reference phase
1119  double fRef,
1120  double distance,
1121  double inclination,
1122  double Mtot_sec,
1123  double eta,
1124  double chi1,
1125  double chi2,
1126  const REAL8Sequence *freqs_in, /* Frequency points at which to evaluate the waveform (Hz) */
1127  double deltaF,
1128  /* If deltaF > 0, the frequency points given in freqs are uniformly spaced with
1129  * spacing deltaF. Otherwise, the frequency points are spaced non-uniformly.
1130  * Then we will use deltaF = 0 to create the frequency series we return. */
1131  int nk_max // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1132  )
1133 {
1134 
1135  /* Check output arrays */
1136  if(!hptilde || !hctilde)
1138 
1139  SEOBNRROMdataDS *romdata=&__lalsim_SEOBNRv2ROMDS_data;
1142  "Error setting up SEOBNRv2ROMDoubleSpinHI data - check your $LAL_DATA_PATH\n");
1143  }
1144 
1145  if(*hptilde || *hctilde)
1146  {
1147  XLALPrintError("(*hptilde) and (*hctilde) are supposed to be NULL, but got %p and %p",(*hptilde),(*hctilde));
1149  }
1150  int retcode=0;
1151 
1152  // 'Nudge' parameter values to allowed boundary values if close by
1153  if (eta > 0.25) nudge(&eta, 0.25, 1e-6);
1154  if (eta < 0.01) nudge(&eta, 0.01, 1e-6);
1155  if (chi1 < -0.9999) nudge(&chi1, -0.9999, 1e-4);
1156  if (chi1 > 0.98999) nudge(&chi1, 0.98999, 1e-4);
1157  if (chi2 < -0.9999) nudge(&chi2, -0.9999, 1e-4);
1158  if (chi2 > 0.98999) nudge(&chi2, 0.98999, 1e-4);
1159 
1160  if ( chi1 < -1.0 || chi2 < -1.0 || chi1 > 0.99 || chi2 > 0.99) {
1161  XLALPrintError( "XLAL Error - %s: chi1 or chi2 smaller than -1.0 or larger than 0.99!\nSEOBNRv2ROMDoubleSpinHI is only available for spins in the range -1 <= a/M <= 0.99.\n", __func__);
1162  XLAL_ERROR( XLAL_EDOM );
1163  }
1164 
1165  if (eta<0.01 || eta > 0.25) {
1166  XLALPrintError( "XLAL Error - %s: eta (%f) smaller than 0.01 or unphysical!\nSEOBNRv2ROMDoubleSpin is only available for eta in the range 0.01 <= eta <= 0.25.\n", __func__,eta);
1167  XLAL_ERROR( XLAL_EDOM );
1168  }
1169 
1170  /* We always need to glue two submodels together for this ROM */
1171  SEOBNRROMdataDS_submodel *submodel_hi; // high frequency ROM
1172  SEOBNRROMdataDS_submodel *submodel_lo; // low frequency ROM
1173  submodel_lo = romdata->sub1;
1174  /* Select high frequency ROM submodel */
1175  if (chi1 < 0.41)
1176  submodel_hi = romdata->sub2;
1177  else
1178  submodel_hi = romdata->sub3;
1179 
1180 
1181  /* Find frequency bounds */
1182  if (!freqs_in) XLAL_ERROR(XLAL_EFAULT);
1183  double fLow = freqs_in->data[0];
1184  double fHigh = freqs_in->data[freqs_in->length - 1];
1185 
1186  if(fRef==0.0)
1187  fRef=fLow;
1188 
1189  /* Convert to geometric units for frequency */
1190  double Mf_ROM_min = fmax(submodel_lo->gA[0], submodel_lo->gPhi[0]); // lowest allowed geometric frequency for ROM
1191  double Mf_ROM_max = fmin(submodel_hi->gA[submodel_hi->nk_amp-1], submodel_hi->gPhi[submodel_hi->nk_phi-1]); // highest allowed geometric frequency for ROM
1192  double fLow_geom = fLow * Mtot_sec;
1193  double fHigh_geom = fHigh * Mtot_sec;
1194  double fRef_geom = fRef * Mtot_sec;
1195  double deltaF_geom = deltaF * Mtot_sec;
1196 
1197  // Enforce allowed geometric frequency range
1198  if (fLow_geom < Mf_ROM_min)
1199  XLAL_ERROR(XLAL_EDOM, "Starting frequency Mflow=%g is smaller than lowest frequency in ROM Mf=%g.\n", fLow_geom, Mf_ROM_min);
1200  if (fHigh_geom == 0 || fHigh_geom > Mf_ROM_max)
1201  fHigh_geom = Mf_ROM_max;
1202  else if (fHigh_geom < Mf_ROM_min)
1203  XLAL_ERROR(XLAL_EDOM, "End frequency %g is smaller than starting frequency %g!\n", fHigh_geom, fLow_geom);
1204  if (fRef_geom > Mf_ROM_max) {
1205  XLALPrintWarning("Reference frequency Mf_ref=%g is greater than maximal frequency in ROM Mf=%g. Starting at maximal frequency in ROM.\n", fRef_geom, Mf_ROM_max);
1206  fRef_geom = Mf_ROM_max; // If fref > fhigh we reset fref to default value of cutoff frequency.
1207  }
1208  if (fRef_geom < Mf_ROM_min) {
1209  XLALPrintWarning("Reference frequency Mf_ref=%g is smaller than lowest frequency in ROM Mf=%g. Starting at lowest frequency in ROM.\n", fLow_geom, Mf_ROM_min);
1210  fRef_geom = Mf_ROM_min;
1211  }
1212 
1213  if (Mtot_sec/LAL_MTSUN_SI > 500.0)
1214  XLALPrintWarning("Total mass=%gMsun > 500Msun. SEOBNRv2_ROM_DoubleSpin_HI disagrees with SEOBNRv2 for high total masses.\n", Mtot_sec/LAL_MTSUN_SI);
1215 
1216  /* Internal storage for waveform coefficiencts */
1217  SEOBNRROMdataDS_coeff *romdata_coeff_lo=NULL;
1218  SEOBNRROMdataDS_coeff *romdata_coeff_hi=NULL;
1219  SEOBNRROMdataDS_coeff_Init(&romdata_coeff_lo, submodel_lo->nk_amp, submodel_lo->nk_phi);
1220  SEOBNRROMdataDS_coeff_Init(&romdata_coeff_hi, submodel_hi->nk_amp, submodel_hi->nk_phi);
1221  REAL8 amp_pre_lo, amp_pre_hi;
1222 
1223  /* Interpolate projection coefficients and evaluate them at (eta,chi1,chi2) */
1225  eta, // Input: eta-value for which projection coefficients should be evaluated
1226  chi1, // Input: chi1-value for which projection coefficients should be evaluated
1227  chi2, // Input: chi2-value for which projection coefficients should be evaluated
1228  submodel_lo->cvec_amp, // Input: data for spline coefficients for amplitude
1229  submodel_lo->cvec_phi, // Input: data for spline coefficients for phase
1230  submodel_lo->cvec_amp_pre, // Input: data for spline coefficients for amplitude prefactor
1231  submodel_lo->nk_amp, // number of SVD-modes == number of basis functions for amplitude
1232  submodel_lo->nk_phi, // number of SVD-modes == number of basis functions for phase
1233  nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1234  submodel_lo->ncx, // Number of points in eta + 2
1235  submodel_lo->ncy, // Number of points in chi1 + 2
1236  submodel_lo->ncz, // Number of points in chi2 + 2
1237  submodel_lo->etavec, // B-spline knots in eta
1238  submodel_lo->chi1vec, // B-spline knots in chi1
1239  submodel_lo->chi2vec, // B-spline knots in chi2
1240  romdata_coeff_lo->c_amp, // Output: interpolated projection coefficients for amplitude
1241  romdata_coeff_lo->c_phi, // Output: interpolated projection coefficients for phase
1242  &amp_pre_lo // Output: interpolated amplitude prefactor
1243  );
1244 
1245  if(retcode!=0) {
1246  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1247  XLAL_ERROR(retcode);
1248  }
1249 
1250  /* Interpolate projection coefficients and evaluate them at (eta,chi1,chi2) */
1252  eta, // Input: eta-value for which projection coefficients should be evaluated
1253  chi1, // Input: chi1-value for which projection coefficients should be evaluated
1254  chi2, // Input: chi2-value for which projection coefficients should be evaluated
1255  submodel_hi->cvec_amp, // Input: data for spline coefficients for amplitude
1256  submodel_hi->cvec_phi, // Input: data for spline coefficients for phase
1257  submodel_hi->cvec_amp_pre, // Input: data for spline coefficients for amplitude prefactor
1258  submodel_hi->nk_amp, // number of SVD-modes == number of basis functions for amplitude
1259  submodel_hi->nk_phi, // number of SVD-modes == number of basis functions for phase
1260  nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1261  submodel_hi->ncx, // Number of points in eta + 2
1262  submodel_hi->ncy, // Number of points in chi1 + 2
1263  submodel_hi->ncz, // Number of points in chi2 + 2
1264  submodel_hi->etavec, // B-spline knots in eta
1265  submodel_hi->chi1vec, // B-spline knots in chi1
1266  submodel_hi->chi2vec, // B-spline knots in chi2
1267  romdata_coeff_hi->c_amp, // Output: interpolated projection coefficients for amplitude
1268  romdata_coeff_hi->c_phi, // Output: interpolated projection coefficients for phase
1269  &amp_pre_hi // Output: interpolated amplitude prefactor
1270  );
1271 
1272  if(retcode!=0) {
1273  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1274  XLAL_ERROR(retcode);
1275  }
1276 
1277 
1278  // Compute function values of amplitude an phase on sparse frequency points by evaluating matrix vector products
1279  // amp_pts = B_A^T . c_A
1280  // phi_pts = B_phi^T . c_phi
1281  gsl_vector* amp_f_lo = gsl_vector_alloc(submodel_lo->nk_amp);
1282  gsl_vector* phi_f_lo = gsl_vector_alloc(submodel_lo->nk_phi);
1283  gsl_blas_dgemv(CblasTrans, 1.0, submodel_lo->Bamp, romdata_coeff_lo->c_amp, 0.0, amp_f_lo);
1284  gsl_blas_dgemv(CblasTrans, 1.0, submodel_lo->Bphi, romdata_coeff_lo->c_phi, 0.0, phi_f_lo);
1285 
1286  gsl_vector* amp_f_hi = gsl_vector_alloc(submodel_hi->nk_amp);
1287  gsl_vector* phi_f_hi = gsl_vector_alloc(submodel_hi->nk_phi);
1288  gsl_blas_dgemv(CblasTrans, 1.0, submodel_hi->Bamp, romdata_coeff_hi->c_amp, 0.0, amp_f_hi);
1289  gsl_blas_dgemv(CblasTrans, 1.0, submodel_hi->Bphi, romdata_coeff_hi->c_phi, 0.0, phi_f_hi);
1290 
1291  const double Mfm = 0.01; // Gluing frequency: the low and high frequency ROMs overlap here; this is used both for amplitude and phase.
1292 
1293  // Glue amplitude
1294  gsl_interp_accel *acc_amp;
1295  gsl_spline *spline_amp;
1296  GlueAmplitude(submodel_lo, submodel_hi, amp_f_lo, amp_f_hi, amp_pre_lo, amp_pre_hi, Mfm,
1297  &acc_amp, &spline_amp
1298  );
1299 
1300  // Glue phasing in frequency to C^1 smoothness
1301  gsl_interp_accel *acc_phi;
1302  gsl_spline *spline_phi;
1303  GluePhasing(submodel_lo, submodel_hi, phi_f_lo, phi_f_hi, Mfm,
1304  &acc_phi, &spline_phi
1305  );
1306 
1307  size_t npts = 0;
1308  LIGOTimeGPS tC = {0, 0};
1309  UINT4 offset = 0; // Index shift between freqs and the frequency series
1310  REAL8Sequence *freqs = NULL;
1311  if (deltaF > 0) { // freqs contains uniform frequency grid with spacing deltaF; we start at frequency 0
1312  /* Set up output array with size closest power of 2 */
1313  npts = NextPow2(fHigh_geom / deltaF_geom) + 1;
1314  if (fHigh_geom < fHigh * Mtot_sec) /* Resize waveform if user wants f_max larger than cutoff frequency */
1315  npts = NextPow2(fHigh * Mtot_sec / deltaF_geom) + 1;
1316 
1317  XLALGPSAdd(&tC, -1. / deltaF); /* coalesce at t=0 */
1318  *hptilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &tC, 0.0, deltaF, &lalStrainUnit, npts);
1319  *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &tC, 0.0, deltaF, &lalStrainUnit, npts);
1320 
1321  // Recreate freqs using only the lower and upper bounds
1322  // Use fLow, fHigh and deltaF rather than geometric frequencies for numerical accuracy
1323  double fHigh_temp = fHigh_geom / Mtot_sec;
1324  UINT4 iStart = (UINT4) ceil(fLow / deltaF);
1325  UINT4 iStop = (UINT4) ceil(fHigh_temp / deltaF);
1326  freqs = XLALCreateREAL8Sequence(iStop - iStart);
1327  if (!freqs) {
1328  XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
1329  }
1330  for (UINT4 i=iStart; i<iStop; i++)
1331  freqs->data[i-iStart] = i*deltaF_geom;
1332 
1333  offset = iStart;
1334  } else { // freqs contains frequencies with non-uniform spacing; we start at lowest given frequency
1335  npts = freqs_in->length;
1336  *hptilde = XLALCreateCOMPLEX16FrequencySeries("hptilde: FD waveform", &tC, fLow, 0, &lalStrainUnit, npts);
1337  *hctilde = XLALCreateCOMPLEX16FrequencySeries("hctilde: FD waveform", &tC, fLow, 0, &lalStrainUnit, npts);
1338  offset = 0;
1339 
1340  freqs = XLALCreateREAL8Sequence(freqs_in->length);
1341  if (!freqs) {
1342  XLAL_ERROR(XLAL_EFUNC, "Frequency array allocation failed.");
1343  }
1344  for (UINT4 i=0; i<freqs_in->length; i++)
1345  freqs->data[i] = freqs_in->data[i] * Mtot_sec;
1346  }
1347 
1348  if (!(*hptilde) || !(*hctilde)) {
1349  XLALDestroyREAL8Sequence(freqs);
1350  gsl_spline_free(spline_amp);
1351  gsl_spline_free(spline_phi);
1352  gsl_interp_accel_free(acc_amp);
1353  gsl_interp_accel_free(acc_phi);
1354  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1355  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1357  }
1358  memset((*hptilde)->data->data, 0, npts * sizeof(COMPLEX16));
1359  memset((*hctilde)->data->data, 0, npts * sizeof(COMPLEX16));
1360 
1361  XLALUnitMultiply(&(*hptilde)->sampleUnits, &(*hptilde)->sampleUnits, &lalSecondUnit);
1362  XLALUnitMultiply(&(*hctilde)->sampleUnits, &(*hctilde)->sampleUnits, &lalSecondUnit);
1363 
1364  COMPLEX16 *pdata=(*hptilde)->data->data;
1365  COMPLEX16 *cdata=(*hctilde)->data->data;
1366 
1367  REAL8 cosi = cos(inclination);
1368  REAL8 pcoef = 0.5*(1.0 + cosi*cosi);
1369  REAL8 ccoef = cosi;
1370 
1371  REAL8 s = 0.5; // Scale polarization amplitude so that strain agrees with FFT of SEOBNRv2
1372  double Mtot = Mtot_sec / LAL_MTSUN_SI;
1373  double amp0 = Mtot * Mtot_sec * LAL_MRSUN_SI / (distance); // Correct overall amplitude to undo mass-dependent scaling used in ROM
1374 
1375  // Evaluate reference phase for setting phiRef correctly
1376  double phase_change = gsl_spline_eval(spline_phi, fRef_geom, acc_phi) - 2*phiRef;
1377 
1378  // Assemble waveform from aplitude and phase
1379  for (UINT4 i=0; i<freqs->length; i++) { // loop over frequency points in sequence
1380  double f = freqs->data[i];
1381  if (f > Mf_ROM_max) continue; // We're beyond the highest allowed frequency; since freqs may not be ordered, we'll just skip the current frequency and leave zero in the buffer
1382  int j = i + offset; // shift index for frequency series if needed
1383  double A = gsl_spline_eval(spline_amp, f, acc_amp);
1384  double phase = gsl_spline_eval(spline_phi, f, acc_phi) - phase_change;
1385  COMPLEX16 htilde = s*amp0*A * cexp(I*phase);
1386  pdata[j] = pcoef * htilde;
1387  cdata[j] = -I * ccoef * htilde;
1388  }
1389 
1390  /* Correct phasing so we coalesce at t=0 (with the definition of the epoch=-1/deltaF above) */
1391  /* JV: disable this so as not to clutter logs */
1392  // if (deltaF > 0)
1393  // XLAL_PRINT_WARNING("Warning: Depending on specified frequency sequence correction to time of coalescence may not be accurate.\n");
1394 
1395  // Get SEOBNRv2 ringdown frequency for 22 mode
1396  double Mf_final = SEOBNRROM_Ringdown_Mf_From_Mtot_Eta(Mtot_sec, eta, chi1, chi2, SEOBNRv2);
1397 
1398  UINT4 L = freqs->length;
1399  // prevent gsl interpolation errors
1400  if (Mf_final > freqs->data[L-1])
1401  Mf_final = freqs->data[L-1];
1402  if (Mf_final < freqs->data[0]) {
1403  XLALDestroyREAL8Sequence(freqs);
1404  gsl_spline_free(spline_amp);
1405  gsl_spline_free(spline_phi);
1406  gsl_interp_accel_free(acc_amp);
1407  gsl_interp_accel_free(acc_phi);
1408  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1409  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1410  XLAL_ERROR(XLAL_EDOM, "f_ringdown < f_min");
1411  }
1412 
1413  // Time correction is t(f_final) = 1/(2pi) dphi/df (f_final)
1414  // We compute the dimensionless time correction t/M since we use geometric units.
1415  REAL8 t_corr = gsl_spline_eval_deriv(spline_phi, Mf_final, acc_phi) / (2*LAL_PI);
1416 
1417  // Now correct phase
1418  for (UINT4 i=0; i<freqs->length; i++) { // loop over frequency points in sequence
1419  double f = freqs->data[i] - fRef_geom;
1420  int j = i + offset; // shift index for frequency series if needed
1421  pdata[j] *= cexp(-2*LAL_PI * I * f * t_corr);
1422  cdata[j] *= cexp(-2*LAL_PI * I * f * t_corr);
1423  }
1424 
1425  XLALDestroyREAL8Sequence(freqs);
1426 
1427  gsl_spline_free(spline_amp);
1428  gsl_spline_free(spline_phi);
1429  gsl_interp_accel_free(acc_amp);
1430  gsl_interp_accel_free(acc_phi);
1431  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1432  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1433 
1434  return(XLAL_SUCCESS);
1435 }
1436 
1437 /**
1438  * @addtogroup LALSimIMRSEOBNRROM_c
1439  *
1440  * \author Michael Puerrer
1441  *
1442  * \brief C code for SEOBNRv2 reduced order model
1443  * (double spin high resolution low mass version).
1444  * See M. Pürrer ,CQG 31 195010, 2014, arXiv:1402.4146 for the basic approach.
1445  * Further details in PRD 93, 064041, 2016, arXiv:1512.02248.
1446  *
1447  * This is a frequency domain model that approximates the time domain SEOBNRv2 model.
1448  *
1449  * The binary data HDF5 file (SEOBNRv2ROM_DS_HI_v1.0.hdf5)
1450  * is available at on LIGO clusters in /home/cbc/.
1451  * Make sure the files are in your LAL_DATA_PATH.
1452  *
1453  * @note Note that due to its construction the iFFT of the ROM has a small (~ 20 M) offset
1454  * in the peak time that scales with total mass as compared to the time-domain SEOBNRv2 model.
1455  *
1456  * @note Parameter ranges:
1457  * * 0.01 <= eta <= 0.25
1458  * * -1 <= chi_i <= 0.99
1459  * * Mtot >= 2 Msun < 500Msun
1460  *
1461  * Aligned component spins chi1, chi2.
1462  * Symmetric mass-ratio eta = m1*m2/(m1+m2)^2.
1463  * Total mass Mtot.
1464  *
1465  * @{
1466  */
1467 
1468 
1469 /**
1470  * Compute waveform in LAL format at specified frequencies for the SEOBNRv2_ROM_DoubleSpin_HI model.
1471  *
1472  * XLALSimIMRSEOBNRv2ROMDoubleSpinHI() returns the plus and cross polarizations as a complex
1473  * frequency series with equal spacing deltaF and contains zeros from zero frequency
1474  * to the starting frequency and zeros beyond the cutoff frequency in the ringdown.
1475  *
1476  * In contrast, XLALSimIMRSEOBNRv2ROMDoubleSpinHIFrequencySequence() returns a
1477  * complex frequency series with entries exactly at the frequencies specified in
1478  * the sequence freqs (which can be unequally spaced). No zeros are added.
1479  *
1480  * If XLALSimIMRSEOBNRv2ROMDoubleSpinHIFrequencySequence() is called with frequencies that
1481  * are beyond the maxium allowed geometric frequency for the ROM, zero strain is returned.
1482  * It is not assumed that the frequency sequence is ordered.
1483  *
1484  * This function is designed as an entry point for reduced order quadratures.
1485  */
1487  struct tagCOMPLEX16FrequencySeries **hptilde, /**< Output: Frequency-domain waveform h+ */
1488  struct tagCOMPLEX16FrequencySeries **hctilde, /**< Output: Frequency-domain waveform hx */
1489  const REAL8Sequence *freqs, /**< Frequency points at which to evaluate the waveform (Hz) */
1490  REAL8 phiRef, /**< Orbital phase at reference time */
1491  REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
1492  REAL8 distance, /**< Distance of source (m) */
1493  REAL8 inclination, /**< Inclination of source (rad) */
1494  REAL8 m1SI, /**< Mass of companion 1 (kg) */
1495  REAL8 m2SI, /**< Mass of companion 2 (kg) */
1496  REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1497  REAL8 chi2, /**< Dimensionless aligned component spin 2 */
1498  INT4 nk_max) /**< Truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
1499 {
1500  /* Internally we need m1 > m2, so change around if this is not the case */
1501  if (m1SI < m2SI) {
1502  // Swap m1 and m2
1503  double m1temp = m1SI;
1504  double chi1temp = chi1;
1505  m1SI = m2SI;
1506  chi1 = chi2;
1507  m2SI = m1temp;
1508  chi2 = chi1temp;
1509  }
1510 
1511  /* Get masses in terms of solar mass */
1512  double mass1 = m1SI / LAL_MSUN_SI;
1513  double mass2 = m2SI / LAL_MSUN_SI;
1514  double Mtot = mass1+mass2;
1515  double eta = mass1 * mass2 / (Mtot*Mtot); /* Symmetric mass-ratio */
1516  double Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
1517 
1518  if (!freqs) XLAL_ERROR(XLAL_EFAULT);
1519 
1520  // Load ROM data if not loaded already
1521 #ifdef LAL_PTHREAD_LOCK
1522  (void) pthread_once(&SEOBNRv2ROMDoubleSpin_is_initialized, SEOBNRv2ROMDoubleSpin_Init_LALDATA);
1523 #else
1525 #endif
1526 
1529  "Error setting up SEOBNRv2ROMDoubleSpinHI data - check your $LAL_DATA_PATH\n");
1530  }
1531 
1532  // Call the internal core function with deltaF = 0 to indicate that freqs is non-uniformly
1533  // spaced and we want the strain only at these frequencies
1534  int retcode = SEOBNRv2ROMDoubleSpinCore(hptilde,hctilde,
1535  phiRef, fRef, distance, inclination, Mtot_sec, eta, chi1, chi2, freqs, 0, nk_max);
1536 
1537  return(retcode);
1538 }
1539 
1540 /**
1541  * Compute waveform in LAL format for the SEOBNRv2_ROM_DoubleSpin_HI model.
1542  *
1543  * Returns the plus and cross polarizations as a complex frequency series with
1544  * equal spacing deltaF and contains zeros from zero frequency to the starting
1545  * frequency fLow and zeros beyond the cutoff frequency in the ringdown.
1546  */
1548  struct tagCOMPLEX16FrequencySeries **hptilde, /**< Output: Frequency-domain waveform h+ */
1549  struct tagCOMPLEX16FrequencySeries **hctilde, /**< Output: Frequency-domain waveform hx */
1550  REAL8 phiRef, /**< Phase at reference time */
1551  REAL8 deltaF, /**< Sampling frequency (Hz) */
1552  REAL8 fLow, /**< Starting GW frequency (Hz) */
1553  REAL8 fHigh, /**< End frequency; 0 defaults to Mf=0.14 */
1554  REAL8 fRef, /**< Reference frequency (Hz); 0 defaults to fLow */
1555  REAL8 distance, /**< Distance of source (m) */
1556  REAL8 inclination, /**< Inclination of source (rad) */
1557  REAL8 m1SI, /**< Mass of companion 1 (kg) */
1558  REAL8 m2SI, /**< Mass of companion 2 (kg) */
1559  REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1560  REAL8 chi2, /**< Dimensionless aligned component spin 2 */
1561  INT4 nk_max) /**< Truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1 */
1562 {
1563  /* Internally we need m1 > m2, so change around if this is not the case */
1564  if (m1SI < m2SI) {
1565  // Swap m1 and m2
1566  double m1temp = m1SI;
1567  double chi1temp = chi1;
1568  m1SI = m2SI;
1569  chi1 = chi2;
1570  m2SI = m1temp;
1571  chi2 = chi1temp;
1572  }
1573 
1574  /* Get masses in terms of solar mass */
1575  double mass1 = m1SI / LAL_MSUN_SI;
1576  double mass2 = m2SI / LAL_MSUN_SI;
1577  double Mtot = mass1+mass2;
1578  double eta = mass1 * mass2 / (Mtot*Mtot); /* Symmetric mass-ratio */
1579  double Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
1580 
1581  if(fRef==0.0)
1582  fRef=fLow;
1583 
1584  // Load ROM data if not loaded already
1585 #ifdef LAL_PTHREAD_LOCK
1586  (void) pthread_once(&SEOBNRv2ROMDoubleSpin_is_initialized, SEOBNRv2ROMDoubleSpin_Init_LALDATA);
1587 #else
1589 #endif
1590 
1591  // Use fLow, fHigh, deltaF to compute freqs sequence
1592  // Instead of building a full sequency we only transfer the boundaries and let
1593  // the internal core function do the rest (and properly take care of corner cases).
1595  freqs->data[0] = fLow;
1596  freqs->data[1] = fHigh;
1597 
1598  int retcode = SEOBNRv2ROMDoubleSpinCore(hptilde,hctilde,
1599  phiRef, fRef, distance, inclination, Mtot_sec, eta, chi1, chi2, freqs, deltaF, nk_max);
1600 
1601  XLALDestroyREAL8Sequence(freqs);
1602 
1603  return(retcode);
1604 }
1605 
1606 /** @} */
1607 
1608 // Auxiliary function to perform setup of phase spline for t(f) and f(t) functions
1610  gsl_spline **spline_phi, // phase spline
1611  gsl_interp_accel **acc_phi, // phase spline accelerator
1612  REAL8 *Mf_final, // ringdown frequency in Mf
1613  REAL8 *Mtot_sec, // total mass in seconds
1614  REAL8 m1SI, // Mass of companion 1 (kg)
1615  REAL8 m2SI, // Mass of companion 2 (kg)
1616  REAL8 chi1, // Aligned spin of companion 1
1617  REAL8 chi2 // Aligned spin of companion 2
1618 )
1619 {
1620  /* Get masses in terms of solar mass */
1621  double mass1 = m1SI / LAL_MSUN_SI;
1622  double mass2 = m2SI / LAL_MSUN_SI;
1623  double Mtot = mass1 + mass2;
1624  double eta = mass1 * mass2 / (Mtot*Mtot); /* Symmetric mass-ratio */
1625  *Mtot_sec = Mtot * LAL_MTSUN_SI; /* Total mass in seconds */
1626 
1627  // 'Nudge' parameter values to allowed boundary values if close by
1628  if (eta > 0.25) nudge(&eta, 0.25, 1e-6);
1629  if (eta < 0.01) nudge(&eta, 0.01, 1e-6);
1630  if (chi1 < -0.9999) nudge(&chi1, -0.9999, 1e-4);
1631  if (chi1 > 0.98999) nudge(&chi1, 0.98999, 1e-4);
1632  if (chi2 < -0.9999) nudge(&chi2, -0.9999, 1e-4);
1633  if (chi2 > 0.98999) nudge(&chi2, 0.98999, 1e-4);
1634 
1635  if ( chi1 < -1.0 || chi2 < -1.0 || chi1 > 0.99 || chi2 > 0.99) {
1636  XLALPrintError( "XLAL Error - %s: chi1 or chi2 smaller than -1.0 or larger than 0.99!\nSEOBNRv2ROMDoubleSpinHI is only available for spins in the range -1 <= a/M <= 0.99.\n", __func__);
1637  XLAL_ERROR( XLAL_EDOM );
1638  }
1639 
1640  if (eta < 0.01 || eta > 0.25) {
1641  XLALPrintError( "XLAL Error - %s: eta (%f) smaller than 0.01 or unphysical!\nSEOBNRv2ROMDoubleSpin is only available for spins in the range 0.01 <= eta <= 0.25.\n", __func__,eta);
1642  XLAL_ERROR( XLAL_EDOM );
1643  }
1644 
1645  // Load ROM data if not loaded already
1646 #ifdef LAL_PTHREAD_LOCK
1647  (void) pthread_once(&SEOBNRv2ROMDoubleSpin_is_initialized, SEOBNRv2ROMDoubleSpin_Init_LALDATA);
1648 #else
1650 #endif
1651 
1652  SEOBNRROMdataDS *romdata=&__lalsim_SEOBNRv2ROMDS_data;
1655  "Error setting up SEOBNRv2ROMDoubleSpinHI data - check your $LAL_DATA_PATH\n");
1656  }
1657 
1658  /* We always need to glue two submodels together for this ROM */
1659  SEOBNRROMdataDS_submodel *submodel_hi; // high frequency ROM
1660  SEOBNRROMdataDS_submodel *submodel_lo; // low frequency ROM
1661  submodel_lo = romdata->sub1;
1662  /* Select high frequency ROM submodel */
1663  if (chi1 < 0.41)
1664  submodel_hi = romdata->sub2;
1665  else
1666  submodel_hi = romdata->sub3;
1667 
1668  /* Internal storage for waveform coefficiencts */
1669  SEOBNRROMdataDS_coeff *romdata_coeff_lo=NULL;
1670  SEOBNRROMdataDS_coeff *romdata_coeff_hi=NULL;
1671  SEOBNRROMdataDS_coeff_Init(&romdata_coeff_lo, submodel_lo->nk_amp, submodel_lo->nk_phi);
1672  SEOBNRROMdataDS_coeff_Init(&romdata_coeff_hi, submodel_hi->nk_amp, submodel_hi->nk_phi);
1673  REAL8 amp_pre_lo, amp_pre_hi;
1674 
1675  /* Interpolate projection coefficients and evaluate them at (eta,chi1,chi2) */
1676  int nk_max = -1; // adjust truncation parameter if speed is an issue
1677  int retcode=TP_Spline_interpolation_3d(
1678  eta, // Input: eta-value for which projection coefficients should be evaluated
1679  chi1, // Input: chi1-value for which projection coefficients should be evaluated
1680  chi2, // Input: chi2-value for which projection coefficients should be evaluated
1681  submodel_lo->cvec_amp, // Input: data for spline coefficients for amplitude
1682  submodel_lo->cvec_phi, // Input: data for spline coefficients for phase
1683  submodel_lo->cvec_amp_pre, // Input: data for spline coefficients for amplitude prefactor
1684  submodel_lo->nk_amp, // number of SVD-modes == number of basis functions for amplitude
1685  submodel_lo->nk_phi, // number of SVD-modes == number of basis functions for phase
1686  nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1687  submodel_lo->ncx, // Number of points in eta + 2
1688  submodel_lo->ncy, // Number of points in chi1 + 2
1689  submodel_lo->ncz, // Number of points in chi2 + 2
1690  submodel_lo->etavec, // B-spline knots in eta
1691  submodel_lo->chi1vec, // B-spline knots in chi1
1692  submodel_lo->chi2vec, // B-spline knots in chi2
1693  romdata_coeff_lo->c_amp, // Output: interpolated projection coefficients for amplitude
1694  romdata_coeff_lo->c_phi, // Output: interpolated projection coefficients for phase
1695  &amp_pre_lo // Output: interpolated amplitude prefactor
1696  );
1697 
1698  if(retcode!=0) {
1699  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1700  XLAL_ERROR(retcode);
1701  }
1702 
1703  /* Interpolate projection coefficients and evaluate them at (eta,chi1,chi2) */
1705  eta, // Input: eta-value for which projection coefficients should be evaluated
1706  chi1, // Input: chi1-value for which projection coefficients should be evaluated
1707  chi2, // Input: chi2-value for which projection coefficients should be evaluated
1708  submodel_hi->cvec_amp, // Input: data for spline coefficients for amplitude
1709  submodel_hi->cvec_phi, // Input: data for spline coefficients for phase
1710  submodel_hi->cvec_amp_pre, // Input: data for spline coefficients for amplitude prefactor
1711  submodel_hi->nk_amp, // number of SVD-modes == number of basis functions for amplitude
1712  submodel_hi->nk_phi, // number of SVD-modes == number of basis functions for phase
1713  nk_max, // truncate interpolants at SVD mode nk_max; don't truncate if nk_max == -1
1714  submodel_hi->ncx, // Number of points in eta + 2
1715  submodel_hi->ncy, // Number of points in chi1 + 2
1716  submodel_hi->ncz, // Number of points in chi2 + 2
1717  submodel_hi->etavec, // B-spline knots in eta
1718  submodel_hi->chi1vec, // B-spline knots in chi1
1719  submodel_hi->chi2vec, // B-spline knots in chi2
1720  romdata_coeff_hi->c_amp, // Output: interpolated projection coefficients for amplitude
1721  romdata_coeff_hi->c_phi, // Output: interpolated projection coefficients for phase
1722  &amp_pre_hi // Output: interpolated amplitude prefactor
1723  );
1724 
1725  if(retcode!=0) {
1726  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1727  XLAL_ERROR(retcode);
1728  }
1729 
1730  // Compute function values of amplitude an phase on sparse frequency points by evaluating matrix vector products
1731  // phi_pts = B_phi^T . c_phi
1732  gsl_vector* phi_f_lo = gsl_vector_alloc(submodel_lo->nk_phi);
1733  gsl_blas_dgemv(CblasTrans, 1.0, submodel_lo->Bphi, romdata_coeff_lo->c_phi, 0.0, phi_f_lo);
1734 
1735  gsl_vector* phi_f_hi = gsl_vector_alloc(submodel_hi->nk_phi);
1736  gsl_blas_dgemv(CblasTrans, 1.0, submodel_hi->Bphi, romdata_coeff_hi->c_phi, 0.0, phi_f_hi);
1737 
1738  const double Mfm = 0.01; // Gluing frequency: the low and high frequency ROMs overlap here; this is used both for amplitude and phase.
1739 
1740  // Glue phasing in frequency to C^1 smoothness
1741  GluePhasing(submodel_lo, submodel_hi, phi_f_lo, phi_f_hi, Mfm,
1742  acc_phi, spline_phi
1743  );
1744 
1745  // Get SEOBNRv2 ringdown frequency for 22 mode
1746  *Mf_final = SEOBNRROM_Ringdown_Mf_From_Mtot_Eta(*Mtot_sec, eta, chi1, chi2, SEOBNRv2);
1747 
1748  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_lo);
1749  SEOBNRROMdataDS_coeff_Cleanup(romdata_coeff_hi);
1750 
1751  return(XLAL_SUCCESS);
1752 }
1753 
1754 /**
1755  * Compute the 'time' elapsed in the ROM waveform from a given starting frequency until the ringdown.
1756  *
1757  * The notion of elapsed 'time' (in seconds) is defined here as the difference of the
1758  * frequency derivative of the frequency domain phase between the ringdown frequency
1759  * and the starting frequency ('frequency' argument). This notion of time is similar to the
1760  * chirp time, but it includes both the inspiral and the merger ringdown part of SEOBNRv2.
1761  *
1762  * The allowed frequency range for the starting frequency in geometric frequency is [0.00053, 0.135].
1763  * The SEOBNRv2 ringdown frequency can be obtained by calling XLALSimInspiralGetFinalFreq().
1764  *
1765  * See XLALSimIMRSEOBNRv2ROMDoubleSpinHIFrequencyOfTime() for the inverse function.
1766  */
1768  REAL8 *t, /**< Output: time (s) elapsed from starting frequency to ringdown */
1769  REAL8 frequency, /**< Starting frequency (Hz) */
1770  REAL8 m1SI, /**< Mass of companion 1 (kg) */
1771  REAL8 m2SI, /**< Mass of companion 2 (kg) */
1772  REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1773  REAL8 chi2 /**< Dimensionless aligned component spin 2 */
1774 )
1775 {
1776  /* Internally we need m1 > m2, so change around if this is not the case */
1777  if (m1SI < m2SI) {
1778  // Swap m1 and m2
1779  double m1temp = m1SI;
1780  double chi1temp = chi1;
1781  m1SI = m2SI;
1782  chi1 = chi2;
1783  m2SI = m1temp;
1784  chi2 = chi1temp;
1785  }
1786 
1787  // Set up phase spline
1788  gsl_spline *spline_phi;
1789  gsl_interp_accel *acc_phi;
1790  double Mf_final, Mtot_sec;
1791  int ret = SEOBNRv2ROMDoubleSpinTimeFrequencySetup(&spline_phi, &acc_phi, &Mf_final, &Mtot_sec, m1SI, m2SI, chi1, chi2);
1792  if(ret != 0)
1793  XLAL_ERROR(ret);
1794 
1795  // ROM frequency bounds in Mf
1796  double Mf_ROM_min = 0.0000985;
1797  double Mf_ROM_max = 0.3;
1798 
1799  // Time correction is t(f_final) = 1/(2pi) dphi/df (f_final)
1800  double t_corr = gsl_spline_eval_deriv(spline_phi, Mf_final, acc_phi) / (2*LAL_PI); // t_corr / M
1801  XLAL_PRINT_INFO("t_corr[s] = %g\n", t_corr * Mtot_sec);
1802 
1803  double Mf = frequency * Mtot_sec;
1804  if (Mf < Mf_ROM_min || Mf > Mf_ROM_max || Mf > Mf_final) {
1805  gsl_spline_free(spline_phi);
1806  gsl_interp_accel_free(acc_phi);
1807  XLAL_ERROR(XLAL_EDOM, "Frequency %g is outside allowed frequency range.\n", frequency);
1808  }
1809 
1810  // Compute time relative to origin at merger
1811  double time_M = gsl_spline_eval_deriv(spline_phi, frequency * Mtot_sec, acc_phi) / (2*LAL_PI) - t_corr;
1812  *t = time_M * Mtot_sec;
1813 
1814  gsl_spline_free(spline_phi);
1815  gsl_interp_accel_free(acc_phi);
1816 
1817  return(XLAL_SUCCESS);
1818 }
1819 
1820 /**
1821  * Compute the starting frequency so that the given amount of 'time' elapses in the ROM waveform
1822  * from the starting frequency until the ringdown.
1823  *
1824  * The notion of elapsed 'time' (in seconds) is defined here as the difference of the
1825  * frequency derivative of the frequency domain phase between the ringdown frequency
1826  * and the starting frequency ('frequency' argument). This notion of time is similar to the
1827  * chirp time, but it includes both the inspiral and the merger ringdown part of SEOBNRv2.
1828  *
1829  * If the frequency that corresponds to the specified elapsed time is lower than the
1830  * geometric frequency Mf=0.00053 (ROM starting frequency) or above half of the SEOBNRv2
1831  * ringdown frequency an error is thrown.
1832  * The SEOBNRv2 ringdown frequency can be obtained by calling XLALSimInspiralGetFinalFreq().
1833  *
1834  * See XLALSimIMRSEOBNRv2ROMDoubleSpinHITimeOfFrequency() for the inverse function.
1835  */
1837  REAL8 *frequency, /**< Output: Frequency (Hz) */
1838  REAL8 t, /**< Time (s) at frequency */
1839  REAL8 m1SI, /**< Mass of companion 1 (kg) */
1840  REAL8 m2SI, /**< Mass of companion 2 (kg) */
1841  REAL8 chi1, /**< Dimensionless aligned component spin 1 */
1842  REAL8 chi2 /**< Dimensionless aligned component spin 2 */
1843 )
1844 {
1845  /* Internally we need m1 > m2, so change around if this is not the case */
1846  if (m1SI < m2SI) {
1847  // Swap m1 and m2
1848  double m1temp = m1SI;
1849  double chi1temp = chi1;
1850  m1SI = m2SI;
1851  chi1 = chi2;
1852  m2SI = m1temp;
1853  chi2 = chi1temp;
1854  }
1855 
1856  // Set up phase spline
1857  gsl_spline *spline_phi;
1858  gsl_interp_accel *acc_phi;
1859  double Mf_final, Mtot_sec;
1860  int ret = SEOBNRv2ROMDoubleSpinTimeFrequencySetup(&spline_phi, &acc_phi, &Mf_final, &Mtot_sec, m1SI, m2SI, chi1, chi2);
1861  if(ret != 0)
1862  XLAL_ERROR(ret);
1863 
1864  // ROM frequency bounds in Mf
1865  double Mf_ROM_min = 0.00053;
1866 
1867  // Time correction is t(f_final) = 1/(2pi) dphi/df (f_final)
1868  double t_corr = gsl_spline_eval_deriv(spline_phi, Mf_final, acc_phi) / (2*LAL_PI); // t_corr / M
1869  XLAL_PRINT_INFO("t_corr[s] = %g\n", t_corr * Mtot_sec);
1870 
1871  // Assume for now that we only care about f(t) *before* merger so that f(t) - f_ringdown >= 0.
1872  // Assume that we only need to cover the frequency range [f_min, f_ringdown/2].
1873  int N = 20;
1874  double log_f_pts[N];
1875  double log_t_pts[N];
1876  double log_f_min = log(Mf_ROM_min);
1877  double log_f_rng_2 = log(Mf_final/2.0);
1878  double dlog_f = (log_f_rng_2 - log_f_min) / (N-1);
1879 
1880  // Set up data in log-log space
1881  for (int i=0; i<N; i++) {
1882  log_f_pts[i] = log_f_rng_2 - i*dlog_f; // gsl likes the x-values to be monotonically increasing
1883  // Compute time relative to origin at merger
1884  double time_M = gsl_spline_eval_deriv(spline_phi, exp(log_f_pts[i]), acc_phi) / (2*LAL_PI) - t_corr;
1885  log_t_pts[i] = log(time_M * Mtot_sec);
1886  }
1887 
1888  // Check whether time is in bounds
1889  double t_rng_2 = exp(log_t_pts[0]); // time of f_ringdown/2
1890  double t_min = exp(log_t_pts[N-1]); // time of f_min
1891  if (t < t_rng_2 || t > t_min) {
1892  gsl_spline_free(spline_phi);
1893  gsl_interp_accel_free(acc_phi);
1894  XLAL_ERROR(XLAL_EDOM, "The frequency of time %g is outside allowed frequency range.\n", t);
1895  }
1896 
1897  // create new spline for data
1898  gsl_interp_accel *acc = gsl_interp_accel_alloc();
1899  gsl_spline *spline = gsl_spline_alloc(gsl_interp_cspline, N);
1900  gsl_spline_init(spline, log_t_pts, log_f_pts, N);
1901 
1902  *frequency = exp(gsl_spline_eval(spline, log(t), acc)) / Mtot_sec;
1903 
1904  gsl_spline_free(spline);
1905  gsl_interp_accel_free(acc);
1906  gsl_spline_free(spline_phi);
1907  gsl_interp_accel_free(acc_phi);
1908 
1909  return(XLAL_SUCCESS);
1910 }
1911 
1912 
1913 /** Setup SEOBNRv2ROMDoubleSpin model using data files installed in $LAL_DATA_PATH
1914  */
1916 {
1917  if (SEOBNRv2ROMDoubleSpin_IsSetup()) return;
1918 
1919  // For gsl binary data: If we find one ROM datafile in a directory listed in LAL_DATA_PATH,
1920  // then we expect the remaining datafiles to also be there.
1921  // For HDF5 there is only one file.
1922 #ifdef LAL_HDF5_ENABLED
1923 #define datafile ROMDataHDF5
1924 #else
1925  const char datafile[] = "SEOBNRv2ROM_DS_HI_sub1_Phase_ciall.dat";
1926 #endif
1927 
1928  char *path = XLAL_FILE_RESOLVE_PATH(datafile);
1929  if (path==NULL)
1930  XLAL_ERROR_VOID(XLAL_EIO, "Unable to resolve data file %s in $LAL_DATA_PATH\n", datafile);
1931  char *dir = dirname(path);
1932  int ret = SEOBNRv2ROMDoubleSpin_Init(dir);
1933  XLALFree(path);
1934 
1935  if(ret!=XLAL_SUCCESS)
1936  XLAL_ERROR_VOID(XLAL_FAILURE, "Unable to find SEOBNRv2ROMDoubleSpin_HI data files in $LAL_DATA_PATH\n");
1937 }
struct tagLALH5File LALH5File
static const REAL8 Mf_ROM_max
static const REAL8 Mf_ROM_min
static void nudge(REAL8 *x, REAL8 X, REAL8 epsilon)
Auxiliary functions for SEOBNRv1/v2 reduced order modeling codes in LALSimIMRSEOBNRv2ROMDoubleSpin....
static UNUSED gsl_vector * Fit_cubic(const gsl_vector *xi, const gsl_vector *yi)
static UNUSED REAL8 Interpolate_Coefficent_Tensor(gsl_vector *v, REAL8 eta, REAL8 chi1, REAL8 chi2, int ncy, int ncz, gsl_bspline_workspace *bwx, gsl_bspline_workspace *bwy, gsl_bspline_workspace *bwz)
static UNUSED int read_vector(const char dir[], const char fname[], gsl_vector *v)
static UNUSED double SEOBNRROM_Ringdown_Mf_From_Mtot_Eta(const double Mtot_sec, const double eta, const double chi1, const double chi2, Approximant apx)
static UNUSED int read_matrix(const char dir[], const char fname[], gsl_matrix *m)
static const double gA[]
static const double gPhi[]
static int load_data(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre)
static const double chi1vec_sub1[]
static const double etavec_sub1[]
static const int ncx_sub2
static int SEOBNRv2ROMDoubleSpinCore(COMPLEX16FrequencySeries **hptilde, COMPLEX16FrequencySeries **hctilde, double phiRef, double fRef, double distance, double inclination, double Mtot_sec, double eta, double chi1, double chi2, const REAL8Sequence *freqs, double deltaF, int nk_max)
Core function for computing the ROM waveform.
static int SEOBNRROMdataDS_Init_submodel(SEOBNRROMdataDS_submodel **submodel, const int nk_amp, const int nk_phi, const double *gA, const double *gPhi, const double *etavec, const double *chi1vec, const double *chi2vec, const int ncx, const int ncy, const int ncz, const char dir[], load_dataPtr load_data)
static bool SEOBNRv2ROMDoubleSpin_IsSetup(void)
Helper function to check if the SEOBNRv2ROMDoubleSpin model has been initialised.
static void GluePhasing(SEOBNRROMdataDS_submodel *submodel_lo, SEOBNRROMdataDS_submodel *submodel_hi, gsl_vector *phi_f_lo, gsl_vector *phi_f_hi, const double Mfm, gsl_interp_accel **acc_phi_out, gsl_spline **spline_phi_out)
static const double chi1vec_sub2[]
int(* load_dataPtr)(const char *, gsl_vector *, gsl_vector *, gsl_matrix *, gsl_matrix *, gsl_vector *)
static const double gPhi_sub1[]
static const int ncy_sub1
static const int ncx_sub1
static void SEOBNRv2ROMDoubleSpin_Init_LALDATA(void)
Setup SEOBNRv2ROMDoubleSpin model using data files installed in $LAL_DATA_PATH.
static const double g_sub2[]
static const int ncz_sub1
static int SEOBNRv2ROMDoubleSpin_Init(const char dir[])
Setup SEOBNRv2ROMDoubleSpin model using data files installed in dir.
static const double etavec_sub3[]
static const int ncz_sub3
static void SplineData_Destroy(SplineData *splinedata)
int XLALSimIMRSEOBNRv2ROMDoubleSpinHITimeOfFrequency(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.
static const int ncx_sub3
static const double chi2vec_sub1[]
static const int ncy_sub2
static UNUSED REAL8 Interpolate_Coefficent_Matrix(gsl_vector *v, REAL8 eta, REAL8 chi, int ncx, int ncy, gsl_bspline_workspace *bwx, gsl_bspline_workspace *bwy)
static const int ncz_sub2
static SEOBNRROMdataDS __lalsim_SEOBNRv2ROMDS_data
static void SplineData_Init(SplineData **splinedata, int ncx, int ncy, int ncz, const double *etavec, const double *chi1vec, const double *chi2vec)
static int load_data_sub1(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre)
int XLALSimIMRSEOBNRv2ROMDoubleSpinHIFrequencyOfTime(REAL8 *frequency, REAL8 t, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2)
Compute the starting frequency so that the given amount of 'time' elapses in the ROM waveform from th...
static int load_data_sub3(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre)
static const double gA_sub1[]
static UNUSED int read_vector_test(const char dir[], const char fname[], gsl_vector *v)
static int load_data_sub2(const char dir[], gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_matrix *Bamp, gsl_matrix *Bphi, gsl_vector *cvec_amp_pre)
static const double chi2vec_sub3[]
static const double etavec_sub2[]
static const double chi2vec_sub2[]
static void SEOBNRROMdataDS_coeff_Init(SEOBNRROMdataDS_coeff **romdatacoeff, int nk_amp, int nk_phi)
static void SEOBNRROMdataDS_Cleanup_submodel(SEOBNRROMdataDS_submodel *submodel)
static void SEOBNRROMdataDS_coeff_Cleanup(SEOBNRROMdataDS_coeff *romdatacoeff)
static int SEOBNRv2ROMDoubleSpinTimeFrequencySetup(gsl_spline **spline_phi, gsl_interp_accel **acc_phi, REAL8 *Mf_final, REAL8 *Mtot_sec, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2)
static int TP_Spline_interpolation_3d(REAL8 eta, REAL8 chi1, REAL8 chi2, gsl_vector *cvec_amp, gsl_vector *cvec_phi, gsl_vector *cvec_amp_pre, int nk_amp, int nk_phi, int nk_max, int ncx, int ncy, int ncz, const double *etavec, const double *chi1vec, const double *chi2vec, gsl_vector *c_amp, gsl_vector *c_phi, REAL8 *amp_pre)
static void SEOBNRROMdataDS_Cleanup(SEOBNRROMdataDS *romdata)
static void GlueAmplitude(SEOBNRROMdataDS_submodel *submodel_lo, SEOBNRROMdataDS_submodel *submodel_hi, gsl_vector *amp_f_lo, gsl_vector *amp_f_hi, double amp_pre_lo, double amp_pre_hi, const double Mfm, gsl_interp_accel **acc_amp, gsl_spline **spline_amp)
static size_t NextPow2(const size_t n)
static const double chi1vec_sub3[]
static const int ncy_sub3
static int SEOBNRROMdataDS_Init(SEOBNRROMdataDS *romdata, const char dir[])
int s
Definition: bh_qnmode.c:137
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
sigmaKerr data[0]
#define XLAL_FILE_RESOLVE_PATH(fname)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALH5FileClose(LALH5File UNUSED *file)
LALH5File * XLALH5GroupOpen(LALH5File UNUSED *file, const char UNUSED *name)
LALH5File * XLALH5FileOpen(const char UNUSED *path, const char UNUSED *mode)
#define LAL_MSUN_SI
#define LAL_PI
#define LAL_MTSUN_SI
#define LAL_MRSUN_SI
double complex COMPLEX16
double REAL8
uint32_t UINT4
int32_t INT4
void * XLALCalloc(size_t m, size_t n)
void * XLALMalloc(size_t n)
void XLALFree(void *p)
int XLALSimIMRSEOBNRv2ROMDoubleSpinHI(struct tagCOMPLEX16FrequencySeries **hptilde, struct tagCOMPLEX16FrequencySeries **hctilde, REAL8 phiRef, REAL8 deltaF, REAL8 fLow, REAL8 fHigh, REAL8 fRef, REAL8 distance, REAL8 inclination, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, INT4 nk_max)
Compute waveform in LAL format for the SEOBNRv2_ROM_DoubleSpin_HI model.
int XLALSimIMRSEOBNRv2ROMDoubleSpinHIFrequencySequence(struct tagCOMPLEX16FrequencySeries **hptilde, struct tagCOMPLEX16FrequencySeries **hctilde, const REAL8Sequence *freqs, REAL8 phiRef, REAL8 fRef, REAL8 distance, REAL8 inclination, REAL8 m1SI, REAL8 m2SI, REAL8 chi1, REAL8 chi2, INT4 nk_max)
Compute waveform in LAL format at specified frequencies for the SEOBNRv2_ROM_DoubleSpin_HI model.
@ SEOBNRv2
Spin-aligned EOBNR model v2.
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
const LALUnit lalStrainUnit
const LALUnit lalSecondUnit
LALUnit * XLALUnitMultiply(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
#define XLAL_ERROR_VOID(...)
#define XLAL_PRINT_INFO(...)
int int int XLALPrintInfo(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_ERROR(...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
int int XLALPrintWarning(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EDOM
XLAL_EIO
XLAL_EFAILED
XLAL_FAILURE
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
path
int N
int pthread_once(pthread_once_t *once_control, void(*init_routine)(void))
REAL8 * data
gsl_bspline_workspace * bwx
gsl_bspline_workspace * bwz
gsl_bspline_workspace * bwy
SEOBNRROMdataDS_submodel * sub2
SEOBNRROMdataDS_submodel * sub1
SEOBNRROMdataDS_submodel * sub3