LALSimulation  5.4.0.1-fe68b98
LALSimIMRNRWaveforms.c
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2015 Ian Harry, Patricia Schmidt, Riccardo Sturani
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with with program; see the file COPYING. If not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <math.h>
23 #include <complex.h>
24 #include <time.h>
25 #include <stdbool.h>
26 #include <alloca.h>
27 #include <libgen.h>
28 
29 #ifdef __GNUC__
30 #define UNUSED __attribute__ ((unused))
31 #else
32 #define UNUSED
33 #endif
34 
35 #include <gsl/gsl_errno.h>
36 #include <gsl/gsl_bspline.h>
37 #include <gsl/gsl_blas.h>
38 #include <gsl/gsl_min.h>
39 #include <gsl/gsl_spline.h>
40 #include <lal/Units.h>
41 #include <lal/SeqFactories.h>
42 #include <lal/LALConstants.h>
43 #include <lal/XLALError.h>
44 #include <lal/FrequencySeries.h>
45 #include <lal/Date.h>
46 #include <lal/StringInput.h>
47 #include <lal/Sequence.h>
48 #include <lal/LALStdio.h>
49 #include <lal/FileIO.h>
50 
51 #include <lal/LALSimInspiral.h>
52 #include <lal/LALSimIMR.h>
53 #include <lal/LALConfig.h>
54 #include <lal/SphericalHarmonics.h>
55 #include <lal/LALSimInspiralPrecess.h>
56 
57 #include <lal/H5FileIO.h>
58 
60 
62  UNUSED LALH5File* file,
63  UNUSED REAL8 fRef
64 )
65 {
66  #ifndef LAL_HDF5_ENABLED
67  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
68  #else
69  gsl_vector *omega_w_vec = NULL;
70  LALH5File *omega_group = NULL;
71  REAL8 min_fref, lowest_arr_fref;
72  if (fRef > 0)
73  {
74  /* This is the declared f_lower (using 1MSUN total mass) */
75  XLALH5FileQueryScalarAttributeValue(&min_fref, file, "f_lower_at_1MSUN");
76  /* This is the lowest f_lower in the Omega-vs-time array */
77  omega_group = XLALH5GroupOpen(file, "Omega-vs-time");
78  ReadHDF5RealVectorDataset(omega_group, "Y", &omega_w_vec);
79  XLALH5FileClose(omega_group);
80  lowest_arr_fref = gsl_vector_get(omega_w_vec, 0);
81  lowest_arr_fref = lowest_arr_fref / (LAL_MTSUN_SI * LAL_PI);
82  gsl_vector_free(omega_w_vec);
83  /* First, is fRef smaller than min_fref?
84  * Allow some float-precision slop */
85  if (fRef < 0.9999 * min_fref)
86  {
87  XLAL_ERROR(XLAL_EINVAL, "The supplied reference frequency is lower than the start frequency of the waveform. Start frequency (scaled by total mass) is %e and supplied fRef (scaled by total mass) is %e.\n", min_fref, fRef);
88  }
89  /* The Omega array may not quite extend to the minimum fRef, if that
90  * happens, fall back on the attribute values. */
91  if (fRef < lowest_arr_fref)
92  {
93  fRef = -1;
94  }
95  }
96  return fRef;
97  #endif
98 }
99 
101  UNUSED LALH5File* file,
102  UNUSED REAL8 fRef
103 )
104 {
105  #ifndef LAL_HDF5_ENABLED
106  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
107  #else
108  /* NOTE: This is an internal function, it is expected that fRef is scaled
109  * to correspond to the fRef with a total mass of 1 solar mass */
110  LALH5File *curr_group = NULL;
111  gsl_vector *omega_t_vec = NULL;
112  gsl_vector *omega_w_vec = NULL;
113  gsl_interp_accel *acc;
114  gsl_spline *spline;
115  REAL8 ref_time;
116  curr_group = XLALH5GroupOpen(file, "Omega-vs-time");
117  ReadHDF5RealVectorDataset(curr_group, "X", &omega_t_vec);
118  ReadHDF5RealVectorDataset(curr_group, "Y", &omega_w_vec);
119  XLALH5FileClose(curr_group);
120  acc = gsl_interp_accel_alloc();
121  spline = gsl_spline_alloc(gsl_interp_cspline, omega_w_vec->size);
122  INT4 status = gsl_spline_init(spline, omega_w_vec->data, omega_t_vec->data,
123  omega_t_vec->size);
124  XLAL_CHECK(status == GSL_SUCCESS, XLAL_FAILURE, "Failed gsl_spline_init evaluation. Probably omega_w is not monotonically increasing.\n");
125  ref_time = gsl_spline_eval(spline, fRef * (LAL_MTSUN_SI * LAL_PI), acc);
126  if ( isnan(ref_time) ){
127  XLAL_ERROR(XLAL_FAILURE, "Interpolation error: gsl_spline_eval returning nan\n");
128  }
129  gsl_vector_free(omega_t_vec);
130  gsl_vector_free(omega_w_vec);
131  gsl_spline_free(spline);
132  gsl_interp_accel_free(acc);
133  return ref_time;
134  #endif
135 }
136 
138  UNUSED LALH5File* file, /**< Pointer to HDF5 file */
139  UNUSED const char *groupName, /**< Name of group in HDF file */
140  UNUSED REAL8 ref_point /**< Point at which to evaluate */
141  )
142 {
143  #ifndef LAL_HDF5_ENABLED
144  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
145  #else
146  LALH5File *curr_group = NULL;
147  gsl_vector *curr_t_vec = NULL;
148  gsl_vector *curr_y_vec = NULL;
149  gsl_interp_accel *acc;
150  gsl_spline *spline;
151  REAL8 ret_val;
152  curr_group = XLALH5GroupOpen(file, groupName);
153  ReadHDF5RealVectorDataset(curr_group, "X", &curr_t_vec);
154  ReadHDF5RealVectorDataset(curr_group, "Y", &curr_y_vec);
155  XLALH5FileClose(curr_group);
156  acc = gsl_interp_accel_alloc();
157  spline = gsl_spline_alloc(gsl_interp_cspline, curr_t_vec->size);
158  INT4 status = gsl_spline_init(spline, curr_t_vec->data, curr_y_vec->data,
159  curr_t_vec->size);
160  XLAL_CHECK(status == GSL_SUCCESS, XLAL_FAILURE, "Failed gsl_spline_init evaluation. Probably omega_w is not monotonically increasing.\n");
161  ret_val = gsl_spline_eval(spline, ref_point, acc);
162  if ( isnan(ret_val) ){
163  XLAL_ERROR(XLAL_FAILURE, "Interpolation error: gsl_spline_eval returning nan. Group name %s\n", groupName);
164  }
165  gsl_vector_free(curr_t_vec);
166  gsl_vector_free(curr_y_vec);
167  gsl_spline_free (spline);
168  gsl_interp_accel_free (acc);
169  return ret_val;
170  #endif
171 }
172 
173 /* Everything needs to be declared as unused in case HDF is not enabled. */
175  UNUSED REAL8 *S1x, /**< [out] Dimensionless spin1x in LAL frame */
176  UNUSED REAL8 *S1y, /**< [out] Dimensionless spin1y in LAL frame */
177  UNUSED REAL8 *S1z, /**< [out] Dimensionless spin1z in LAL frame */
178  UNUSED REAL8 *S2x, /**< [out] Dimensionless spin2x in LAL frame */
179  UNUSED REAL8 *S2y, /**< [out] Dimensionless spin2y in LAL frame */
180  UNUSED REAL8 *S2z, /**< [out] Dimensionless spin2z in LAL frame */
181  UNUSED REAL8 fRef, /**< Reference frequency */
182  UNUSED REAL8 mTot, /**< Total mass */
183  UNUSED LALH5File* file /**< Pointer to HDF5 file */
184 )
185 {
186  #ifndef LAL_HDF5_ENABLED
187  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
188  #else
189  REAL8 nrSpin1x, nrSpin1y, nrSpin1z, nrSpin2x, nrSpin2y, nrSpin2z;
190  REAL8 ln_hat_x, ln_hat_y, ln_hat_z, n_hat_x, n_hat_y, n_hat_z, n_norm;
191  REAL8 pos1x, pos1y, pos1z, pos2x, pos2y, pos2z;
192  REAL8 ref_time;
193  INT8 nr_file_format;
194  /* We'll work by always using mTot = 1 */
195  fRef = fRef * mTot;
196 
197  XLALH5FileQueryScalarAttributeValue(&nr_file_format, file, "Format");
198  if ((nr_file_format < 2) && (fRef > 0))
199  {
200  /* Supplying a fRef > 0 indicates that the user actually wants to use
201  * fRef. If this is not possible, because the file format is < 2, then
202  * the code will fail. */
203  XLAL_ERROR(XLAL_EINVAL, "This NR file is format 1. Only formats 2 and above support the use of reference frequency. For formats < 2 give the reference frequency as 0 or -1 to indicate that the start of the waveform should be used as the reference.");
204  }
205 
206  /* Check if fRef is possible given inputs */
208 
209  if (fRef <= 0)
210  {
211  XLALH5FileQueryScalarAttributeValue(&nrSpin1x, file, "spin1x");
212  XLALH5FileQueryScalarAttributeValue(&nrSpin1y, file, "spin1y");
213  XLALH5FileQueryScalarAttributeValue(&nrSpin1z, file, "spin1z");
214  XLALH5FileQueryScalarAttributeValue(&nrSpin2x, file, "spin2x");
215  XLALH5FileQueryScalarAttributeValue(&nrSpin2y, file, "spin2y");
216  XLALH5FileQueryScalarAttributeValue(&nrSpin2z, file, "spin2z");
217 
218  XLALH5FileQueryScalarAttributeValue(&ln_hat_x , file, "LNhatx");
219  XLALH5FileQueryScalarAttributeValue(&ln_hat_y , file, "LNhaty");
220  XLALH5FileQueryScalarAttributeValue(&ln_hat_z , file, "LNhatz");
221 
222  XLALH5FileQueryScalarAttributeValue(&n_hat_x , file, "nhatx");
223  XLALH5FileQueryScalarAttributeValue(&n_hat_y , file, "nhaty");
224  XLALH5FileQueryScalarAttributeValue(&n_hat_z , file, "nhatz");
225  }
226  else
227  {
228  /* This will be real slow if done many times!
229  * One speed issue is we create the spline for *all* points, but only
230  * evaluate at a single point. */
231 
232  /* First interpolate between time and freq to get a reference time */
234  REAL8 fminNR;
235  XLALH5FileQueryScalarAttributeValue(&fminNR, file, "f_lower_at_1MSUN");
236  XLAL_CHECK( ref_time!=XLAL_FAILURE, XLAL_FAILURE, "Error computing reference time. Try setting fRef equal to the f_low given by the NR simulation (%.16f Hz) or to a value <=0 to deactivate fRef for a non-precessing simulation.\n", fminNR/mTot);
237 
238  /* Now use ref_time to get spins, LN and nhat */
240  (file, "spin1x-vs-time", ref_time);
242  (file, "spin1y-vs-time", ref_time);
244  (file, "spin1z-vs-time", ref_time);
246  (file, "spin2x-vs-time", ref_time);
248  (file, "spin2y-vs-time", ref_time);
250  (file, "spin2z-vs-time", ref_time);
252  (file, "LNhatx-vs-time", ref_time);
254  (file, "LNhaty-vs-time", ref_time);
256  (file, "LNhatz-vs-time", ref_time);
257  /* Would have been easier if we could store n_hat directly!! */
259  (file, "position1x-vs-time", ref_time);
261  (file, "position1y-vs-time", ref_time);
263  (file, "position1z-vs-time", ref_time);
265  (file, "position2x-vs-time", ref_time);
267  (file, "position2y-vs-time", ref_time);
269  (file, "position2z-vs-time", ref_time);
270  n_hat_x = pos1x - pos2x;
271  n_hat_y = pos1y - pos2y;
272  n_hat_z = pos1z - pos2z;
273  n_norm = sqrt(n_hat_x*n_hat_x + n_hat_y*n_hat_y + n_hat_z*n_hat_z);
274  n_hat_x = n_hat_x / n_norm;
275  n_hat_y = n_hat_y / n_norm;
276  n_hat_z = n_hat_z / n_norm;
277  }
278 
279  *S1x = nrSpin1x * n_hat_x + nrSpin1y * n_hat_y + nrSpin1z * n_hat_z;
280  *S1y = nrSpin1x * (-ln_hat_z * n_hat_y + ln_hat_y * n_hat_z)
281  + nrSpin1y * (ln_hat_z * n_hat_x - ln_hat_x * n_hat_z)
282  + nrSpin1z * (-ln_hat_y * n_hat_x + ln_hat_x * n_hat_y) ;
283  *S1z = nrSpin1x * ln_hat_x + nrSpin1y * ln_hat_y + nrSpin1z * ln_hat_z;
284 
285  *S2x = nrSpin2x * n_hat_x + nrSpin2y * n_hat_y + nrSpin2z * n_hat_z;
286  *S2y = nrSpin2x * (-ln_hat_z * n_hat_y + ln_hat_y * n_hat_z)
287  + nrSpin2y * (ln_hat_z * n_hat_x - ln_hat_x * n_hat_z)
288  + nrSpin2z * (-ln_hat_y * n_hat_x + ln_hat_x * n_hat_y);
289  *S2z = nrSpin2x * ln_hat_x + nrSpin2y * ln_hat_y + nrSpin2z * ln_hat_z;
290 
291  return XLAL_SUCCESS;
292  #endif
293 }
294 
296  UNUSED REAL8Vector** output, /**< Returned vector uncompressed */
297  UNUSED LALH5File* pointer, /**< Pointer to HDF5 file */
298  UNUSED REAL8 totalMass, /**< Total mass of system for scaling */
299  UNUSED REAL8 startTime, /**< Start time of veturn vector */
300  UNUSED size_t length, /**< Length of returned vector */
301  UNUSED REAL8 deltaT, /**< Sample rate of returned vector */
302  UNUSED const char *keyName /**< Name of vector to uncompress */
303  )
304 {
305  #ifndef LAL_HDF5_ENABLED
306  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
307  #else
308  UINT4 idx;
309  size_t comp_data_length;
310  REAL8 massTime;
311  gsl_interp_accel *acc;
312  gsl_spline *spline;
313  gsl_vector *knotsVector, *dataVector;
314  LALH5File *group = XLALH5GroupOpen(pointer, keyName);
315  knotsVector=dataVector=NULL;
316 
317  ReadHDF5RealVectorDataset(group, "X", &knotsVector);
318  ReadHDF5RealVectorDataset(group, "Y", &dataVector);
319  XLALH5FileClose(group);
320 
321  *output = XLALCreateREAL8Vector(length);
322 
323  comp_data_length = dataVector->size;
324  /* SPLINE STUFF */
325  acc = gsl_interp_accel_alloc();
326  spline = gsl_spline_alloc(gsl_interp_cspline, comp_data_length);
327  INT4 status = gsl_spline_init(spline, knotsVector->data, dataVector->data,
328  comp_data_length);
329  XLAL_CHECK(status == GSL_SUCCESS, XLAL_FAILURE, "Failed gsl_spline_init evaluation. Probably omega_w is not monotonically increasing.\n");
330 
331  for (idx = 0; idx < length; idx++)
332  {
333  massTime = (startTime + idx*deltaT) / (totalMass * LAL_MTSUN_SI);
334  /* This if statement is used to catch the case where massTime at idx=0
335  * ends up at double precision smaller than the first point in the
336  * interpolation. In this case set it back to exactly the first point.
337  * Sanity checking that we are not trying to use data below the
338  * interpolation range is done elsewhere.
339  */
340  if ((idx == 0) && (massTime < knotsVector->data[0]))
341  {
342  massTime = knotsVector->data[0];
343  }
344  (*output)->data[idx] = gsl_spline_eval(spline, massTime, acc);
345  }
346 
347  gsl_vector_free(knotsVector);
348  gsl_vector_free(dataVector);
349  gsl_spline_free (spline);
350  gsl_interp_accel_free (acc);
351 
352  return XLAL_SUCCESS;
353  #endif
354 }
355 
357  UNUSED REAL8 *theta, /**< Returned inclination angle of source */
358  UNUSED REAL8 *psi, /**< Returned azimuth angle of source */
359  UNUSED REAL8 *calpha, /**< Returned cosine of the polarisation angle */
360  UNUSED REAL8 *salpha, /**< Returned sine of the polarisation angle */
361  UNUSED LALH5File* filepointer, /**< Pointer to NR HDF5 file */
362  UNUSED const REAL8 inclination, /**< Inclination of source */
363  UNUSED const REAL8 phi_ref, /**< Orbital reference phase*/
364  UNUSED REAL8 fRef /**< Reference frequency */
365  )
366 {
367  #ifndef LAL_HDF5_ENABLED
368  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
369  #else
370 
371  /* Compute the angles necessary to rotate from the intrinsic NR source frame
372  * into the LAL frame. See DCC-T1600045 for details.
373  */
374 
375  /* Attribute declarations go in here */
376  /* Declarations */
377  REAL8 orb_phase, ln_hat_x, ln_hat_y, ln_hat_z, ln_hat_norm, ref_time;
378  REAL8 n_hat_x, n_hat_y, n_hat_z, n_hat_norm;
379  REAL8 pos1x, pos1y, pos1z, pos2x, pos2y, pos2z;
380  REAL8 r_x, r_y, r_z, r_norm;
381  REAL8 corb_phase, sorb_phase, sinclination, cinclination;
382  REAL8 ln_cross_n_x, ln_cross_n_y, ln_cross_n_z, ln_cross_n_norm;
383  REAL8 z_wave_x, z_wave_y, z_wave_z, z_wave_norm;
384  REAL8 stheta, ctheta, spsi, cpsi, theta_hat_x, theta_hat_y, theta_hat_z;
385  REAL8 psi_hat_x, psi_hat_y, psi_hat_z;
386  REAL8 n_dot_theta, ln_cross_n_dot_theta, n_dot_psi, ln_cross_n_dot_psi;
387  REAL8 y_val;
388 
389  fRef = XLALSimInspiralNRWaveformCheckFRef(filepointer, fRef);
390 
391  /* Following section IV of DCC-T1600045
392  * Step 1: Define Phi = phiref
393  */
394  orb_phase = phi_ref;
395 
396  /* Step 2: Compute Zref
397  * 2.1: Compute LN_hat from file. LN_hat = direction of orbital ang. mom.
398  * 2.2: Compute n_hat from file.
399  * n_hat = direction from object 2 to object 1
400  */
401 
402  if (fRef > 0)
403  {
404  ref_time = XLALSimInspiralNRWaveformGetRefTimeFromRefFreq(filepointer,
405  fRef);
406  XLAL_CHECK( ref_time!=XLAL_FAILURE, XLAL_FAILURE, "Error computing reference time. Try setting fRef equal to the f_low given by the NR simulation or to a value <=0 to deactivate fRef for a non-precessing simulation.\n");
408  (filepointer, "LNhatx-vs-time", ref_time);
410  (filepointer, "LNhaty-vs-time", ref_time);
412  (filepointer, "LNhatz-vs-time", ref_time);
413  /* Would have been easier if we could store n_hat directly!! */
415  (filepointer, "position1x-vs-time", ref_time);
417  (filepointer, "position1y-vs-time", ref_time);
419  (filepointer, "position1z-vs-time", ref_time);
421  (filepointer, "position2x-vs-time", ref_time);
423  (filepointer, "position2y-vs-time", ref_time);
425  (filepointer, "position2z-vs-time", ref_time);
426  r_x = pos1x - pos2x;
427  r_y = pos1y - pos2y;
428  r_z = pos1z - pos2z;
429  r_norm=sqrt(r_x*r_x+ r_y*r_y + r_z*r_z);
430  n_hat_x=r_x/r_norm;
431  n_hat_y=r_y/r_norm;
432  n_hat_z=r_z/r_norm;
433  }
434  else
435  {
436  XLALH5FileQueryScalarAttributeValue(&ln_hat_x , filepointer, "LNhatx");
437  XLALH5FileQueryScalarAttributeValue(&ln_hat_y , filepointer, "LNhaty");
438  XLALH5FileQueryScalarAttributeValue(&ln_hat_z , filepointer, "LNhatz");
439  XLALH5FileQueryScalarAttributeValue(&n_hat_x , filepointer, "nhatx");
440  XLALH5FileQueryScalarAttributeValue(&n_hat_y , filepointer, "nhaty");
441  XLALH5FileQueryScalarAttributeValue(&n_hat_z , filepointer, "nhatz");
442  }
443 
444  ln_hat_norm = sqrt(ln_hat_x * ln_hat_x + ln_hat_y * ln_hat_y + ln_hat_z * ln_hat_z);
445 
446  if (fabs(ln_hat_norm - 1) > 0.001)
447  {
448  XLAL_PRINT_ERROR("ln_hat_mag = %.16f\n", ln_hat_norm);
449  XLAL_ERROR(XLAL_EDOM, "The size of the LN hat vector in the supplied HDF file is not equal to unity");
450  }
451 
452  ln_hat_x = ln_hat_x / ln_hat_norm;
453  ln_hat_y = ln_hat_y / ln_hat_norm;
454  ln_hat_z = ln_hat_z / ln_hat_norm;
455 
456  n_hat_norm = sqrt(n_hat_x * n_hat_x + n_hat_y * n_hat_y + n_hat_z * n_hat_z);
457 
458  if (fabs(n_hat_norm - 1) > 0.001)
459  {
460  XLAL_PRINT_ERROR("n_hat_mag = %.16f\n", n_hat_norm);
461  XLAL_ERROR(XLAL_EDOM, "The size of the n hat vector in the supplied HDF file is not equal to unity");
462  }
463 
464  n_hat_x = n_hat_x / n_hat_norm;
465  n_hat_y = n_hat_y / n_hat_norm;
466  n_hat_z = n_hat_z / n_hat_norm;
467 
468  /* 2.3: Compute Z in the lal wave frame */
469 
470  corb_phase = cos(orb_phase);
471  sorb_phase = sin(orb_phase);
472  sinclination = sin(inclination);
473  cinclination = cos(inclination);
474 
475  ln_cross_n_x = ln_hat_y * n_hat_z - ln_hat_z * n_hat_y;
476  ln_cross_n_y = ln_hat_z * n_hat_x - ln_hat_x * n_hat_z;
477  ln_cross_n_z = ln_hat_x * n_hat_y - ln_hat_y * n_hat_x;
478 
479  ln_cross_n_norm = sqrt(ln_cross_n_x*ln_cross_n_x + ln_cross_n_y*ln_cross_n_y + ln_cross_n_z*ln_cross_n_z);
480  ln_cross_n_x = ln_cross_n_x / ln_cross_n_norm;
481  ln_cross_n_y = ln_cross_n_y / ln_cross_n_norm;
482  ln_cross_n_z = ln_cross_n_z / ln_cross_n_norm;
483 
484  if (fabs(ln_cross_n_norm - 1) > 0.001)
485  {
486  XLAL_PRINT_ERROR("ln_cross_n mag = %.16f\n", ln_cross_n_norm);
487  XLAL_ERROR(XLAL_EDOM, "The size of the LN x n vector computed here is not equal to unity. This shouldn't be possible, please email lalsimulation developers.");
488  }
489 
490  z_wave_x = sinclination * (sorb_phase * n_hat_x + corb_phase * ln_cross_n_x);
491  z_wave_y = sinclination * (sorb_phase * n_hat_y + corb_phase * ln_cross_n_y);
492  z_wave_z = sinclination * (sorb_phase * n_hat_z + corb_phase * ln_cross_n_z);
493 
494  z_wave_x += cinclination * ln_hat_x;
495  z_wave_y += cinclination * ln_hat_y;
496  z_wave_z += cinclination * ln_hat_z;
497 
498  z_wave_norm = sqrt(z_wave_x * z_wave_x + z_wave_y * z_wave_y + z_wave_z * z_wave_z);
499 
500  z_wave_x = z_wave_x / z_wave_norm;
501  z_wave_y = z_wave_y / z_wave_norm;
502  z_wave_z = z_wave_z / z_wave_norm;
503 
504  if (fabs(z_wave_norm - 1) > 0.001)
505  {
506  XLAL_PRINT_ERROR("Z mag = %.16f\n", z_wave_norm);
507  XLAL_ERROR(XLAL_EDOM, "The size of the Z vector computed here is not equal to unity. This shouldn't be possible, please email lalsimulation developers.");
508  }
509 
510  /* Step 3.1: Extract theta and psi from Z in the lal wave frame
511  * NOTE: Theta can only run between 0 and pi, so no problem with arccos here
512  */
513 
514  *theta = acos(z_wave_z);
515 
516  /* Degenerate if Z_wave[2] == 1. In this case just choose psi randomly,
517  * the choice will be cancelled out by alpha correction (I hope!)
518  */
519 
520  if(fabs(z_wave_z - 1.0 ) < 0.000001)
521  {
522  *psi = 0.5;
523  }
524  else
525  {
526  /* psi can run between 0 and 2pi, but only one solution works for x and y */
527  /* Possible numerical issues if z_wave_x = sin(theta) */
528  if(fabs(z_wave_x / sin(*theta)) > 1.)
529  {
530  if(fabs(z_wave_x / sin(*theta)) < 1.00001)
531  {
532  if((z_wave_x * sin(*theta)) < 0.)
533  {
534  *psi = LAL_PI;
535  }
536  else
537  {
538  *psi = 0.;
539  }
540  }
541  else
542  {
543  XLAL_PRINT_ERROR("z_wave_x = %.16f\n", z_wave_x);
544  XLAL_PRINT_ERROR("sin(theta) = %.16f\n", sin(*theta));
545  XLAL_ERROR(XLAL_EDOM, "Z_x cannot be bigger than sin(theta). Please email lalsimulation developers.");
546  }
547  }
548  else
549  {
550  *psi = acos(z_wave_x / sin(*theta));
551  }
552  y_val = sin(*psi) * sin(*theta);
553  /* If z_wave[1] is negative, flip psi so that sin(psi) goes negative
554  * while preserving cos(psi) */
555  if( z_wave_y < 0.)
556  {
557  *psi = 2 * LAL_PI - *psi;
558  y_val = sin(*psi) * sin(*theta);
559  }
560  if( fabs(y_val - z_wave_y) > 0.005)
561  {
562  XLAL_PRINT_ERROR("orb_phase = %.16f\n", orb_phase);
563  XLAL_PRINT_ERROR("y_val = %.16f, z_wave_y = %.16f, fabs(y_val - z_wave_y) = %.16f\n", y_val, z_wave_y, fabs(y_val - z_wave_y));
564  XLAL_ERROR(XLAL_EDOM, "Something's wrong in Ian's math. Tell him he's an idiot!");
565  }
566  }
567 
568  /* 3.2: Compute the vectors theta_hat and psi_hat */
569 
570  stheta = sin(*theta);
571  ctheta = cos(*theta);
572  spsi = sin(*psi);
573  cpsi = cos(*psi);
574 
575  theta_hat_x = cpsi * ctheta;
576  theta_hat_y = spsi * ctheta;
577  theta_hat_z = - stheta;
578 
579  psi_hat_x = -spsi;
580  psi_hat_y = cpsi;
581  psi_hat_z = 0.0;
582 
583  /* Step 4: Compute sin(alpha) and cos(alpha) */
584 
585  n_dot_theta = n_hat_x * theta_hat_x + n_hat_y * theta_hat_y + n_hat_z * theta_hat_z;
586  ln_cross_n_dot_theta = ln_cross_n_x * theta_hat_x + ln_cross_n_y * theta_hat_y + ln_cross_n_z * theta_hat_z;
587  n_dot_psi = n_hat_x * psi_hat_x + n_hat_y * psi_hat_y + n_hat_z * psi_hat_z;
588  ln_cross_n_dot_psi = ln_cross_n_x * psi_hat_x + ln_cross_n_y * psi_hat_y + ln_cross_n_z * psi_hat_z;
589 
590  *salpha = corb_phase * n_dot_theta - sorb_phase * ln_cross_n_dot_theta;
591  *calpha = corb_phase * n_dot_psi - sorb_phase * ln_cross_n_dot_psi;
592 
593  /* Step 5: Also useful to keep the source frame vectors as defined in
594  * equation 16 of Harald's document.
595  */
596 
597  /*
598  * x_source_hat[0] = corb_phase * n_hat_x - sorb_phase * ln_cross_n_x;
599  * x_source_hat[1] = corb_phase * n_hat_y - sorb_phase * ln_cross_n_y;
600  * x_source_hat[2] = corb_phase * n_hat_z - sorb_phase * ln_cross_n_z;
601  * y_source_hat[0] = sorb_phase * n_hat_x + corb_phase * ln_cross_n_x;
602  * y_source_hat[1] = sorb_phase * n_hat_y + corb_phase * ln_cross_n_y;
603  * y_source_hat[2] = sorb_phase * n_hat_z + corb_phase * ln_cross_n_z;
604  * z_source_hat[0] = ln_hat_x;
605  * z_source_hat[1] = ln_hat_y;
606  * z_source_hat[2] = ln_hat_z;
607  */
608 
609  return XLAL_SUCCESS;
610  #endif
611 }
612 
614  UNUSED REAL8 *S1x, /**< [out] Dimensionless spin1x in LAL frame */
615  UNUSED REAL8 *S1y, /**< [out] Dimensionless spin1y in LAL frame */
616  UNUSED REAL8 *S1z, /**< [out] Dimensionless spin1z in LAL frame */
617  UNUSED REAL8 *S2x, /**< [out] Dimensionless spin2x in LAL frame */
618  UNUSED REAL8 *S2y, /**< [out] Dimensionless spin2y in LAL frame */
619  UNUSED REAL8 *S2z, /**< [out] Dimensionless spin2z in LAL frame */
620  UNUSED REAL8 fRef, /**< Reference frequency */
621  UNUSED REAL8 mTot, /**< Total mass */
622  UNUSED const char *NRDataFile /**< Location of NR HDF file */
623 )
624 {
625  #ifndef LAL_HDF5_ENABLED
626  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
627  #else
628  LALH5File *file;
629  file = XLALH5FileOpen(NRDataFile, "r");
630  if (file == NULL)
631  {
632  XLAL_ERROR(XLAL_EIO, "NR SIMULATION DATA FILE %s NOT FOUND.\n",
633  NRDataFile);
634  }
636  S2y, S2z, fRef, mTot,
637  file);
639  return XLAL_SUCCESS;
640  #endif
641 }
642 
643 
644 /* Everything needs to be declared as unused in case HDF is not enabled. */
645 
647  UNUSED SphHarmTimeSeries **Hlms, /**< OUTPUT */
648  UNUSED LIGOTimeGPS *epoch, /**< OUTPUT */
649  UNUSED UINT4 *length, /**< OUTPUT */
650  UNUSED REAL8 deltaT, /**< sampling interval (s) */
651  UNUSED REAL8 m1, /**< mass of companion 1 (solar units) */
652  UNUSED REAL8 m2, /**< mass of companion 2 (solar units) */
653  UNUSED REAL8 r, /**< distance of source (m) */
654  UNUSED REAL8 fStart, /**< start GW frequency (Hz) */
655  UNUSED REAL8 fRef, /**< reference GW frequency (Hz) */
656  UNUSED REAL8 s1x, /**< initial value of S1x */
657  UNUSED REAL8 s1y, /**< initial value of S1y */
658  UNUSED REAL8 s1z, /**< initial value of S1z */
659  UNUSED REAL8 s2x, /**< initial value of S2x */
660  UNUSED REAL8 s2y, /**< initial value of S2y */
661  UNUSED REAL8 s2z, /**< initial value of S2z */
662  UNUSED LALH5File* file, /**< pointer to location of NR HDF file */
663  UNUSED LALValue* ModeArray /**< Container for the ell and m modes to generate. To generate all available modes pass NULL */
664  )
665 {
666 #ifndef LAL_HDF5_ENABLED
667  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
668 #else
669  /* Declarations */
670  UINT4 curr_idx, nr_file_format;
671  INT4 model, modem;
672  size_t array_length;
673  REAL8 nrEta;
674  REAL8 S1x = 0, S1y = 0, S1z = 0, S2x = 0, S2y = 0, S2z = 0;
675  REAL8 Mflower, time_start_M, time_start_s, time_end_M, time_end_s;
676  REAL8 est_start_time;
677  REAL8 distance_scale_fac;
678  COMPLEX16TimeSeries *hlm;
679  SphHarmTimeSeries *hlms_tmp=NULL;
680 
681  /* These keys follow a strict formulation and cannot be longer than 11
682  * characters */
683  char amp_key[30];
684  char phase_key[31];
685  gsl_vector *tmpVector=NULL;
686  LALH5File *group;
687  LIGOTimeGPS tmpEpoch = LIGOTIMEGPSZERO;
688  REAL8Vector *curr_amp, *curr_phase;
689 
690  /* Sanity checks on physical parameters passed to waveform
691  * generator to guarantee consistency with NR data file.
692  */
694  if (fabs((m1 * m2) / pow((m1 + m2),2.0) - nrEta) > 1E-3)
695  {
696  XLAL_ERROR(XLAL_EDOM, "MASSES (%e and %e) ARE INCONSISTENT WITH THE MASS RATIO OF THE NR SIMULATION (eta=%e).\n", m1, m2, nrEta);
697  }
698 
699  /* Read spin metadata, L_hat, n_hat from HDF5 metadata and make sure
700  * the ChooseTDWaveform() input values are consistent with the data
701  * recorded in the metadata of the HDF5 file.
702  * PS: This assumes that the input spins are in the LAL frame!
703  */
705  &S2x, &S2y, &S2z,
706  fRef, m1+m2, file);
707  XLAL_CHECK(xlalErrno == 0, XLAL_EFUNC, "XLALSimInspiralNRWaveformGetSpinsFromHDF5FilePointer() failed");
708 
709  if (fabs(S1x - s1x) > 1E-3)
710  {
711  XLAL_ERROR(XLAL_EDOM, "SPIN1X IS INCONSISTENT WITH THE NR SIMULATION.\n");
712  }
713 
714  if (fabs(S1y - s1y) > 1E-3)
715  {
716  XLAL_ERROR(XLAL_EDOM, "SPIN1Y IS INCONSISTENT WITH THE NR SIMULATION.\n");
717  }
718 
719  if (fabs(S1z - s1z) > 1E-3)
720  {
721  XLAL_ERROR(XLAL_EDOM, "SPIN1Z IS INCONSISTENT WITH THE NR SIMULATION.\n");
722  }
723 
724  if (fabs(S2x - s2x) > 1E-3)
725  {
726  XLAL_ERROR(XLAL_EDOM, "SPIN2X IS INCONSISTENT WITH THE NR SIMULATION.\n");
727  }
728 
729  if (fabs(S2y - s2y) > 1E-3)
730  {
731  XLAL_ERROR(XLAL_EDOM, "SPIN2Y IS INCONSISTENT WITH THE NR SIMULATION.\n");
732  }
733 
734  if (fabs(S2z - s2z) > 1E-3)
735  {
736  XLAL_ERROR(XLAL_EDOM, "SPIN2Z IS INCONSISTENT WITH THE NR SIMULATION.\n");
737  }
738 
739 
740  /* First estimate the length of time series that is needed.
741  * Demand that 22 mode that is present and use that to figure this out
742  */
743 
744  XLALH5FileQueryScalarAttributeValue(&Mflower, file, "f_lower_at_1MSUN");
745  /* Figure out start time of data */
746  group = XLALH5GroupOpen(file, "amp_l2_m2");
747  ReadHDF5RealVectorDataset(group, "X", &tmpVector);
748  XLALH5FileClose(group);
749  time_start_M = (REAL8)(gsl_vector_get(tmpVector, 0));
750  time_end_M = (REAL8)(gsl_vector_get(tmpVector, tmpVector->size - 1));
751  gsl_vector_free(tmpVector);
752  time_start_s = time_start_M * (m1 + m2) * LAL_MTSUN_SI;
753  time_end_s = time_end_M * (m1 + m2) * LAL_MTSUN_SI;
754 
755  /* We don't want to return the *entire* waveform if it will be much longer
756  * than the specified f_lower. Therefore guess waveform length using
757  * the SEOBNR_ROM function and add 10% for safety.
758  * FIXME: Is this correct for precessing waveforms?
759  */
760 
761  /* We allow fstart to be fractionally lower than expected to
762  * try and catch rounding errors
763  */
764  if (fStart * (1 + 1E-5) < Mflower / (m1 + m2))
765  {
766  XLAL_ERROR(XLAL_EDOM, "WAVEFORM IS NOT LONG ENOUGH TO REACH f_low. \
767  fStart = %e, Mflower = %e, Mflower / (m1 + m2)) = %e",
768  fStart, Mflower, Mflower / (m1 + m2));
769  }
770 
771  XLALH5FileQueryScalarAttributeValue(&nr_file_format, file, "Format");
772  if (nr_file_format > 1)
773  {
774  if (XLALSimInspiralNRWaveformCheckFRef(file, fStart * (m1+m2)) > 0)
775  {
776  /* Can use Omega array to get start time */
777  est_start_time = XLALSimInspiralNRWaveformGetRefTimeFromRefFreq(file, fStart * (m1+m2));
778  REAL8 fminNR;
779  XLALH5FileQueryScalarAttributeValue(&fminNR, file, "f_lower_at_1MSUN");
780  XLAL_CHECK( est_start_time!=XLAL_FAILURE, XLAL_FAILURE, "Error computing starting time. Try setting f_low equal to the f_low given by the NR simulation (%.16f Hz)\n",fminNR/(m1+m2));
781  est_start_time = est_start_time * (m1 + m2) * LAL_MTSUN_SI;
782  }
783  else
784  {
785  /* This is the potential weird case where Omega-vs-time does not start
786  * at precisely the same time as flower_at_1MSUN. This gap should be
787  * small, so just use the full waveform here.
788  */
789  est_start_time = time_start_s;
790  }
791  }
792  else
793  {
794  /* Fall back on SEOBNR chirp time estimate */
795  XLALSimIMRSEOBNRv4ROMTimeOfFrequency(&est_start_time, fStart, m1 * LAL_MSUN_SI, m2 * LAL_MSUN_SI, s1z, s2z);
796  est_start_time = (-est_start_time) * 1.1;
797  }
798 
799  if (est_start_time > time_start_s)
800  {
801  /* Restrict start time of waveform */
802  time_start_s = est_start_time;
803  time_start_M = time_start_s / ((m1 + m2) * LAL_MTSUN_SI);
804  }
805 
806  array_length = (UINT4)(ceil( (time_end_s - time_start_s) / deltaT));
807  *length=array_length;
808 
809  /* Create the return time series, use arbitrary epoch here. We set this
810  * properly later. */
811  XLALGPSAdd(&tmpEpoch, time_start_s);
812  *epoch=tmpEpoch;
813 
814  /* Create the distance scale factor */
815  distance_scale_fac = (m1 + m2) * LAL_MRSUN_SI / r;
816 
817  /* Generate the waveform */
818  /* NOTE: We assume that for a given ell mode, all m modes are present */
819  INT4 NRLmax;
820 
821  XLALH5FileQueryScalarAttributeValue(&NRLmax, file, "Lmax");
822 
823  INT4 modearray_needs_destroying=0;
824  if ( ModeArray == NULL )
825  {/* Default behaviour: Generate all modes upto NRLmax */
826  modearray_needs_destroying=1;
827  ModeArray = XLALSimInspiralCreateModeArray();
828  for (int ell=2; ell<=NRLmax; ell++)
829  {
831  }
832  }
833  /* else Use the ModeArray given */
834  hlm=XLALCreateCOMPLEX16TimeSeries("hlm",&tmpEpoch,0.0,deltaT,&lalStrainUnit,array_length);
835  memset(hlm->data->data, 0, array_length * sizeof(COMPLEX16));
836 
837  for (model=2; model < (NRLmax + 1) ; model++)
838  {
839  for (modem=-model; modem < (model+1); modem++)
840  {
841 
842  /* first check if (l,m) mode is 'activated' in the ModeArray */
843  /* if activated then generate the mode, else skip this mode. */
844  if (XLALSimInspiralModeArrayIsModeActive(ModeArray, model, modem) != 1)
845  {
846  XLAL_PRINT_INFO("SKIPPING model = %i modem = %i\n", model, modem);
847  continue;
848  }
849  XLAL_PRINT_INFO("generating model = %i modem = %i\n", model, modem);
850 
851  snprintf(amp_key, sizeof(amp_key), "amp_l%d_m%d", model, modem);
852  snprintf(phase_key, sizeof(phase_key), "phase_l%d_m%d", model, modem);
853 
854  /* Check that both groups exist */
855  if (XLALH5FileCheckGroupExists(file, amp_key) == 0)
856  {
857  continue;
858  }
859  if (XLALH5FileCheckGroupExists(file, phase_key) == 0)
860  {
861  continue;
862  }
863 
864  /* Get amplitude and phase from file */
866  time_start_s, array_length, deltaT, amp_key);
867  XLALSimInspiralNRWaveformGetDataFromHDF5File(&curr_phase, file, (m1 + m2),
868  time_start_s, array_length, deltaT, phase_key);
869 
870  for (curr_idx = 0; curr_idx < array_length; curr_idx++)
871  {
872  hlm->data->data[curr_idx]= (curr_amp->data[curr_idx]*cos(curr_phase->data[curr_idx]) + I*curr_amp->data[curr_idx]*sin(curr_phase->data[curr_idx]) ) * distance_scale_fac;
873  }
874  /* Note that the hlm built here do not respect the LAL convention
875  * the function XLALSimIMRNRWaveformGetHlm will perform the pi/2 rotation
876  * necessary to bring the in the right frame.
877  * See https://dcc.ligo.org/LIGO-T1900080 for details.
878  */
879  hlms_tmp=XLALSphHarmTimeSeriesAddMode(hlms_tmp,hlm,model,modem);
880  XLALDestroyREAL8Vector(curr_amp);
881  XLALDestroyREAL8Vector(curr_phase);
882  }
883  }
885  if (modearray_needs_destroying)
886  XLALDestroyValue(ModeArray);
887 
888  *Hlms=hlms_tmp;
889 
890  return XLAL_SUCCESS;
891 #endif
892 }
893 
894 /* Everything needs to be declared as unused in case HDF is not enabled. */
896  UNUSED REAL8TimeSeries **hplus, /**< Output h_+ vector */
897  UNUSED REAL8TimeSeries **hcross, /**< Output h_x vector */
898  UNUSED REAL8 phiRef, /**< orbital phase at reference pt. */
899  UNUSED REAL8 inclination, /**< inclination angle */
900  UNUSED REAL8 deltaT, /**< sampling interval (s) */
901  UNUSED REAL8 m1_SI, /**< mass of companion 1 (kg) */
902  UNUSED REAL8 m2_SI, /**< mass of companion 2 (kg) */
903  UNUSED REAL8 r, /**< distance of source (m) */
904  UNUSED REAL8 fStart, /**< start GW frequency (Hz) */
905  UNUSED REAL8 fRef, /**< reference GW frequency (Hz) */
906  UNUSED REAL8 s1x, /**< initial value of S1x */
907  UNUSED REAL8 s1y, /**< initial value of S1y */
908  UNUSED REAL8 s1z, /**< initial value of S1z */
909  UNUSED REAL8 s2x, /**< initial value of S2x */
910  UNUSED REAL8 s2y, /**< initial value of S2y */
911  UNUSED REAL8 s2z, /**< initial value of S2z */
912  UNUSED const char *NRDataFile, /**< Location of NR HDF file */
913  UNUSED LALValue* ModeArray /**< Container for the ell and m modes to generate. To generate all available modes pass NULL */
914  )
915 {
916 #ifndef LAL_HDF5_ENABLED
917  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
918 #else
919  /* Declarations */
920  UINT4 curr_idx, array_length, nr_file_format;
921  INT4 model, modem, NRLmax;
922  REAL8 m1,m2;
923  REAL8 theta, psi, calpha, salpha;
924  REAL8 fRef_pass=fRef;
925  COMPLEX16 curr_ylm, tmp;
926  COMPLEX16TimeSeries *curr_hlm=NULL;
927  REAL8TimeSeries *hplus_corr;
928  REAL8TimeSeries *hcross_corr;
929 
930  /* These keys follow a strict formulation and cannot be longer than 11
931  * characters */
932  LALH5File *file=NULL;
933  LIGOTimeGPS tmpEpoch = LIGOTIMEGPSZERO;
934  SphHarmTimeSeries *tmp_Hlms=NULL;
935 
936  /* Use solar masses for units. NR files will use
937  * solar masses as well, so easier for that conversion
938  */
939  m1 = m1_SI / LAL_MSUN_SI;
940  m2 = m2_SI / LAL_MSUN_SI;
941 
942  file = XLALH5FileOpen(NRDataFile, "r");
943  if (file == NULL)
944  {
945  XLAL_ERROR(XLAL_EIO, "NR SIMULATION DATA FILE %s NOT FOUND.\n", NRDataFile);
946  }
947 
948  XLALH5FileQueryScalarAttributeValue(&nr_file_format, file, "Format");
949  if (nr_file_format < 2)
950  {
951  XLALPrintInfo("This NR file is format %d. Only formats 2 and above support the use of reference frequency. For formats < 2 the reference frequency always corresponds to the start of the waveform.", nr_file_format);
952  fRef_pass = -1;
953  }
954  INT4 err_code = XLALSimIMRNRWaveformGetModes(&tmp_Hlms,&tmpEpoch,&array_length, \
955  deltaT, m1, m2, r, fStart, fRef_pass, \
956  s1x,s1y,s1z,s2x,s2y,s2z, \
957  file, ModeArray);
958  if (err_code!=XLAL_SUCCESS)
960 
961  /* Compute correct angles for hplus and hcross following LAL convention. */
962 
963  theta = psi = calpha = salpha = 0.;
965  &salpha, file, inclination, phiRef, fRef_pass*(m1+m2));
967 
968  *hplus = XLALCreateREAL8TimeSeries("H_PLUS", &tmpEpoch, 0.0, deltaT,
969  &lalStrainUnit, array_length );
970  *hcross = XLALCreateREAL8TimeSeries("H_CROSS", &tmpEpoch, 0.0, deltaT,
971  &lalStrainUnit, array_length );
972 
973  hplus_corr = XLALCreateREAL8TimeSeries("H_PLUS", &tmpEpoch, 0.0, deltaT,
974  &lalStrainUnit, array_length );
975  hcross_corr = XLALCreateREAL8TimeSeries("H_CROSS", &tmpEpoch, 0.0, deltaT,
976  &lalStrainUnit, array_length );
977  for (curr_idx = 0; curr_idx < array_length; curr_idx++)
978  {
979  hplus_corr->data->data[curr_idx] = 0.0;
980  hcross_corr->data->data[curr_idx] = 0.0;
981  }
982 
983  NRLmax=XLALSphHarmTimeSeriesGetMaxL(tmp_Hlms);
984 
985  for (model=2; model < (NRLmax + 1) ; model++)
986  {
987  for (modem=-model; modem < (model+1); modem++)
988  {
989  curr_ylm = XLALSpinWeightedSphericalHarmonic(theta, psi, -2, model, modem);
990  curr_hlm = XLALSphHarmTimeSeriesGetMode(tmp_Hlms, model, modem);
991  if (curr_hlm) {
992  for (curr_idx = 0; curr_idx < array_length; curr_idx++)
993  {
994  tmp=curr_hlm->data->data[curr_idx]*curr_ylm;
995  hplus_corr->data->data[curr_idx] += creal(tmp);
996  hcross_corr->data->data[curr_idx] -= cimag(tmp);
997  }
998  }
999  }
1000  }
1001  XLALDestroySphHarmTimeSeries(tmp_Hlms);
1002 
1003  /* Correct for the "alpha" angle as given in T1500606 or arxiv:1703.01076
1004  * to translate from the NR wave frame to LAL wave-frame.
1005  *
1006  * Helper time series needed.
1007  */
1008 
1009  for (curr_idx = 0; curr_idx < array_length; curr_idx++)
1010  {
1011  (*hplus)->data->data[curr_idx] =
1012  (calpha*calpha - salpha*salpha) * hplus_corr->data->data[curr_idx]
1013  - 2.0*calpha*salpha * hcross_corr->data->data[curr_idx];
1014 
1015  (*hcross)->data->data[curr_idx] =
1016  + 2.0*calpha*salpha * hplus_corr->data->data[curr_idx]
1017  + (calpha*calpha - salpha*salpha) * hcross_corr->data->data[curr_idx];
1018  }
1019 
1020  XLALDestroyREAL8TimeSeries(hplus_corr);
1021  XLALDestroyREAL8TimeSeries(hcross_corr);
1022 
1023  return XLAL_SUCCESS;
1024 #endif
1025 }
1026 
1027 /* Everything needs to be declared as unused in case HDF is not enabled. */
1029  UNUSED REAL8 deltaT, /**< sampling interval (s) */
1030  UNUSED REAL8 m1_SI, /**< mass of companion 1 (kg) */
1031  UNUSED REAL8 m2_SI, /**< mass of companion 2 (kg) */
1032  UNUSED REAL8 r, /**< distance of source (m) */
1033  UNUSED REAL8 fStart, /**< start GW frequency (Hz) */
1034  UNUSED REAL8 fRef, /**< reference GW frequency (Hz) */
1035  UNUSED REAL8 s1x, /**< initial value of S1x */
1036  UNUSED REAL8 s1y, /**< initial value of S1y */
1037  UNUSED REAL8 s1z, /**< initial value of S1z */
1038  UNUSED REAL8 s2x, /**< initial value of S2x */
1039  UNUSED REAL8 s2y, /**< initial value of S2y */
1040  UNUSED REAL8 s2z, /**< initial value of S2z */
1041  UNUSED const char *NRDataFile, /**< Location of NR HDF file */
1042  UNUSED LALValue* ModeArray /**< Container for the ell and m modes to generate. To generate all available modes pass NULL */
1043  )
1044 {
1045 #ifndef LAL_HDF5_ENABLED
1046  XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled");
1047 #else
1048  /* Declarations */
1049  UINT4 curr_idx, array_length, nr_file_format;
1050  INT4 model, modem, NRLmax;
1051  REAL8 m1,m2;
1052  REAL8 theta, psi, calpha, salpha;
1053  REAL8 fRef_pass=fRef;
1054  COMPLEX16TimeSeries *tmp_hlm=NULL;
1055  SphHarmTimeSeries *tmp_Hlms=NULL;
1056  LALH5File *file;
1057  LIGOTimeGPS tmpEpoch = LIGOTIMEGPSZERO;
1058 
1059  /* Use solar masses for units. NR files will use
1060  * solar masses as well, so easier for that conversion
1061  */
1062  m1 = m1_SI / LAL_MSUN_SI;
1063  m2 = m2_SI / LAL_MSUN_SI;
1064 
1065  file = XLALH5FileOpen(NRDataFile, "r");
1066  if (file == NULL)
1067  {
1068  XLAL_ERROR(XLAL_EIO, "NR SIMULATION DATA FILE %s NOT FOUND.\n", NRDataFile);
1069  }
1070 
1071  XLALH5FileQueryScalarAttributeValue(&nr_file_format, file, "Format");
1072  if (nr_file_format < 2)
1073  {
1074  XLALPrintInfo("This NR file is format %d. Only formats 2 and above support the use of reference frequency. For formats < 2 the reference frequency always corresponds to the start of the waveform.", nr_file_format);
1075  fRef_pass = -1;
1076  }
1077 
1078  INT4 err_code = XLALSimIMRNRWaveformGetModes(&tmp_Hlms,&tmpEpoch,&array_length, \
1079  deltaT, m1, m2, r, fStart, fRef_pass, \
1080  s1x,s1y,s1z,s2x,s2y,s2z, \
1081  file, ModeArray);
1082  if (err_code!=XLAL_SUCCESS)
1084 
1085  /* Compute correct angles for hplus and hcross following LAL convention. */
1086 
1087  psi = calpha = salpha = 0.;
1089  &salpha, file, 0., 0., fRef_pass*(m1+m2));
1091 
1092  NRLmax= XLALSphHarmTimeSeriesGetMaxL(tmp_Hlms);
1093  COMPLEX16 facm=-1;
1094  for (model=2; model < (NRLmax + 1) ; model++)
1095  {
1096  for (modem=-model; modem < (model+1); modem++)
1097  {
1098  tmp_hlm=XLALSphHarmTimeSeriesGetMode(tmp_Hlms,model,modem);
1099  if (tmp_hlm) {
1100  for (curr_idx = 0; curr_idx < array_length; curr_idx++)
1101  tmp_hlm->data->data[curr_idx]*=facm;
1102  *Hlms=XLALSphHarmTimeSeriesAddMode(*Hlms,tmp_hlm,model,modem);
1103  }
1104  facm*=I;
1105  }
1106  facm=1./facm;
1107  }
1108  XLALDestroySphHarmTimeSeries(tmp_Hlms);
1109 
1110  /* Correct for the "alpha" angle as given in T1500606 or arxiv:1703.01076
1111  * to translate from the NR wave frame to LAL wave-frame.
1112  */
1113 
1114  REAL8TimeSeries *alphats = XLALCreateREAL8TimeSeries("alpha", &tmpEpoch, 0.0, deltaT, &lalDimensionlessUnit, array_length );
1115  REAL8TimeSeries *thetats = XLALCreateREAL8TimeSeries("theta", &tmpEpoch, 0.0, deltaT, &lalDimensionlessUnit, array_length );
1116  REAL8TimeSeries *psits = XLALCreateREAL8TimeSeries("tpsi", &tmpEpoch, 0.0, deltaT, &lalDimensionlessUnit, array_length );
1117  REAL8 alpha=atan2(salpha,calpha);
1118 
1119  for (curr_idx = 0; curr_idx < array_length; curr_idx++) {
1120  alphats->data->data[curr_idx] = alpha;
1121  thetats->data->data[curr_idx] =-theta;
1122  psits->data->data[curr_idx] =-psi;
1123  }
1124 
1125  err_code=XLALSimInspiralPrecessionRotateModes(*Hlms,alphats,thetats,psits);
1126  if (err_code!=XLAL_SUCCESS)
1128  XLALDestroyREAL8TimeSeries(alphats);
1129  XLALDestroyREAL8TimeSeries(thetats);
1131 
1132  return XLAL_SUCCESS;
1133 #endif
1134 }
struct tagLALH5File LALH5File
int XLALSimIMRSEOBNRv4ROMTimeOfFrequency(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 UNUSED REAL8 XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint(UNUSED LALH5File *file, UNUSED const char *groupName, UNUSED REAL8 ref_point)
int XLALSimInspiralNRWaveformGetSpinsFromHDF5File(UNUSED REAL8 *S1x, UNUSED REAL8 *S1y, UNUSED REAL8 *S1z, UNUSED REAL8 *S2x, UNUSED REAL8 *S2y, UNUSED REAL8 *S2z, UNUSED REAL8 fRef, UNUSED REAL8 mTot, UNUSED const char *NRDataFile)
INT4 XLALSimInspiralNRWaveformGetHplusHcross(UNUSED REAL8TimeSeries **hplus, UNUSED REAL8TimeSeries **hcross, UNUSED REAL8 phiRef, UNUSED REAL8 inclination, UNUSED REAL8 deltaT, UNUSED REAL8 m1_SI, UNUSED REAL8 m2_SI, UNUSED REAL8 r, UNUSED REAL8 fStart, UNUSED REAL8 fRef, UNUSED REAL8 s1x, UNUSED REAL8 s1y, UNUSED REAL8 s1z, UNUSED REAL8 s2x, UNUSED REAL8 s2y, UNUSED REAL8 s2z, UNUSED const char *NRDataFile, UNUSED LALValue *ModeArray)
static UNUSED REAL8 XLALSimInspiralNRWaveformGetRefTimeFromRefFreq(UNUSED LALH5File *file, UNUSED REAL8 fRef)
static UNUSED UINT4 XLALSimInspiralNRWaveformGetDataFromHDF5File(UNUSED REAL8Vector **output, UNUSED LALH5File *pointer, UNUSED REAL8 totalMass, UNUSED REAL8 startTime, UNUSED size_t length, UNUSED REAL8 deltaT, UNUSED const char *keyName)
INT4 XLALSimInspiralNRWaveformGetHlms(UNUSED SphHarmTimeSeries **Hlms, UNUSED REAL8 deltaT, UNUSED REAL8 m1_SI, UNUSED REAL8 m2_SI, UNUSED REAL8 r, UNUSED REAL8 fStart, UNUSED REAL8 fRef, UNUSED REAL8 s1x, UNUSED REAL8 s1y, UNUSED REAL8 s1z, UNUSED REAL8 s2x, UNUSED REAL8 s2y, UNUSED REAL8 s2z, UNUSED const char *NRDataFile, UNUSED LALValue *ModeArray)
static UNUSED INT4 XLALSimIMRNRWaveformGetModes(UNUSED SphHarmTimeSeries **Hlms, UNUSED LIGOTimeGPS *epoch, UNUSED UINT4 *length, UNUSED REAL8 deltaT, UNUSED REAL8 m1, UNUSED REAL8 m2, UNUSED REAL8 r, UNUSED REAL8 fStart, UNUSED REAL8 fRef, UNUSED REAL8 s1x, UNUSED REAL8 s1y, UNUSED REAL8 s1z, UNUSED REAL8 s2x, UNUSED REAL8 s2y, UNUSED REAL8 s2z, UNUSED LALH5File *file, UNUSED LALValue *ModeArray)
static UNUSED UINT4 XLALSimInspiralNRWaveformGetSpinsFromHDF5FilePointer(UNUSED REAL8 *S1x, UNUSED REAL8 *S1y, UNUSED REAL8 *S1z, UNUSED REAL8 *S2x, UNUSED REAL8 *S2y, UNUSED REAL8 *S2z, UNUSED REAL8 fRef, UNUSED REAL8 mTot, UNUSED LALH5File *file)
static UNUSED REAL8 XLALSimInspiralNRWaveformCheckFRef(UNUSED LALH5File *file, UNUSED REAL8 fRef)
static UNUSED UINT4 XLALSimInspiralNRWaveformGetRotationAnglesFromH5File(UNUSED REAL8 *theta, UNUSED REAL8 *psi, UNUSED REAL8 *calpha, UNUSED REAL8 *salpha, UNUSED LALH5File *filepointer, UNUSED const REAL8 inclination, UNUSED const REAL8 phi_ref, UNUSED REAL8 fRef)
Auxiliary functions for SEOBNRv1/v2 reduced order modeling codes in LALSimIMRSEOBNRv2ROMDoubleSpin....
void XLALDestroyValue(LALValue *value)
double theta
Definition: bh_sphwf.c:118
void XLALH5FileClose(LALH5File UNUSED *file)
LALH5File * XLALH5GroupOpen(LALH5File UNUSED *file, const char UNUSED *name)
int XLALH5FileQueryScalarAttributeValue(void UNUSED *value, LALH5File UNUSED *file, const char UNUSED *key)
LALH5File * XLALH5FileOpen(const char UNUSED *path, const char UNUSED *mode)
int XLALH5FileCheckGroupExists(const LALH5File UNUSED *file, const char UNUSED *name)
#define LAL_MSUN_SI
#define LAL_PI
#define LAL_MTSUN_SI
#define LAL_MRSUN_SI
#define LIGOTIMEGPSZERO
double complex COMPLEX16
double REAL8
int64_t INT8
uint32_t UINT4
int32_t INT4
int XLALSimInspiralPrecessionRotateModes(SphHarmTimeSeries *h_lm, REAL8TimeSeries *alpha, REAL8TimeSeries *beta, REAL8TimeSeries *gam)
Takes in the h_lm spherical harmonic decomposed modes and rotates the modes by Euler angles alpha,...
LALValue * XLALSimInspiralCreateModeArray(void)
Create a LALValue pointer to store the mode array.
int XLALSimInspiralModeArrayIsModeActive(LALValue *modes, unsigned l, int m)
LALValue * XLALSimInspiralModeArrayActivateAllModesAtL(LALValue *modes, unsigned l)
SphHarmTimeSeries * XLALSphHarmTimeSeriesAddMode(SphHarmTimeSeries *appended, const COMPLEX16TimeSeries *inmode, UINT4 l, INT4 m)
Prepend a node to a linked list of SphHarmTimeSeries, or create a new head.
COMPLEX16TimeSeries * XLALSphHarmTimeSeriesGetMode(SphHarmTimeSeries *ts, UINT4 l, INT4 m)
Get the time series of a waveform's (l,m) spherical harmonic mode from a SphHarmTimeSeries linked lis...
UINT4 XLALSphHarmTimeSeriesGetMaxL(SphHarmTimeSeries *ts)
Get the largest l index of any mode in the SphHarmTimeSeries linked list.
void XLALDestroySphHarmTimeSeries(SphHarmTimeSeries *ts)
Delete list from current pointer to the end of the list.
static const INT4 r
COMPLEX16 XLALSpinWeightedSphericalHarmonic(REAL8 theta, REAL8 phi, int s, int l, int m)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
void XLALDestroyCOMPLEX16TimeSeries(COMPLEX16TimeSeries *series)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
COMPLEX16TimeSeries * XLALCreateCOMPLEX16TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
const LALUnit lalStrainUnit
const LALUnit lalDimensionlessUnit
REAL8Vector * XLALCreateREAL8Vector(UINT4 length)
void XLALDestroyREAL8Vector(REAL8Vector *vector)
#define XLAL_PRINT_INFO(...)
#define xlalErrno
int int int XLALPrintInfo(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
#define XLAL_PRINT_ERROR(...)
XLAL_SUCCESS
XLAL_EFUNC
XLAL_EDOM
XLAL_EIO
XLAL_EINVAL
XLAL_EFAILED
XLAL_FAILURE
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
string status
double alpha
Definition: sgwb.c:106
COMPLEX16Sequence * data
COMPLEX16 * data
REAL8Sequence * data
REAL8 * data
Structure to carry a collection of spherical harmonic modes in COMPLEX16 time series.
LIGOTimeGPS epoch
Definition: unicorn.c:20
char output[FILENAME_MAX]
Definition: unicorn.c:26
double deltaT
Definition: unicorn.c:24