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