OpenBEM
Open-source framework for electromagnetic simulation with the boundary element method.
Loading...
Searching...
No Matches
line.tpp
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
18#ifndef BEM_RWG_OPINT_SRC_LINE_I
19#define BEM_RWG_OPINT_SRC_LINE_I
20
21#include <cassert>
22
23#include "types.hpp"
24#include "constants.hpp"
28
29
30namespace bem::rwg
31{
32
33template <typename LineQuadratureType>
35 const Complex k,
36 const Triangle<2>& src_tri,
38 )
39{
40 bool dc = true;
41 if (std::abs(k) > 0)
42 dc = false;
43
44 const Complex jk = J * k;
45
50
51 EigMat<Float> points_r = EigMat<Float>::Zero(line_quad_.ref_points().cols(), r_obs.cols());
52 EigMat<Float> points_x = EigMat<Float>::Zero(line_quad_.ref_points().cols(), r_obs.cols());
53 EigMat<Float> weights_r = EigMat<Float>::Zero(line_quad_.ref_points().cols(), r_obs.cols());
54 EigMat<Float> weights_x = EigMat<Float>::Zero(line_quad_.ref_points().cols(), r_obs.cols());
55
60
67
70
71 EigMat<Float> rho = EigMat<Float>::Zero(line_quad_.ref_points().cols(), r_obs.cols());
72 EigMat<Float> points_rx = EigMat<Float>::Zero(line_quad_.ref_points().cols(), r_obs.cols());
73 EigMat<Complex> exp_jkrt = EigMat<Complex>::Zero(line_quad_.ref_points().cols(), r_obs.cols());
74 EigMat<Complex> exp_jkrx = EigMat<Complex>::Zero(line_quad_.ref_points().cols(), r_obs.cols());
75 EigMat<Complex> x_sq_exp = EigMat<Complex>::Zero(line_quad_.ref_points().cols(), r_obs.cols());
76
78 if (base::compute_g_terms_)
79 {
80 I_alpha_edge.resize(1, r_obs.cols());
81 I_beta_edge.resize(1, r_obs.cols());
82 }
83 if (base::compute_grad_g_terms_)
84 {
85 I_par_edge.resize(1, r_obs.cols());
86 I_perp_edge.resize(1, r_obs.cols());
87 }
88
89 // Get the projection of the obs point on the triangle's plane, along
90 // with information about where the projection point falls
93 src_tri.get_plane_projection(proj_r, proj_d, r_obs);
94
95 EigRowVec<Float> proj_d_sign = proj_d.array().sign();
96 proj_d = Eigen::abs(proj_d.array());
97
98 EigRowVec<Complex> exp_jkd = Eigen::exp(-jk * proj_d.array());
99
100 for (uint8_t idx_edge = 0; idx_edge < 3; ++idx_edge)
101 {
102 // Indices of the edge's vertices
104 uint8_t idx_plus = (idx_edge + 1) % 3;
105
107
108 // unit vector perpendicular to the edge pointing out of the triangle
109 EigColVecN<Float, 2> u_hat = { edge.unit_vec()[1], -edge.unit_vec()[0] };
110
111 // vectors from the projection point to to edge vertices
112 rproj_to_vminus = (-proj_r).colwise() + edge.v(0);
113 rproj_to_vplus = (-proj_r).colwise() + edge.v(1);
114
115 // perpendicular displacement and vector from the projected obs point to this edge
116 proj_h.noalias() = u_hat.transpose() * rproj_to_vminus;
117 h_vec.noalias() = u_hat * proj_h;
118
119 // local 1D integration limits for this edge
120 x_minus.noalias() = -edge.unit_vec().transpose() * rproj_to_vminus;
121 x_plus.noalias() = -edge.unit_vec().transpose() * rproj_to_vplus;
122
123 // directed angles between the perpendicular projection and vectors to edge vertices
126 );
129 );
130
131 assert(
132 (Eigen::abs(
133 (x_plus - x_minus).colwise().norm().array() - edge.length()
134 ) <= TRIANGLE_DEFAULT_TOL * edge.length()).all() &&
135 "SrcLineIntegrator::assemble_integrals(): x_plus and x_minus are incorrect"
136 );
137
138 // assert(
139 // (Eigen::abs(
140 // Eigen::abs((theta_plus - theta_minus).colwise().norm().array()) - (angle_between_vectors<2>(rproj_to_vminus, rproj_to_vplus)).array()
141 // ) <= TRIANGLE_DEFAULT_TOL * pi).all() &&
142 // "SrcLineIntegrator::assemble_integrals(): theta_plus and theta_minus are incorrect"
143 // );
144
145 for (std::size_t rr = 0; rr < r_obs.cols(); ++rr)
146 {
147 // Get linear quadrature points and weights
148 line_quad_.compute_points_weights(x_minus.middleCols(rr, 1), x_plus.middleCols(rr, 1));
149
150 // matrices of weights and local 1D coords of src points (rows) per obs points (cols)
151 weights_x.col(rr) = line_quad_.ref_weights().transpose() * edge.length();
152 points_x.col(rr) = line_quad_.points().transpose();
153 }
154
155 rho = Eigen::sqrt(
156 Eigen::pow(proj_h.array(), 2).replicate(points_x.rows(), 1) +
157 Eigen::pow(points_x.array(), 2)
158 );
159
160 // Use angular quadrature points and weights for the (near-)singular case
161 Float near_threshold = edge.length() * 1e-2;
162 Float edge_len_lambda = edge.length() * std::real(k) / two_pi;
163 bool use_angular = (rho.array() <= near_threshold).any() || edge_len_lambda >= half;
164
165 // Do not use angular points when an observation point lies along an edge
166 if ((proj_h.array().abs() < float_eps).any() && (proj_d.array().abs() < float_eps).any())
167 use_angular = false;
168
169 // Use simplified line integrals for non-singular cases
170 bool use_simplified = (rho.array() > edge.length()).all();
171use_simplified = false;
172
173 if (!use_angular)
174 {
175 weights_r = weights_x.array() *
176 Eigen::abs(proj_h.replicate(points_x.rows(), 1).array()) /
177 Eigen::pow(rho.array(), 2);
178 points_r = Eigen::sqrt(
179 Eigen::pow(proj_d.array(), 2).replicate(points_x.rows(), 1) +
180 Eigen::pow(rho.array(), 2)
181 );
183 }
184 else
185 {
186 for (std::size_t rr = 0; rr < r_obs.cols(); ++rr)
187 {
188 line_quad_.compute_points_weights(
189 theta_minus.middleCols(rr, 1), theta_plus.middleCols(rr, 1)
190 );
191
192 // matrices of weights and distances between src (rows) and obs (cols) points
193 weights_r.col(rr) = line_quad_.weights().transpose();
194 points_r.col(rr) = Eigen::sqrt(
195 std::pow(proj_d[rr], 2) +
196 Eigen::pow(proj_h[rr] / Eigen::cos(line_quad_.points().array()), 2)
197 ).transpose();
198 }
199
200 points_rx = Eigen::sqrt(
201 Eigen::pow(proj_d.array(), 2).replicate(points_x.rows(), 1) +
202 Eigen::pow(rho.array(), 2)
203 );
204 }
205
206 exp_jkrt = Eigen::exp(-jk * points_r.array());
207 if (!use_angular)
209 else
210 exp_jkrx = Eigen::exp(-jk * points_rx.array());
211
212 x_sq_exp = weights_x.array() * Eigen::pow(points_x.array(), 2) * exp_jkrx.array();
213
214 r_minus =
215 Eigen::sqrt(Eigen::pow(proj_d.array(), 2) +
216 rproj_to_vminus.colwise().squaredNorm().array());
217
218 r_plus =
219 Eigen::sqrt(Eigen::pow(proj_d.array(), 2) +
220 rproj_to_vplus.colwise().squaredNorm().array());
221
222 exp_jkr_minus = Eigen::exp(-jk * r_minus.array());
223 exp_jkr_plus = Eigen::exp(-jk * r_plus.array());
224
225 if (base::compute_g_terms_)
226 {
227 if (dc)
228 I_alpha_edge = -(
229 weights_r.array() * (
230 points_r.array() - proj_d.replicate(points_r.rows(), 1).array()
231 )).colwise().sum();
232
233 else
234 I_alpha_edge = (
235 weights_r.array() * (exp_jkrt.array() -
236 exp_jkd.replicate(points_r.rows(), 1).array())
237 ).colwise().sum() / jk;
238
239 I_alpha += (proj_h.array() < 0).select(-I_alpha_edge, I_alpha_edge);
240
242 {
243 if (!dc)
244 I_beta.noalias() -= u_hat * (
245 weights_x.array() * exp_jkrx.array()
246 ).colwise().sum().matrix() / jk;
247
248 else
249 I_beta.noalias() -= u_hat * (
250 weights_x.array() * points_rx.array()
251 ).colwise().sum().matrix();
252 }
253 else
254 {
255 I_beta_edge = (x_sq_exp.array() / points_rx.array()).colwise().sum();
257
258 if (!dc)
259 I_beta.noalias() += edge.unit_vec() * ((-proj_h.array() / jk) * (exp_jkr_plus - exp_jkr_minus).array()).matrix();
260
261 else
262 I_beta.noalias() += edge.unit_vec() * (proj_h.array() * (r_plus - r_minus).array()).matrix();
263 }
264 }
265
266 if (base::compute_grad_g_terms_)
267 {
268 I_par_edge = -(x_sq_exp.array() / Eigen::pow(points_rx.array(), 3) * (-one - jk * points_rx.array())).colwise().sum();
269 I_par.noalias() += u_hat * I_par_edge;
270 I_par.noalias() += edge.unit_vec() * (proj_h.array() * (exp_jkr_plus.array() / r_plus.array() - exp_jkr_minus.array() / r_minus.array())).matrix();
271
272 I_perp_edge = (
273 weights_r.array() * exp_jkrt.array() / points_r.array()
274 ).colwise().sum() * proj_d.array() - (weights_r.array() * exp_jkd.replicate(points_r.rows(), 1).array()).colwise().sum();
275 I_perp_edge.array() *= proj_d_sign.array();
276 I_perp += (proj_h.array() < 0).select(-I_perp_edge, I_perp_edge);
277 }
278 }
279
280 SrcResult result;
281
282 if (base::compute_g_terms_)
283 {
284 result.g = -I_alpha.array() / four_pi;
285 result.rs_g.resize(2, r_obs.cols());
286 result.rs_g.row(0) = (proj_r.row(0).array() * result.g.array() + I_beta.row(0).array() / four_pi);
287 result.rs_g.row(1) = (proj_r.row(1).array() * result.g.array() + I_beta.row(1).array() / four_pi);
288 }
289
290 if (base::compute_grad_g_terms_)
291 {
292 result.grad_g.resize(3, r_obs.cols());
293 result.grad_g.row(0) = -I_par.row(0).array() / four_pi;
294 result.grad_g.row(1) = -I_par.row(1).array() / four_pi;
295 result.grad_g.row(2) = I_perp.array() / four_pi;
296 }
297
298 return result;
299
300};
301
302}
303
304#endif
static EigRowVec< Float > directed_angle_between_vectors(ConstEigRef< EigMatNX< Float, dim > > v1, ConstEigRef< EigMatNX< Float, dim > > v2)
Returns the directional angles between pairs of vectors in a right-hand system where counter-clockwis...
SrcResult integrate(const Complex k, const Triangle< 2 > &src_tri, ConstEigRef< EigMatNX< Float, 3 > > r_obs) override
Computes the integral over the source triangle.
Definition line.tpp:34
const Complex J
Imaginary unit.
Definition constants.hpp:40
const Float float_eps
Numerical infinitesimal.
Definition constants.hpp:70
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > EigMat
Dynamic-size matrix containing type T.
Definition types.hpp:66
const Eigen::Ref< const EigObj > ConstEigRef
Read-only reference to an Eigen object.
Definition types.hpp:98
Eigen::Matrix< T, 1, Eigen::Dynamic > EigRowVec
Dynamic-size row vector containing type T.
Definition types.hpp:90
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
Eigen::Matrix< T, N, Eigen::Dynamic > EigMatNX
Fixed-height matrix with N rows containing type T.
Definition types.hpp:78
Namespace for RWG-based BEM functionality.
Data structure to hold the results of integration over the source triangle.
Definition base.hpp:37