Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALInference 4.1.9.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
distance_integrator.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2013-2017 Leo Singer
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#include <stdlib.h>
20#include "bayestar_cosmology.h"
21#include "omp_interruptible.h"
22
23#include <lal/LALError.h>
24#include <lal/LALMalloc.h>
25
26#include <gsl/gsl_integration.h>
27#include <gsl/gsl_interp.h>
28#include <gsl/gsl_sf_bessel.h>
29#include <gsl/gsl_sf_exp.h>
30#include <gsl/gsl_errno.h>
31
32
33#include <lal/distance_integrator.h>
34
35#ifndef _OPENMP
36#define omp ignore
37#endif
38
39
40
41
42
43/* Uniform-in-comoving volume prior for the WMAP9 cosmology.
44 * This is implemented as a cubic spline interpolant.
45 *
46 * The following static variables are defined in bayestar_cosmology.h, which
47 * is automatically generated by bayestar_cosmology.py:
48 * - dVC_dVL_data
49 * - dVC_dVL_tmin
50 * - dVC_dVL_dt
51 * - dVC_dVL_high_z_slope
52 * - dVC_dVL_high_z_intercept
53 */
54static gsl_spline *dVC_dVL_interp = NULL;
55
56double log_dVC_dVL(double DL)
57{
58 const double log_DL = log(DL);
59 if (log_DL <= dVC_dVL_tmin)
60 {
61 return 0.0;
62 } else if (log_DL >= dVC_dVL_tmax) {
64 } else {
65 return gsl_spline_eval(dVC_dVL_interp, log_DL, NULL);
66 }
67}
68
69void dVC_dVL_init(void)
70{
71 const size_t len = sizeof(dVC_dVL_data) / sizeof(*dVC_dVL_data);
72 dVC_dVL_interp = gsl_spline_alloc(gsl_interp_cspline, len);
74 double x[len];
75 for (size_t i = 0; i < len; i ++)
77 int ret = gsl_spline_init(dVC_dVL_interp, x, dVC_dVL_data, len);
78 (void) ret;
79 XLAL_CHECK_ABORT(ret == GSL_SUCCESS);
80}
81
82static double radial_integrand(double r, void *params)
83{
85 const double scale = integrand_params->scale;
86 const double p = integrand_params->p;
87 const double b = integrand_params->b;
88 const int k = integrand_params->k;
89 const int gaussian_flag = integrand_params->gaussian;
90 gsl_error_handler_t *old_handler = gsl_set_error_handler_off();
91 double ret = scale - gsl_pow_2(p / r - 0.5 * b / p);
92 int retval=GSL_SUCCESS;
93 gsl_sf_result result;
94 if (integrand_params->cosmology)
95 ret += log_dVC_dVL(r);
96 if (!gaussian_flag)
97 {
98 retval = gsl_sf_exp_mult_e(
99 ret, gsl_sf_bessel_I0_scaled(b / r) * gsl_pow_int(r, k),
100 &result);
101 }
102 else
103 {
104 retval = gsl_sf_exp_mult_e(
105 ret, gsl_pow_int(r, k),
106 &result);
107 }
108 gsl_set_error_handler(old_handler);
109 switch (retval)
110 {
111 case GSL_SUCCESS:
112 return(result.val);
113 break;
114 case GSL_EUNDRFLW:
115 return(0); /* Underflow */
116 break;
117 default:
118 XLAL_ERROR(XLAL_EFUNC,"GSL error %s",gsl_strerror(retval));
119 }
120}
121
122
123double log_radial_integrand(double r, void *params)
124{
126 const double scale = integrand_params->scale;
127 const double p = integrand_params->p;
128 const double b = integrand_params->b;
129 const int k = integrand_params->k;
130 const int gaussian_flag = integrand_params->gaussian;
131 double ret = 0;
132 if (!gaussian_flag)
133 ret = log(gsl_sf_bessel_I0_scaled(b / r) * gsl_pow_int(r, k))
134 + scale - gsl_pow_2(p / r - 0.5 * b / p);
135 else
136 ret = ((double) k)*log(r) + scale - gsl_pow_2(p / r - 0.5 * b / p);
137 if (integrand_params->cosmology)
138 ret += log_dVC_dVL(r);
139 return ret;
140}
141
142
143double log_radial_integral(double r1, double r2, double p, double b, int k,
144 int cosmology, int gaussian)
145{
146 radial_integrand_params params = {0, p, b, k, cosmology, gaussian};
147 double breakpoints[5];
148 unsigned char nbreakpoints = 0;
149 double result = 0, abserr, log_offset = -INFINITY;
150 int ret;
151
152 if (b != 0) {
153 /* Calculate the approximate distance at which the integrand attains a
154 * maximum (middle) and a fraction eta of the maximum (left and right).
155 * This neglects the scaled Bessel function factors and the power-law
156 * distance prior. It assumes that the likelihood is approximately of
157 * the form
158 *
159 * -p^2/r^2 + B/r.
160 *
161 * Then the middle breakpoint occurs at 1/r = -B/2A, and the left and
162 * right breakpoints occur when
163 *
164 * A/r^2 + B/r = log(eta) - B^2/4A.
165 */
166
167 static const double eta = 0.01;
168 const double middle = 2 * gsl_pow_2(p) / b;
169 const double left = 1 / (1 / middle + sqrt(-log(eta)) / p);
170 const double right = 1 / (1 / middle - sqrt(-log(eta)) / p);
171
172 /* Use whichever of the middle, left, and right points lie within the
173 * integration limits as initial subdivisions for the adaptive
174 * integrator. */
175
176 breakpoints[nbreakpoints++] = r1;
177 if(left > breakpoints[nbreakpoints-1] && left < r2)
178 breakpoints[nbreakpoints++] = left;
179 if(middle > breakpoints[nbreakpoints-1] && middle < r2)
180 breakpoints[nbreakpoints++] = middle;
181 if(right > breakpoints[nbreakpoints-1] && right < r2)
182 breakpoints[nbreakpoints++] = right;
183 breakpoints[nbreakpoints++] = r2;
184 } else {
185 /* Inner breakpoints are undefined because b = 0. */
186 breakpoints[nbreakpoints++] = r1;
187 breakpoints[nbreakpoints++] = r2;
188 }
189
190 /* Re-scale the integrand so that the maximum value at any of the
191 * breakpoints is 1. Note that the initial value of the constant term
192 * is overwritten. */
193
194 for (unsigned char i = 0; i < nbreakpoints; i++)
195 {
196 double new_log_offset = log_radial_integrand(breakpoints[i], &params);
197 if (new_log_offset > log_offset)
198 log_offset = new_log_offset;
199 }
200
201 /* If the largest value of the log integrand was -INFINITY, then the
202 * integrand is 0 everywhere. Set log_offset to 0, because subtracting
203 * -INFINITY would make the integrand infinite. */
204 if (log_offset == -INFINITY)
205 log_offset = 0;
206
207 params.scale = -log_offset;
208 size_t n = 64;
209 double abstol = 1e-8;
210 double reltol = 1e-8;
211 int relaxed=0;
212 do {
213 /* Maximum number of subdivisions for adaptive integration. */
214
215
216 /* Allocate workspace on stack. Hopefully, a little bit faster than
217 * using the heap in multi-threaded code. */
218
219 double alist[n];
220 double blist[n];
221 double rlist[n];
222 double elist[n];
223 size_t order[n];
224 size_t level[n];
225 gsl_integration_workspace workspace = {
226 .alist = alist,
227 .blist = blist,
228 .rlist = rlist,
229 .elist = elist,
230 .order = order,
231 .level = level,
232 .limit = n
233 };
234
235 /* Set up integrand data structure. */
236 const gsl_function func = {radial_integrand, &params};
237 //gsl_error_handler_t *old_handler = gsl_set_error_handler_off();
238
239 /* Perform adaptive Gaussian quadrature. */
240 ret = gsl_integration_qagp(&func, breakpoints, nbreakpoints,
241 abstol, reltol, n, &workspace, &result, &abserr);
242 switch(ret)
243 {
244 case GSL_EROUND:
245 relaxed=1;
246 abstol*=2;
247 break;
248 case GSL_EMAXITER:
249 fprintf(stderr,"GSL error %s, increasing n to %li\n",gsl_strerror(ret),n*=2);
250 break;
251 case GSL_SUCCESS:
252 break;
253 default:
254 fprintf(stderr,"%s unable to handle GSL error: %s\n",__func__,gsl_strerror(ret));
255 exit(1);
256 break;
257 }
258 }
259 while(ret!=GSL_SUCCESS);
260 if(relaxed) fprintf(stderr,"Warning: had to relax absolute tolerance to %le during distance integration for r1=%lf, r2=%lf, p=%lf, b=%lf, k=%i\n",
261 abstol,r1,r2,p,b,k);
262 /* Done! */
263 return log_offset + log(result);
264}
265
266
267log_radial_integrator *log_radial_integrator_init(double r1, double r2, int k, int cosmology,
268 double pmax, size_t size, int gaussian)
269{
270 log_radial_integrator *integrator = NULL;
271 bicubic_interp *region0 = NULL;
272 cubic_interp *region1 = NULL, *region2 = NULL;
273 const double alpha = 4;
274 const double p0 = 0.5 * (k >= 0 ? r2 : r1);
275 const double xmax = log(pmax);
276 const double x0 = GSL_MIN_DBL(log(p0), xmax);
277 const double xmin = x0 - (1 + M_SQRT2) * alpha;
278 const double ymax = x0 + alpha;
279 const double ymin = 2 * x0 - M_SQRT2 * alpha - xmax;
280 const double d = (xmax - xmin) / (size - 1); /* dx = dy = du */
281 const double umin = - (1 + M_SQRT1_2) * alpha;
282 const double vmax = x0 - M_SQRT1_2 * alpha;
283
284 double *z1=calloc(size,sizeof(*z1));
285 double *z2=calloc(size,sizeof(*z2));
286 double *z0=calloc(size*size,sizeof(*z0));
287 XLAL_CHECK_ABORT(z0 && z1 && z2);
288 /*
289 for (size_t i=0;i<size;i++)
290 {
291 z0[i]=calloc(size,sizeof(*z0[i]));
292 XLAL_CHECK_ABORT(z0[i]);
293 }
294 */
295 /* const double umax = xmax - vmax; */ /* unused */
296
297 if(cosmology) dVC_dVL_init();
298
299 int interrupted=0;
301 integrator = malloc(sizeof(*integrator));
302 /* Temporarily turn off gsl_error handler which isn't thread safe. */
303 gsl_error_handler_t *old_handler = gsl_set_error_handler_off();
304
305 #pragma omp parallel for
306 for (size_t i = 0; i < size * size; i ++)
307 {
308
311
312 const size_t ix = i / size;
313 const size_t iy = i % size;
314 const double x = xmin + ix * d;
315 const double y = ymin + iy * d;
316 const double p = exp(x);
317 const double r0 = exp(y);
318 const double b = 2 * gsl_pow_2(p) / r0;
319 /* Note: using this where p > r0; could reduce evaluations by half */
320 z0[ix*size + iy] = log_radial_integral(r1, r2, p, b, k, cosmology, gaussian);
321 }
322 gsl_set_error_handler(old_handler);
323
325 goto done;
326
327 region0 = bicubic_interp_init(z0, size, size, xmin, ymin, d, d);
328
329 for (size_t i = 0; i < size; i ++)
330 z1[i] = z0[i*size + (size - 1)];
331 region1 = cubic_interp_init(z1, size, xmin, d);
332
333 for (size_t i = 0; i < size; i ++)
334 z2[i] = z0[i*size + (size - 1 - i)];
335 region2 = cubic_interp_init(z2, size, umin, d);
336
337done:
338 interrupted = OMP_WAS_INTERRUPTED;
340
341 free(z2); free(z1); free(z0);
342 if (interrupted || !(integrator && region0 && region1 && region2))
343 {
344 free(integrator);
345 free(region0);
346 free(region1);
347 free(region2);
348 XLAL_ERROR_NULL(XLAL_ENOMEM, "not enough memory to allocate integrator");
349 }
350
351 integrator->region0 = region0;
352 integrator->region1 = region1;
353 integrator->region2 = region2;
354 integrator->xmax = xmax;
355 integrator->ymax = ymax;
356 integrator->vmax = vmax;
357 integrator->r1 = r1;
358 integrator->r2 = r2;
359 integrator->k = k;
360 return integrator;
361}
362
363
365{
366 if (integrator)
367 {
368 bicubic_interp_free(integrator->region0);
369 integrator->region0 = NULL;
370 cubic_interp_free(integrator->region1);
371 integrator->region1 = NULL;
372 cubic_interp_free(integrator->region2);
373 integrator->region2 = NULL;
374 }
375 free(integrator);
376}
377
378
379double log_radial_integrator_eval(const log_radial_integrator *integrator, double p, double b, double log_p, double log_b)
380{
381 const double x = log_p;
382 const double y = M_LN2 + 2 * log_p - log_b;
383 double result;
384 XLAL_CHECK_ABORT(x <= integrator->xmax);
385
386 if (p == 0) {
387 /* note: p2 == 0 implies b == 0 */
388 XLAL_CHECK_ABORT(b == 0);
389 int k1 = integrator->k + 1;
390
391 if (k1 == 0)
392 {
393 result = log(log(integrator->r2 / integrator->r1));
394 } else {
395 result = log((gsl_pow_int(integrator->r2, k1) - gsl_pow_int(integrator->r1, k1)) / k1);
396 }
397 } else {
398 if (y >= integrator->ymax) {
399 result = cubic_interp_eval(integrator->region1, x);
400 } else {
401 const double v = 0.5 * (x + y);
402 if (v <= integrator->vmax)
403 {
404 const double u = 0.5 * (x - y);
405 result = cubic_interp_eval(integrator->region2, u);
406 } else {
407 result = bicubic_interp_eval(integrator->region0, x, y);
408 }
409 }
410 result += gsl_pow_2(0.5 * b / p);
411 }
412
413 return result;
414}
int k
#define fprintf
static const double dVC_dVL_high_z_slope
static const double dVC_dVL_dt
static const double dVC_dVL_tmin
static const double dVC_dVL_high_z_intercept
static const double dVC_dVL_tmax
static const double dVC_dVL_data[]
double e
bicubic_interp * bicubic_interp_init(const double *data, int ns, int nt, double smin, double tmin, double ds, double dt)
Definition: cubic_interp.c:136
void cubic_interp_free(cubic_interp *interp)
Definition: cubic_interp.c:120
void bicubic_interp_free(bicubic_interp *interp)
Definition: cubic_interp.c:187
double bicubic_interp_eval(const bicubic_interp *interp, double s, double t)
Definition: cubic_interp.c:193
cubic_interp * cubic_interp_init(const double *data, int n, double tmin, double dt)
Definition: cubic_interp.c:95
double cubic_interp_eval(const cubic_interp *interp, double t)
Definition: cubic_interp.c:126
double log_radial_integrator_eval(const log_radial_integrator *integrator, double p, double b, double log_p, double log_b)
Evaluate the log distance integrator for given SNRs.
double log_radial_integrand(double r, void *params)
void dVC_dVL_init(void)
void log_radial_integrator_free(log_radial_integrator *integrator)
Free an integrator.
static gsl_spline * dVC_dVL_interp
log_radial_integrator * log_radial_integrator_init(double r1, double r2, int k, int cosmology, double pmax, size_t size, int gaussian)
Distance integrator for marginalisation.
double log_radial_integral(double r1, double r2, double p, double b, int k, int cosmology, int gaussian)
double log_dVC_dVL(double DL)
static double radial_integrand(double r, void *params)
const double u
const double r2
coeffs k1
static const INT4 r
#define XLAL_ERROR_NULL(...)
#define XLAL_ERROR(...)
#define XLAL_CHECK_ABORT(assertion)
XLAL_ENOMEM
XLAL_EFUNC
list y
#define OMP_EXIT_LOOP_EARLY
#define OMP_BEGIN_INTERRUPTIBLE
#define OMP_END_INTERRUPTIBLE
#define OMP_WAS_INTERRUPTED
double alpha
bicubic_interp * region0