LoAdSG
integral.hpp
1
9
10#ifndef INTEGRAL_HPP
11#define INTEGRAL_HPP
12
13#include <functional>
14#include <iostream>
15#include <limits>
16
17#include "util.hpp"
18
43template <typename T, typename F, size_t N>
44 T integral_aux_boundary( const F& f, const std::array<T, Util::pow(3, N)>& f_evaluated, std::array<T, N>& x1, std::array<T, N> x2, const T& volume, const T& epsilon, std::array<size_t, N>& level, std::array<size_t, N>& index, size_t dimension, bool& refine, size_t depth, size_t depth_min, size_t depth_max, const T& gamma )
45{
46
47
51 auto perform_refinement = [&] () -> T
52 {
53
54 // Do not refine when maximum depth is reached or base function is a boundary for the dimension
55 if( (depth >= depth_max) || (level[dimension] == 0) )
56 {
57 return (T) 0.0;
58 }
59
60 // Calculate stride for traversing f_evaluated
61 size_t stride = 1;
62 for( size_t i = 0; i < dimension; ++i )
63 {
64 stride *= 3;
65 }
66
67 // Create data structures to evaluate and store function values for the next hierarchical level
68 std::array<T, Util::pow(3, N)> f_evaluated_l;
69 std::array<T, Util::pow(3, N)> f_evaluated_r;
70
71 size_t refinement_index = 0;
72 std::array<T, N> position;
73 size_t threeN = Util::pow<size_t>(3, N);
74
75 // Traverse the N-dimensional rectilinear domain distributing f_evaluated along the new arrays, evaluating f when necessary
76 for( size_t i = 0; i < threeN; ++i )
77 {
78
79 // Variable for determining combinations
80 size_t tmp = i;
81
82 // Iterate over the dimensions setting position to the correct value
83 for( size_t k = 0; k < N; ++k )
84 {
85
86 // Skip refinement dimension
87 if( k == dimension )
88 {
89 refinement_index = (tmp%3);
90 tmp /= 3;
91 continue;
92 }
93
94 switch( (tmp%3) )
95 {
96 // Left
97 case 0:
98 position[k] = x1[k];
99 break;
100
101 // Center
102 case 1:
103 position[k] = (x1[k] + x2[k]) * (T) 0.5;
104 break;
105
106 // Right
107 case 2:
108 position[k] = x2[k];
109 break;
110
111 // This label is only for compiler compatibility, it cannot be reached
112 default:
113 break;
114 }
115
116 // Shift the second digit to the first place
117 tmp /= 3;
118
119 }
120
121 // Adjust position in refinement dimension
122 switch( refinement_index )
123 {
124
125 // Left
126 case 0:
127 f_evaluated_l[i] = f_evaluated[i];
128 f_evaluated_r[i] = f_evaluated[i + stride];
129 break;
130
131 // Center
132 case 1:
133 // Interpolate position linearily
134 position[dimension] = x1[dimension] + (x2[dimension] - x1[dimension]) * (T) 0.25;
135 f_evaluated_l[i] = f(position);
136 // Interpolate position linearily
137 position[dimension] = x1[dimension] + (x2[dimension] - x1[dimension]) * (T) 0.75;
138 f_evaluated_r[i] = f(position);
139 break;
140
141 // Right
142 case 2:
143 f_evaluated_l[i] = f_evaluated[i - stride];
144 f_evaluated_r[i] = f_evaluated[i];
145 break;
146
147 default:
148 break;
149
150 }
151
152 }
153
154 // Correction achieved by the higher level basis functions
155 T correction = (T) 0.0;
156
157 // Temporary refinement flag
158 bool refine2 = false;
159
160 // Calculate the center of the corresponding dimension
161 T center = (x1[dimension] + x2[dimension]) * (T) 0.5;
162
163 // Increase level vector in the current component
164 ++level[dimension];
165
166 // Calculate the index of the left base function
167 index[dimension] *= 2;
168
169 // Trim the domain to the left half
170 T tmp = x2[dimension];
171 x2[dimension] = center;
172
173 // Perform correction using the left base function
174 correction += integral_aux_boundary( f, f_evaluated_l, x1, x2, volume*(T)0.5, epsilon, level, index, dimension, refine2, depth+1, depth_min, depth_max, gamma );
175
176 // Calculate the index of the right base function
177 ++index[dimension];
178
179 // Trim the domain to right half
180 x2[dimension] = tmp;
181 tmp = x1[dimension];
182 x1[dimension] = center;
183
184 // Perform correction using the right base function
185 correction += integral_aux_boundary( f, f_evaluated_r, x1, x2, volume*(T)0.5, epsilon, level, index, dimension, refine2, depth+1, depth_min, depth_max, gamma );
186
187 // Reset state
188 x1[dimension] = tmp;
189
190 // Restore old state for level and index
191 --index[dimension];
192 index[dimension] /= 2;
193
194 --level[dimension];
195
196 // Return correction
197 return correction;
198
199 };
200
201 // Perform the actual integration
202 if( dimension > 0 )
203 {
204
205 // Dimensions above zero will only call the next lower dimension and refine when needed
206 T local_integral = integral_aux_boundary( f, f_evaluated, x1, x2, volume, epsilon, level, index, dimension-1, refine, depth, depth_min, depth_max, gamma );
207
208 // If refinement was requests on dimension 0, we refine them all (octree refinement)...
209 if( refine )
210 {
211 // ...by incrementing the local integral by the correction from the higher level base functions
212 local_integral += perform_refinement();
213 }
214
215 // return the local integral
216 return local_integral;
217
218 }
219 else
220 {
221
222 // Dimension 0 is where the actual work takes place
223
224 // Accumulator for the hierarchical surplus
225 T w = (T) 0.0;
226
227 // Traverse the N-dimensional rectilinear domain at all combinations of corner and center points
228 for( size_t i = 0; i < Util::pow<size_t>(3, N); ++i )
229 {
230
231 // Variable for determining combinations
232 size_t tmp = i;
233
234 // Variable storing the weight of the function evaluation
235 T wcoeff = (T) 1.0;
236
237 // Flag to indicate if the combination stored in i should be used (Boundary dimensions are evaluated at one point, instead of three)
238 bool use = true;
239
240 // Iterate over the dimensions setting position to the right value and calculating wcoeff
241 for( size_t k = 0; k < N; ++k )
242 {
243
244 // If k is a boundary dimension and the corresponding index in i is not the index of the boundary function,
245 // we skip the combination
246 if( (level[k] == 0) && ( (tmp%3) != index[k] ) )
247 {
248 use = false;
249 break;
250 }
251
252 if( level[k] > 0 )
253 {
254 if( (tmp%3) != 1 )
255 {
256 wcoeff *= (T) -0.5;
257 }
258 }
259
260 // Shift the second digit to the first place
261 tmp /= 3;
262
263 }
264
265 // Skip combination if dimension k is a boundary and position would not be centered in k
266 if( !use )
267 {
268 continue;
269 }
270
271 // Increment the hierarchical surplus by the weighted function evaluation
272 w += wcoeff * f_evaluated[i];
273 }
274
275 // The base function's contribution is the product of two factors:
276 // 2^(-N) * volume: The integral over the unscaled base function
277 // w: The hierarchical surplus
278 //
279 T local_integral = (Util::pow<T>(0.5, N) * volume) * w;
280
281 // Refine when the hierarchical surplus exceeds epsilon
282 if( (depth < depth_min) || (Util::abs(w) > epsilon) )
283 {
284 local_integral += perform_refinement();
285 refine = true;
286 }
287 else
288 {
289 refine = false;
290 }
291
292 // Return local integral
293 return local_integral;
294
295 }
296
297 // Just for compiler compatibility
298 return (T) 0.0;
299
300}
301
326template <typename T, typename F, size_t N>
327 T integral_aux( const F& f, const std::array<T, Util::pow(3, N)>& f_evaluated, std::array<T, N>& x1, std::array<T, N>& x2, const T& volume, const T& epsilon, size_t dimension, bool& refine, size_t depth, size_t depth_min, size_t depth_max, const T& gamma )
328{
329
330
334 auto perform_refinement = [&] () -> T
335 {
336
337 // Do not refine when maximum depth is reached or base function is a boundary for the dimension
338 if( depth >= depth_max )
339 {
340 return (T) 0.0;
341 }
342
343 // Calculate stride for traversing f_evaluated
344 size_t stride = 1;
345 for( size_t i = 0; i < dimension; ++i )
346 {
347 stride *= 3;
348 }
349
350 // Create data structures to evaluate and store function values for the next hierarchical level
351 std::array<T, Util::pow(3, N)> f_evaluated_l;
352 std::array<T, Util::pow(3, N)> f_evaluated_r;
353
354 size_t refinement_index = 0;
355 std::array<T, N> position;
356
357 size_t endi = Util::pow<size_t>(3, N);
358 // Traverse the N-dimensional rectilinear domain distributing f_evaluated along the new arrays, evaluating f when necessary
359 for( size_t i = 0; i < endi; ++i )
360 {
361
362 // Variable for determining combinations
363 size_t tmp = i;
364
365 // Iterate over the dimensions setting position to the right value and calculating wcoeff
366 for( size_t k = 0; k < N; ++k )
367 {
368
369 if( k == dimension )
370 {
371 refinement_index = (tmp%3);
372 tmp /= 3;
373 continue;
374 }
375
376 switch( (tmp%3) )
377 {
378 // Left
379 case 0:
380 position[k] = x1[k];
381 break;
382
383 // Center
384 case 1:
385 position[k] = (x1[k] + x2[k]) * (T) 0.5;
386 break;
387
388 // Right
389 case 2:
390 position[k] = x2[k];
391 break;
392
393 // This label is only for compiler compatibility, it cannot be reached
394 default:
395 break;
396 }
397
398 // Shift the second digit to the first place
399 tmp /= 3;
400
401 }
402
403 // Adjust position in refinement dimension
404 switch( refinement_index )
405 {
406
407 // Left
408 case 0:
409 f_evaluated_l[i] = f_evaluated[i];
410 f_evaluated_r[i] = f_evaluated[i + stride];
411 break;
412
413 // Center
414 case 1:
415 // Interpolate position linearily
416 position[dimension] = x1[dimension] + (x2[dimension] - x1[dimension]) * (T) 0.25;
417 f_evaluated_l[i] = f(position);
418 // Interpolate position linearily
419 position[dimension] = x1[dimension] + (x2[dimension] - x1[dimension]) * (T) 0.75;
420 f_evaluated_r[i] = f(position);
421 break;
422
423 // Right
424 case 2:
425 f_evaluated_l[i] = f_evaluated[i - stride];
426 f_evaluated_r[i] = f_evaluated[i];
427 break;
428
429 default:
430 break;
431
432 }
433
434 }
435
436 // Correction achieved by the higher level basis functions
437 T correction = (T) 0.0;
438
439 // Temporary refinement flag
440 bool refine2 = false;
441
442 // Calculate the center of the corresponding dimension
443 T center = (x1[dimension] + x2[dimension]) * (T) 0.5;
444
445 // Trim the domain to the left half
446 T tmp = x2[dimension];
447 x2[dimension] = center;
448
449 // Perform correction using the left base function
450 correction += integral_aux( f, f_evaluated_l, x1, x2, volume*(T)0.5, epsilon, dimension, refine2, depth+1, depth_min, depth_max, gamma );
451
452 // Trim the domain to right half
453 x2[dimension] = tmp;
454 tmp = x1[dimension];
455 x1[dimension] = center;
456
457 // Perform correction using the right base function
458 correction += integral_aux( f, f_evaluated_r, x1, x2, volume*(T)0.5, epsilon, dimension, refine2, depth+1, depth_min, depth_max, gamma );
459 x1[dimension] = tmp;
460
461 // Return correction
462 return correction;
463
464 };
465
466 // Perform the actual integration
467 if( dimension > 0 )
468 {
469
470 // Dimensions above zero will only call the next lower dimension and refine when needed
471 T local_integral = integral_aux( f, f_evaluated, x1, x2, volume, epsilon, dimension-1, refine, depth, depth_min, depth_max, gamma );
472
473 // If refinement was requests on dimension 0, we refine them all (octree refinement)...
474 if( refine )
475 {
476 // ...by incrementing the local integral by the correction from the higher level base functions
477 local_integral += perform_refinement();
478 }
479
480 // return the local integral
481 return local_integral;
482
483 }
484 else
485 {
486
487 // Dimension 0 is where the actual work takes place
488
489 // Accumulator for the hierarchical surplus
490 T w = (T) 0.0;
491
492 // Traverse the N-dimensional rectilinear domain at all combinations of corner and center points
493 for( size_t i = 0; i < Util::pow<size_t>(3, N); ++i )
494 {
495
496 // Variable for determining combinations
497 size_t tmp = i;
498
499 // Variable storing the weight of the function evaluation
500 T wcoeff = (T) 1.0;
501
502 // Iterate over the dimensions setting position to the right value and calculating wcoeff
503 for( size_t k = 0; k < N; ++k )
504 {
505
506 if( (tmp%3) != 1 )
507 {
508 wcoeff *= (T) -0.5;
509 }
510
511 // Shift the second digit to the first place
512 tmp /= 3;
513
514 }
515
516 // Increment the hierarchical surplus by the weighted function evaluation
517 w += wcoeff * f_evaluated[i];
518
519 }
520
521 // The base function's contribution is the product of two factors:
522 // 2^(-N) * volume: The integral over the unscaled base function
523 // w: The hierarchical surplus
524 //
525 T local_integral = (Util::pow<T>(0.5, N)) * volume * w;
526
527 // Refine when the hierarchical surplus exceeds epsilon
528 if( (depth < depth_min) || (Util::abs(w) > epsilon) )
529 {
530 local_integral += perform_refinement();
531 refine = true;
532 }
533 else
534 {
535 refine = false;
536 }
537
538 // Return local integral
539 return local_integral;
540
541 }
542
543 // Just for compiler compatibility
544 return (T) 0.0;
545
546}
547
565template <typename T, typename F, size_t N>
566 T integral( const F& f, std::array<T, N>& x1, std::array<T, N> x2, const T& epsilon, size_t depth_min = 0, size_t depth_max = std::numeric_limits<size_t>::max(), const T& gamma = (T) 1.0e12 )
567{
568
569 // Accumulator for the integral
570 T integral = (T) 0.0;
571
572 // Calculate the domain's volume
573 T volume = (T) 1.0;
574
575 for( size_t i = 0; i < N; ++i )
576 {
577 volume *= (x2[i] - x1[i]);
578 }
579
580 // === Determine the portion of the outermost 2^N boundary points =
581 //
582 //
583 //
584 //
585 //
586 //
587 // ==
588
589 size_t array_size = Util::pow<size_t>(3, N);
590 T t = Util::pow<T>(0.5,N);
591 size_t twoN = Util::POW2(N);
592
593
594 // Array for storing point to evaluate f in
595 std::array<T, N> position;
596 std::array<T, Util::pow(3, N)> f_evaluated;
597
598 // Iterate over all 2^N combinations of edges
599 for( size_t i = 0; i < array_size; ++i )
600 {
601
602 bool boundary = true;
603
604 size_t tmp = i;
605
606 // Iterate over the dimensions
607 for( size_t k = 0; k < N; ++k )
608 {
609
610 // Adjust position to represent the corner point encoded by i
611 switch( tmp % 3 )
612 {
613
614 case 0:
615 position[k] = x1[k];
616 break;
617
618 case 1:
619 position[k] = (x1[k] + x2[k]) * (T) 0.5;
620 boundary = false;
621 break;
622
623 case 2:
624 position[k] = x2[k];
625 break;
626
627 default:
628 break;
629
630 }
631
632 tmp /= 3;
633
634 }
635
636
637 f_evaluated[i] = f(position);
638
639 if( boundary )
640 {
641 integral += volume * (t) * f_evaluated[i];
642 }
643
644 }
645
646 // === Adaptively calculate the portion of all other boundary points ===
647
648 // Refinement flag
649 bool refine = false;
650
651 // Allocate multi indices storing the level and index of the corresponding base functions
652 std::array<size_t, N> level;
653 std::array<size_t, N> index;
654
655
656
657 // Iterate over all 2^N combinations of corner points creating the levels of the base functions
658 for( size_t i = 0; i < twoN; ++i )
659 {
660
661 // Counter for the number of non-boundary dimensions
662 size_t ones = 0;
663
664 // Adjust level to represent the base function level encoded by i
665 for( size_t k = 0; k < N; ++k )
666 {
667 if( (i & (1<<k)) > 0 )
668 {
669 level[k] = 1;
670 ++ones;
671 }
672 else
673 {
674 level[k] = 0;
675 }
676 }
677
678 // Skip the outermost boundary base functions (0, 0, ..., 0) and the inner base functions (1, 1, ..., 1)
679 if( (ones == 0) || (ones == N) )
680 {
681 continue;
682 }
683
684 // Again iterate over all 2^N combinations of corner points, but this time creating the indices of the base functions
685 for( size_t j = 0; j < twoN; ++j )
686 {
687
688 // Flag indicating, whether to use the generated index or not
689 bool use = true;
690
691 // Again iterate over all dimensions
692 for( size_t k = 0; k < N; ++k )
693 {
694
695 // index[k] encodes the position in the domain
696 // 0: Left
697 // 1: Right
698 //
699 index[k] = (( j & (1<<k) ) >> k) * 2;
700
701 // Base functions (1) (2) do not exist, therefore skip them
702 if( (level[k] == 1) && (index[k] == 2) )
703 {
704 use = false;
705 break;
706 }
707
708 }
709
710 // Skip non-existant base functions
711 if( !use )
712 {
713 continue;
714 }
715
716 // Add the portion of the base function described by level and index to the integral accumulator
717
718
719 integral += integral_aux_boundary( f, f_evaluated, x1, x2, volume, epsilon, level, index, N-1, refine, 0, depth_min, depth_max, gamma );
720
721 }
722
723 }
724
725 // === Calculate the portion of the inner point base functions ===
726
727
728
729 integral += integral_aux( f, f_evaluated, x1, x2, volume, epsilon, N-1, refine, 0, depth_min, depth_max, gamma );
730
731 // === Return the approximated integral ===
732 return integral;
733
734}
735
736#endif /* INTEGRAL_HPP */