LALSimulation  5.4.0.1-fe68b98
LALSimNRSur7dq4Remnant.c
Go to the documentation of this file.
1 /* Copyright (C) 2019 Vijay Varma
2  * Evaluates NRSur7dq4Remnant model for remnant BH mass, spin and recoil kick
3  * for generically precessing BBH.
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 /**
22  * \author Vijay Varma
23  *
24  * \file
25  *
26  * \brief NRSur7dq4Remnant model for remnant BH mass, spin and recoil kick for
27  * generically precessing BBH.
28  *
29  * The binary data file is available at:
30  * https://dcc.ligo.org/LIGO-T1900393/public.
31  * Get the lalsuite-extra repo or put the data into a location in your
32  * LAL_DATA_PATH.
33  *
34  * **Paper**: https://arxiv.org/abs/1905.09300,
35  * https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.1.033015
36  *
37  * **Parameter ranges**:
38  *
39  * q = [1, 6.01]
40  *
41  * \f$\chi_{1}, \chi_{2}\f$ = [-1, 1]
42  *
43  * **Training parameter ranges**:
44  *
45  * q = [1, 4.01]
46  *
47  * \f$\chi_{1}, \chi_{2}\f$ = [-0.81, 0.81]
48  *
49  * But extrapolates reasonably to the above mass ratios and spins. However,
50  * if a guarantee of accuracy is required, this model should be used within
51  * the training parameter range.
52  */
53 
54 #ifdef __GNUC__
55 #define UNUSED __attribute__ ((unused))
56 #else
57 #define UNUSED
58 #endif
59 
60 
61 #include <libgen.h>
62 
63 #include <gsl/gsl_bspline.h>
64 #include <gsl/gsl_blas.h>
65 #include <gsl/gsl_spline.h>
66 #include <gsl/gsl_complex_math.h>
67 
68 #include <lal/SeqFactories.h>
69 #include <lal/FileIO.h>
70 #include <lal/H5FileIO.h>
71 #include <lal/XLALError.h>
72 #include <lal/LALSimIMR.h>
73 
75 
76 #include "LALSimNRSur7dq4Remnant.h"
77 
78 
79 #ifdef LAL_PTHREAD_LOCK
80 #include <pthread.h>
81 #endif
82 
83 #ifdef LAL_PTHREAD_LOCK
84 static pthread_once_t NRSur7dq4Remnant_is_initialized = PTHREAD_ONCE_INIT;
85 #endif
86 
87 
88 /**
89  * Global surrogate data. This data will be loaded at most once. Any
90  * executable which calls NRSur7dq4Remnant_Init_LALDATA directly or by calling
91  * the XLALNRSur7dq4Remnant function will have a memory leak according to
92  * valgrind, because we never free this memory. This memory, however, gets freed
93  * after the executable terminates.
94  */
96 
97 //*************************************************************************/
98 //************************* function definitions **************************/
99 //*************************************************************************/
100 
101 /**
102  * Helper function to check if the NRSur7dq4Remnant model has been initialized.
103  */
104 static bool NRSur7dq4Remnant_IsSetup(void) {
106  return true;
107  else
108  return false;
109 }
110 
111 /**
112  * Surrogate initialization.
113  *
114  * This needs to be called once, before __lalsim_NRSur7dq4Remnant_data is used.
115  * It finds the H5 data file with the NRSur7dq4Remnant data and loads the
116  * surrogate. Can be called multiple times, will just return true if surrogate
117  * is already loaded.
118  */
119 static void NRSur7dq4Remnant_Init_LALDATA(void) {
120 
121  if (NRSur7dq4Remnant_IsSetup()) return;
122 
123  LALH5File *file = NULL;
125 
127  file);
128 
130  if (ret != XLAL_SUCCESS) {
131  XLAL_ERROR_VOID(XLAL_FAILURE, "Failure loading data from %s\n",
133  }
134 }
135 
136 /**
137  * Map from mass ratio and spins to surrogate fit parameters.
138  *
139  * The fit parameters are \f$[log_e(q), \chi_{1x}, \chi_{1y}, \hat{\chi},
140  * \chi_{2x}, \chi_{2y} \chi_a]\f$.
141  * \f$\hat{\chi}\f$ is defined in Eq.(8) of arxiv:1905.09300.
142  * \f$\chi_a = (\chi_{1z} - \chi_{2z})/2 \f$.
143  *
144  * The spins must be specified in the coorbital frame at t=-100M from
145  * the total amplitude peak, as described in Sec.IV.C of arxiv:1905.09300.
146  */
148  gsl_vector* fit_params, /**< Output: mapped fit parameters. */
149  const REAL8 q, /**< Mass ratio m1 / m2 >= 1. */
150  const REAL8 chiAx, /**< Dimless x-spin of heavier BH. */
151  const REAL8 chiAy, /**< Dimless y-spin of heavier BH. */
152  const REAL8 chiAz, /**< Dimless z-spin of heavier BH. */
153  const REAL8 chiBx, /**< Dimless x-spin of lighter BH. */
154  const REAL8 chiBy, /**< Dimless y-spin of lighter BH. */
155  const REAL8 chiBz, /**< Dimless z-spin of lighter BH. */
156  LALDict* LALparams /**< Dict with extra parameters */
157 ) {
158 
159  // By default we do not allow unlimited_extrapolation
160  UINT4 unlim_extrap = 0;
161  if (LALparams != NULL &&
162  XLALDictContains(LALparams, "unlimited_extrapolation")) {
163  // Unless the user asks for it
164  unlim_extrap
165  = XLALDictLookupUINT4Value(LALparams, "unlimited_extrapolation");
166  }
167 
168  //// Sanity check parameter ranges
169  REAL8 chiAmag = sqrt(chiAx*chiAx + chiAy*chiAy + chiAz*chiAz);
170  REAL8 chiBmag = sqrt(chiBx*chiBx + chiBy*chiBy + chiBz*chiBz);
171  // These are the limits of the training parameter space, so print a
172  // warning when extrapolated beyond.
173  REAL8 q_max_soft = 4.01;
174  REAL8 chi_max_soft = 0.81;
175  // These are the limits beyond which extrapolation is expected to be
176  // very bad, so raise an error.
177  REAL8 q_max_hard = 6.01;
178 
179  if (q < 1) {
180  XLAL_ERROR(XLAL_FAILURE, "Invalid mass ratio q = %0.4f < 1\n", q);
181  }
182  if ((q > q_max_hard) && (unlim_extrap == 0)) {
184  "Too much extrapolation in mass ratio; q = %0.4f > %0.4f\n",
185  q, q_max_hard);
186  }
187  if (q > q_max_soft) {
189  "Extrapolating outside training range q = %0.4f > %0.4f\n",
190  q, q_max_soft);
191  }
192 
193  if (chiAmag > 1) {
195  "Invalid spin magnitude |chiA| = %0.4f > 1\n", chiAmag);
196  }
197  if (chiBmag > 1) {
199  "Invalid spin magnitude |chiB| = %0.4f > 1\n", chiBmag);
200  }
201  if (chiAmag > chi_max_soft) {
203  "Extrapolating outside training range |chiA| = %0.4f > %0.4f\n",
204  chiAmag, chi_max_soft);
205  }
206  if (chiBmag > chi_max_soft) {
208  "Extrapolating outside training range |chiB| = %0.4f > %0.4f\n",
209  chiBmag, chi_max_soft);
210  }
211 
212 
213  const REAL8 eta = q/(1.+q)/(1.+q);
214  const REAL8 chi_wtAvg = (q*chiAz+chiBz)/(1.+q);
215  const REAL8 chi_hat
216  = (chi_wtAvg - 38.*eta/113.*(chiAz + chiBz))/(1. - 76.*eta/113.);
217  const REAL8 chi_a = (chiAz - chiBz)/2.;
218 
219  XLAL_CHECK((fit_params != NULL) && (fit_params->size == 7), XLAL_EDIMS,
220  "Size of fit_params should be 7, not %zu.\n", fit_params->size);
221 
222  gsl_vector_set(fit_params, 0, log(q));
223  gsl_vector_set(fit_params, 1, chiAx);
224  gsl_vector_set(fit_params, 2, chiAy);
225  gsl_vector_set(fit_params, 3, chi_hat);
226  gsl_vector_set(fit_params, 4, chiBx);
227  gsl_vector_set(fit_params, 5, chiBy);
228  gsl_vector_set(fit_params, 6, chi_a);
229 
230  return XLAL_SUCCESS;
231 }
232 
233 //************************ main functions ***************************/
234 
235 /**
236  * Returns the remnant BH's mass, spin, or kick according to NRSur7dq4Remnant
237  * model.
238  *
239  * Reference: arxiv:1905.09300.
240  *
241  * The input spins must be dimensionless, and given in the coorbital frame at
242  * t=-100M from the total amplitude peak, as described in the paper. The
243  * remnant mass is returned in units of total mass, the remnant spin is
244  * dimensionless, and the remnant kick is in units of c. The remnant spin and
245  * kick vectors are also returned in the same frame.
246  *
247  * remnant_property can be one of "mf", "chif" or "vf", respectively, for
248  * the remnant mass, spin or kick.
249  *
250  * Usage examples:
251  * mf = lalsim.NRSur7dq4Remnant(q, s1x, s1y, s1z, s2x, s2y, s2z, "mf").data[0]
252  * chif = lalsim.NRSur7dq4Remnant(q, s1x, s1y, s1z, s2x, s2y, s2z, "chif").data
253  */
255  gsl_vector **result, /**<Output: The requested remnant property. */
256  REAL8 q, /**< Mass ratio of Bh1/Bh2. q>=1. */
257  REAL8 s1x, /**< S1x in coorbital frame at t=-100M */
258  REAL8 s1y, /**< S1y in coorbital frame at t=-100M */
259  REAL8 s1z, /**< S1z in coorbital frame at t=-100M */
260  REAL8 s2x, /**< S2x in coorbital frame at t=-100M */
261  REAL8 s2y, /**< S2y in coorbital frame at t=-100M */
262  REAL8 s2z, /**< S2z in coorbital frame at t=-100M */
263  char *remnant_property, /**< One of "mf", "chif" or "vf" */
264  LALDict* LALparams /**< Dict with extra parameters */
265 ) {
266 
267 #ifdef LAL_PTHREAD_LOCK
268  (void) pthread_once(&NRSur7dq4Remnant_is_initialized,
270 #else
272 #endif
273 
274  // Loaded surrogate data
276  if (!sur_data->setup) {
277  XLAL_ERROR(XLAL_EFAILED, "Error loading surrogate data.\n");
278  }
279 
280  // assign size to dummy_worker
281  gsl_vector *dummy_worker = gsl_vector_alloc(sur_data->params_dim);
282 
283  // Compute fit_params (initialize with dimension of the surrogate)
284  gsl_vector *fit_params = gsl_vector_alloc(sur_data->params_dim);
285  int ret = NRSur7dq4Remnant_fitParams(fit_params, q, s1x, s1y, s1z,
286  s2x, s2y, s2z, LALparams);
287  if(ret != XLAL_SUCCESS) {
288  XLAL_ERROR(XLAL_EFUNC, "Failed to evaluate fit_params.");
289  }
290 
291  REAL8 tmp;
292  // For final mass, we have a single scalar fit
293  if (strcmp(remnant_property, "mf") == 0) {
294 
295  ScalarFitData *fit_data = sur_data->mf_data;
296 
297  *result = gsl_vector_alloc(1);
298  tmp = NRHybSur_eval_fit(fit_data, fit_params, sur_data->x_train,
299  dummy_worker);
300  gsl_vector_set(*result, 0, tmp);
301 
302  return XLAL_SUCCESS;
303 
304  // For final spin and kick, we have a vector fit
305  } else {
306 
307  VectorFitData *vec_data;
308  if (strcmp(remnant_property, "chif") == 0) {
309  vec_data = sur_data->chif_data;
310  } else if (strcmp(remnant_property, "vf") == 0) {
311  vec_data = sur_data->vf_data;
312  } else {
313  XLAL_ERROR(XLAL_EINVAL, "Invalid remnant_property, should be one "
314  "of 'mf', 'chif' or 'vf'");
315  }
316 
317  *result = gsl_vector_alloc(vec_data->vec_dim);
318  for (UINT4 i=0; i<vec_data->vec_dim; i++) {
319  tmp = NRHybSur_eval_fit(vec_data->fit_data[i], fit_params,
320  sur_data->x_train, dummy_worker);
321  gsl_vector_set(*result, i, tmp);
322  }
323 
324  return XLAL_SUCCESS;
325  }
326 }
struct tagLALH5File LALH5File
int XLALDictContains(const LALDict *dict, const char *key)
UINT4 XLALDictLookupUINT4Value(LALDict *dict, const char *key)
REAL8 NRHybSur_eval_fit(const NRHybSurFitData *fit_data, const gsl_vector *fit_params, const gsl_matrix *x_train, gsl_vector *dummy_worker)
Evaluate a NRHybSur fit.
static bool NRSur7dq4Remnant_IsSetup(void)
Helper function to check if the NRSur7dq4Remnant model has been initialized.
int XLALNRSur7dq4Remnant(gsl_vector **result, REAL8 q, REAL8 s1x, REAL8 s1y, REAL8 s1z, REAL8 s2x, REAL8 s2y, REAL8 s2z, char *remnant_property, LALDict *LALparams)
Returns the remnant BH's mass, spin, or kick according to NRSur7dq4Remnant model.
static int NRSur7dq4Remnant_fitParams(gsl_vector *fit_params, const REAL8 q, const REAL8 chiAx, const REAL8 chiAy, const REAL8 chiAz, const REAL8 chiBx, const REAL8 chiBy, const REAL8 chiBz, LALDict *LALparams)
Map from mass ratio and spins to surrogate fit parameters.
static PrecessingRemnantFitData __lalsim_NRSur7dq4Remnant_data
Global surrogate data.
static void NRSur7dq4Remnant_Init_LALDATA(void)
Surrogate initialization.
NRSur7dq4Remnant model for remnant BH mass, spin and recoil kick for generically precessing BBH.
static const char NRSur7dq4Remnant_DATAFILE[]
void NRSurRemnant_LoadH5File(LALH5File **file, const char *NRSurRemnant_DATAFILE)
Loads H5 file for a NRSurRemnant model.
int PrecessingNRSurRemnant_Init(PrecessingRemnantFitData *sur_data, LALH5File *file)
Initializes fit data for a precessing NRSurRemnant.
Utils for NR surrogates for remnant BH mass, spin and recoil kick.
#define print_warning(...)
#define ScalarFitData
double i
Definition: bh_ringdown.c:118
void XLALH5FileClose(LALH5File UNUSED *file)
double REAL8
uint32_t UINT4
static const INT4 q
#define XLAL_ERROR_VOID(...)
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
XLAL_SUCCESS
XLAL_EFUNC
XLAL_EDIMS
XLAL_EINVAL
XLAL_EFAILED
XLAL_FAILURE
int pthread_once(pthread_once_t *once_control, void(*init_routine)(void))
NRSurRemnant GPR fit data for the mass, spin, and recoil kick for generically precessing BBHs.
VectorFitData * vf_data
Fit data for recoil kick.
UINT4 setup
Indicates if NRSurRemnant has been initialized.
ScalarFitData * mf_data
Fit data for final mass.
UINT4 params_dim
Dimensions of the model.
VectorFitData * chif_data
Fit data for final spin.
gsl_matrix * x_train
Training set parameters, needed for GPR fits.
Data used in a single vector fit NOTE: basisFunctionOrders, coefs, componentIndices,...
FitData ** fit_data
Vector of FitData.
int vec_dim
Dimension of the vector.