OpenBEM
Open-source framework for electromagnetic simulation with the boundary element method.
Loading...
Searching...
No Matches
adaptive.cpp
Go to the documentation of this file.
1// OpenBEM - Copyright (C) 2026 Shashwat Sharma
2
3// This file is part of OpenBEM.
4
5// OpenBEM is free software: you can redistribute it and/or modify it under the terms of the
6// GNU General Public License as published by the Free Software Foundation, either version 3
7// of the License, or (at your option) any later version.
8
9// You should have received a copy of the GNU General Public License along with OpenBEM.
10// If not, see <https://www.gnu.org/licenses/>.
11
12
19
20#include <functional>
21
22#include "types.hpp"
23#include "constants.hpp"
27
28
29namespace bem
30{
31
32template <uint8_t dim>
34 const Triangle<dim>& tri,
36 )
37{
38 if (!eval)
39 throw std::invalid_argument(
40 "AdaptiveTriangleQuadrature::compute_points_weights(): invalid or missing eval."
41 );
42
43 base::points_.resize(dim, 0);
44 base::weights_.resize(1, 0);
45 vals_.resize(1, 0);
46 reset_recursion_count();
47
48 // The adaptive routine requires at least 11 function evals
49 // As a first step, try iterative Gaussian quadrature with four
50 // evals to see if that's already enough, to possibly save time.
52 iq.set_tol(tol_);
53 iq.set_max_iters(2);
54 iq.set_starting_order(1);
55 iq.compute_points_weights(tri, eval);
56 if (iq.converged())
57 {
58 base::points_ = iq.points();
59 base::weights_ = iq.weights();
60 base::points_weights_computed_ = true;
61 vals_ = eval(base::points_);
62 converged_ = true;
63 return;
64 }
65
66 int level = 0;
67 uint8_t longest_edge = tri.longest_edge_index();
68
69 get_5_points(p_main_, tri, longest_edge);
70 vals_main_ = eval(p_main_);
71
72 run_recursion(level, tri, eval, longest_edge);
73
74 if (level == max_levels_)
75 {
76 base::points_weights_computed_ = true;
77 converged_ = false;
78 return;
79 }
80
81 converged_iter_ = level;
82 base::points_weights_computed_ = true;
83 converged_ = true;
84
85};
86
87
88template <uint8_t dim>
90 int& level,
91 const Triangle<dim>& tri,
94 )
95{
96
97 level++;
98
100 get_subtris(v_subtri1, v_subtri2, tri, longest_edge);
101
103
104 // if (level == 2) std::cout << "level: " << level << "\n" << subtri1(0) << "\n--\n" << subtri1(1) << "\n--\n" << subtri1(2) << "\n==" << std::endl;
105 // if (level == 2) std::cout << "level: " << level << "\n" << subtri2(0) << "\n--\n" << subtri2(1) << "\n--\n" << subtri2(2) << "\n==" << std::endl;
106
107 uint8_t longest_edge1 = subtri1.longest_edge_index();
108 uint8_t longest_edge2 = subtri2.longest_edge_index();
109
111 get_5_points(p_sub1, subtri1, longest_edge1);
112 get_5_points(p_sub2, subtri2, longest_edge2);
113
114 // if (level == 2) std::cout << "level: " << level << "\n" << p_sub1[0] << "\n--\n" << p_sub1[1] << "\n--\n" << p_sub1[2] << "\n--\n" << p_sub1[3] << "\n--\n" << p_sub1[4] << "\n==" << std::endl;
115 // if (level == 2) std::cout << "level: " << level << "\n" << p_sub2[0] << "\n--\n" << p_sub2[1] << "\n--\n" << p_sub2[2] << "\n--\n" << p_sub2[3] << "\n--\n" << p_sub2[4] << "\n==" << std::endl;
116
117 p_sub_.col(0) = p_sub1.col(0);
118 p_sub_.col(1) = p_sub1.col(1);
119 p_sub_.col(2) = p_sub1.col(4);
120 p_sub_.col(3) = p_sub2.col(0);
121 p_sub_.col(4) = p_sub2.col(1);
122 p_sub_.col(5) = p_sub2.col(4);
123
124 // if (level == 1) std::cout << "level: " << level << "\n" << p_sub_[0] << "\n--\n" << p_sub_[1] << "\n--\n" << p_sub_[2] << "\n--\n" << p_sub_[3] << "\n--\n" << p_sub_[4] << "\n--\n" << p_sub_[5] << "\n==" << std::endl;
125
126 vals_sub_ = eval(p_sub_);
127
128 Float area1 = subtri1.area();
129 Float area2 = subtri2.area();
130 Float area = area1 + area2;
131
132 // 5-point integral result
133 Complex I5 = (vals_main_.sum() + vals_main_[4]) * area / (Float)6.0;
134
135 // 10-point integral result
136 Complex I10 = vals_sub_.sum() + vals_sub_[2] + vals_sub_[5];
137
138 if (longest_edge1 == 1)
139 {
140 idx_extra1_[0] = 4;
141 idx_extra1_[1] = 3;
142 }
143 else if (longest_edge1 == 2)
144 {
145 idx_extra1_[0] = 3;
146 idx_extra1_[1] = 0;
147 }
148 else if (longest_edge1 == 0)
149 {
150 idx_extra1_[0] = 0;
151 idx_extra1_[1] = 4;
152 }
153 I10 += vals_main_[idx_extra1_[0]] + vals_main_[idx_extra1_[1]];
154
155 if (longest_edge2 == 1)
156 {
157 idx_extra2_[0] = 2;
158 idx_extra2_[1] = 4;
159 }
160 else if (longest_edge2 == 2)
161 {
162 idx_extra2_[0] = 4;
163 idx_extra2_[1] = 1;
164 }
165 else if (longest_edge2 == 0)
166 {
167 idx_extra2_[0] = 1;
168 idx_extra2_[1] = 2;
169 }
170 I10 += vals_main_[idx_extra2_[0]] + vals_main_[idx_extra2_[1]];
171 I10 *= area / 12.0;
172
173 // if (level == 2) std::cout << "level: " << level << ", " << (int)idx_extra1_[0] << ", " << (int)idx_extra1_[1] << "\n==" << std::endl;
174 // if (level == 2) std::cout << "level: " << level << ", " << (int)idx_extra2_[0] << ", " << (int)idx_extra2_[1] << "\n==" << std::endl;
175
176 bool converged = check_convergence(I5, I10);
177
178 // {
179 // std::cout << tri.v[0] << ", " << std::endl << tri.v[1] << ", " << std::endl << tri.v[2] << ", " << std::endl << "---" << std::endl;
180 // for (uint8_t ii = 0; ii < 5; ii++)
181 // std::cout << p[ii] << ", " << std::endl;
182 // std::cout << "---" << std::endl;
183 // for (uint8_t ii = 0; ii < 5; ii++)
184 // std::cout << p_sub1[ii] << ", " << std::endl;
185 // std::cout << "---" << std::endl;
186 // for (uint8_t ii = 0; ii < 5; ii++)
187 // std::cout << p_sub2[ii] << ", " << std::endl;
188 // std::cout << "------" << std::endl;
189 // std::cout << idx_extra1_[0] << ", " << idx_extra1_[1] << ", " << idx_extra2_[0] << ", " << idx_extra2_[1] << std::endl;
190 // std::cout << "------------" << std::endl;
191 // }
192
193 if (converged || level == max_levels_)
194 {
195
196 const uint16_t num_add = 10;
197 const uint16_t idx_add = base::points_.cols();
198
199 base::points_.conservativeResize(dim, base::points_.cols() + num_add);
200 base::weights_.conservativeResize(1, base::weights_.size() + num_add);
201 vals_.conservativeResize(1, vals_.size() + num_add);
202
203 for (uint8_t ii = 0; ii < 2; ii++)
204 {
205 base::points_.col(idx_add + 2 * ii) = p_main_.col(idx_extra1_[ii]);
206 base::points_.col(idx_add + 2 * ii + 1) = p_main_.col(idx_extra2_[ii]);
207
208 vals_[idx_add + 2 * ii] = vals_main_[idx_extra1_[ii]];
209 vals_[idx_add + 2 * ii + 1] = vals_main_[idx_extra2_[ii]];
210
211 // if (level == 2) std::cout << "level: " << level << "\n" << points_[points_.cols()-2] << "\n--\n" << points_[points_.cols()-1] << "\n==" << std::endl;
212 }
213
214 base::points_.rightCols(p_sub_.cols()) = p_sub_;
215 base::weights_.rightCols(10) = weights10_ * area / (Float)12.0;
216 vals_.rightCols(vals_sub_.size()) = vals_sub_;
217
218 total_recursions_++;
219
220 return I10;
221
222 }
223
224 // ------ Subtriangle 1 ------
225
226 Complex I3_subtri1 = (vals_main_[0] + vals_main_[3] + vals_main_[4]) * area1 / (Float)3.0;
227
228 vals_subtri1_ <<
229 vals_sub_[0],
230 vals_sub_[1],
231 vals_main_[idx_extra1_[0]],
232 vals_main_[idx_extra1_[1]],
233 vals_sub_[2];
234
235 Complex I5_subtri1 = (vals_subtri1_.sum() + vals_subtri1_[4]) * area1 / (Float)6.0;
236
237 // ------ Subtriangle 2 ------
238
239 Complex I3_subtri2 = (vals_main_[1] + vals_main_[2] + vals_main_[4]) * area2 / (Float)3.0;
240
241 vals_subtri2_ <<
242 vals_sub_[3],
243 vals_sub_[4],
244 vals_main_[idx_extra2_[0]],
245 vals_main_[idx_extra2_[1]],
246 vals_sub_[5];
247
248 Complex I5_subtri2 = (vals_subtri2_.sum() + vals_subtri2_[4]) * area2 / (Float)6.0;
249
250 // ------ Subtriangle recursion ------
251
253 bool converged_subtri1 = check_convergence(I3_subtri1, I5_subtri1);
255 {
256 p_main_ = p_sub1;
257 vals_main_ = vals_subtri1_;
258 I10_subtri1 = run_recursion(level, subtri1, eval, longest_edge1);
259 level--;
260 }
261 else
262 {
263 const uint16_t num_add = 5;
264
265 base::points_.conservativeResize(dim, base::points_.cols() + num_add);
266 base::points_.rightCols(num_add) = p_sub1;
267
268 base::weights_.conservativeResize(1, base::weights_.size() + num_add);
269 base::weights_.rightCols(num_add) = weights5_ * area1 / (Float)6.0;
270
271 vals_.conservativeResize(1, vals_.size() + num_add);
272 vals_.rightCols(num_add) = vals_subtri1_;
273
275 }
276
278 bool converged_subtri2 = check_convergence(I3_subtri2, I5_subtri2);
280 {
281 p_main_ = p_sub2;
282 vals_main_ = vals_subtri2_;
283 I10_subtri2 = run_recursion(level, subtri2, eval, longest_edge2);
284 level--;
285 }
286 else
287 {
288 const uint16_t num_add = 5;
289
290 base::points_.conservativeResize(dim, base::points_.cols() + num_add);
291 base::points_.rightCols(num_add) = p_sub2;
292
293 base::weights_.conservativeResize(1, base::weights_.size() + num_add);
294 base::weights_.rightCols(num_add) = weights5_ * area2 / (Float)6.0;
295
296 vals_.conservativeResize(1, vals_.size() + num_add);
297 vals_.rightCols(num_add) = vals_subtri2_;
298
300 }
301
302 return I10_subtri1 + I10_subtri2;
303
304}
305
306template <uint8_t dim>
307void AdaptiveTriangleQuadrature<dim>::get_5_points(
309 const Triangle<dim>& tri,
311 )
312{
313
314 const EigColVecN<Float, dim>& v0 = tri.v((longest_edge + 2) % 3);
316 const EigColVecN<Float, dim>& v2 = tri.v((longest_edge + 1) % 3);
317
318 const EigColVecN<Float, dim> vm = (v1 + v2) / two;
319
320 p.col(0) = (v1 + vm) / two;
321 p.col(1) = (v2 + vm) / two;
322 p.col(2) = (v0 + v2) / two;
323 p.col(3) = (v0 + v1) / two;
324 p.col(4) = (v0 + vm) / two;
325
326 return;
327
328}
329
330
331template <uint8_t dim>
332void AdaptiveTriangleQuadrature<dim>::get_subtris(
335 const Triangle<dim>& tri,
337 )
338{
339 const EigColVecN<Float, dim>& v0 = tri.v((longest_edge + 2) % 3);
341 const EigColVecN<Float, dim>& v2 = tri.v((longest_edge + 1) % 3);
342
343 const EigColVecN<Float, dim> vm = (v1 + v2) / two;
344
345 v_subtri1.col(0) = v0;
346 v_subtri1.col(1) = v1;
347 v_subtri1.col(2) = vm;
348
349 v_subtri2.col(0) = v0;
350 v_subtri2.col(1) = vm;
351 v_subtri2.col(2) = v2;
352
353 return;
354};
355
356
357template class AdaptiveTriangleQuadrature<2>;
358template class AdaptiveTriangleQuadrature<3>;
359
360}
Class for adaptive quadrature over a triangle. Reference: O. Ergul, L. Gurel, "The Multilevel Fast Mu...
Definition adaptive.hpp:50
void compute_points_weights(const Triangle< dim > &tri, std::function< EigRowVec< Complex >(ConstEigRef< EigMatNX< Float, dim > >)> eval={}) override
Computes and stores the points on which to evaluate the integrand, and the corresponding weights.
Definition adaptive.cpp:33
const Eigen::Ref< const EigObj > ConstEigRef
Read-only reference to an Eigen object.
Definition types.hpp:98
double Float
Floating point number.
Definition types.hpp:47
std::complex< Float > Complex
Complex floating point number.
Definition types.hpp:51
Eigen::Matrix< T, N, 1 > EigColVecN
Fixed-size column vector of size N containing type T.
Definition types.hpp:86
Primary namespace for the OpenBEM library.
Definition constants.hpp:31