-
Notifications
You must be signed in to change notification settings - Fork 1
/
bkgd_intg.c
313 lines (241 loc) · 6.7 KB
/
bkgd_intg.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
#include <gsl/gsl_integration.h>
#include <stdio.h>
#include "bkgd_intg.h"
#include "bkgd_param.h"
#define INTG_SCAN_SZ 100
#define INTG_DIFF_THRESH 1e-6
#define INTG_MIN 1e-50
#define INTG_LOW 1e-6
#define INTG_HIGH 1e6
#define INTG_RANGE_HIGH 1
#define INTG_RANGE_MID 2
#define INTG_RANGE_LOW 3
#define INTG_RANGE_ZERO 4
typedef struct {
double a;
double b;
double a_y;
double b_y;
int type; /* HIGH, MID, LOW or ZERO */
} BkgdIntgRange;
/**
* frees provided list of integration ranges
*/
static void free_intg_ranges(GSList *ranges) {
GSList *cur;
cur = ranges;
while(cur != NULL) {
g_free(cur->data);
cur = g_slist_next(cur);
}
if(ranges != NULL) {
g_slist_free(ranges);
}
}
int intg_range_type(double val) {
if(val < INTG_MIN || isnan(val)) {
return INTG_RANGE_ZERO;
}
if(val < INTG_LOW) {
return INTG_RANGE_LOW;
}
if(val < INTG_HIGH) {
return INTG_RANGE_MID;
}
return INTG_RANGE_HIGH;
}
/**
* Breaks the range of integration down into sub-regions with high-,
* mid- and low- and "near zero" function values. This is so that
* numerical integration can be performed separately on each of the
* regions reducing the possibilty of round-off error
* occuring.
*
* Assumes that function is positive over entire range of
* integration.
*
* May not work well for especially tricky functions where there are
* oscillations between low to high function values in a short range.
*/
static GSList *find_intg_ranges(gsl_function *f, double a, double b,
void *param) {
BkgdIntgRange *r;
GSList *ranges;
double delta, x, y, mid, start, end, mid_y, start_y;
int new_type, mid_type;
ranges = NULL;
r = g_new(BkgdIntgRange,1);
r->a = a;
r->b = a;
/* start a range of integration at the start */
r->a_y = f->function(r->a, param);
r->type = intg_range_type(r->a_y);
delta = (b - a) / (double)INTG_SCAN_SZ;
/* look for areas where integration will run into problems
* and create separate regions for these
*/
x = a;
while(x < b) {
x = x + delta;
if(x > b) {
x = b;
}
y = f->function(x, param);
new_type = intg_range_type(y);
if(r->type == new_type) {
/* next test point is still of same type so extend current range */
r->b = x;
r->b_y = y;
} else {
/* search for transition point between ranges */
start = r->b;
start_y = r->b_y;
end = x;
mid = x;
mid_y = y;
while((end - start) > INTG_DIFF_THRESH) {
mid = (start+end)/2.0;
mid_y = f->function(mid, param);
mid_type = intg_range_type(mid_y);
if(mid_type == r->type) {
/* mid point is in range of current type */
start = mid;
start_y = mid_y;
} else {
/* mid point is still too far away */
new_type = mid_type;
end = mid;
}
}
/* end old range */
r->b = start;
r->b_y = start_y;
ranges = g_slist_append(ranges, r);
/* start new range */
r = g_new(BkgdIntgRange, 1);
r->a = start;
r->b = start;
r->a_y = start_y;
r->b_y = start_y;
r->type = new_type;
x = start;
}
}
/* add last valid range to list of ranges */
if(r != NULL) {
if(r->a == r->b) {
g_free(r);
} else {
r->b_y = f->function(r->b, param);
ranges = g_slist_append(ranges, r);
}
}
return ranges;
}
void bkgd_gsl_integration_wrapper(gsl_function *f, double a, double b,
gsl_integration_workspace *w,
void *param,
double *intg_ptr, double *err_ptr) {
GSList *ranges, *cur;
BkgdIntgRange *r;
double intg, err, intg_ttl, err_ttl;
/* find set of ranges over which numerical integration
* can be performed
*/
ranges = find_intg_ranges(f, a, b, param);
cur = ranges;
intg_ttl = 0.0;
err_ttl = 0.0;
while(cur != NULL) {
r = cur->data;
/* integrate numerically over range t=[a..b]*/
/* for debugging */
/* switch(r->type) { */
/* case(INTG_RANGE_ZERO): */
/* fprintf(stderr, " zero-valued range:"); */
/* break; */
/* case(INTG_RANGE_LOW): */
/* fprintf(stderr, " low-valued range:"); */
/* break; */
/* case(INTG_RANGE_MID): */
/* fprintf(stderr, " mid-valued range:"); */
/* break; */
/* case(INTG_RANGE_HIGH): */
/* fprintf(stderr, " high-valued range:"); */
/* break; */
/* default: */
/* g_error("unknown range type"); */
/* } */
/* fprintf(stderr, "[%g,%g], edge vals:[%g,%g]\n", */
/* r->a, r->b, r->a_y, r->b_y); */
/*******************/
if(r->type == INTG_RANGE_ZERO) {
/* skip ranges that are very near zero */
intg = 0.0;
} else {
gsl_integration_qags(f, r->a, r->b, BKGD_INTG_EPS_ABS,
BKGD_INTG_EPS_REL, BKGD_INTG_LIMIT,
w, &intg, &err);
}
/* fprintf(stderr," intg area=%g\n", intg);*/
intg_ttl += intg;
err_ttl += err;
cur = g_slist_next(cur);
}
free_intg_ranges(ranges);
/* fprintf(stderr, "done intg=%g\n", intg_ttl);*/
*intg_ptr = intg_ttl;
*err_ptr = err_ttl;
}
/**
* Numerically evaluates an integral for a cons BLOCK of length and
* distance r_len and r_dist (in morgans). A BkgdIntg argument must be
* provided that specifies the function to be integrated and the
* relevant parameters.
*
* This function is essentially a wrapper that is used to populate an
* interpolation table for rapid lookup of integrals.
*
*/
double bkgd_calc_blk_integral(double r_dist, double r_len, void *parm) {
BkgdIntg *bi = parm;
double intg, err;
bi->p->r_near = r_dist;
bi->p->r_far = r_dist + r_len;
/* fprintf(stderr, "r_dist=%.20f, r_len=%g\n", r_dist, r_len);*/
if(bi->p->t_dist) {
bkgd_gsl_integration_wrapper(&bi->f, bi->p->a, bi->p->b,
bi->w, bi->p, &intg, &err);
} else {
/* No integration needed since we are using point distribution,
* Just call integrand with t value.
*/
intg = bi->f.function(bi->p->t, bi->p);
}
return intg;
}
/**
* Numerically evaluates an integral for a cons SITE at distance
* r_dist (in morgans). A BkgdIntg argument must be provided that
* specifies the function to be integrated and the relevant
* parameters.
*
* This function is essentially a wrapper that is used to populate an
* interpolation table for rapid lookup of integrals.
*
*/
double bkgd_calc_site_integral(double r_dist, void *parm) {
BkgdIntg *bi = parm;
double intg, err;
bi->p->r_near = bi->p->r_far = r_dist;
if(bi->p->t_dist) {
bkgd_gsl_integration_wrapper(&bi->f, bi->p->a, bi->p->b,
bi->w, bi->p, &intg, &err);
} else {
/* No integration needed since we are using point distribution,
* Just call integrand with t value.
*/
intg = bi->f.function(bi->p->t, bi->p);
}
return intg;
}