Bembel
Coefficients.hpp
1 // This file is part of Bembel, the higher order C++ boundary element library.
2 //
3 // Copyright (C) 2022 see <http://www.bembel.eu>
4 //
5 // It was written as part of a cooperation of J. Doelz, H. Harbrecht, S. Kurz,
6 // M. Multerer, S. Schoeps, and F. Wolf at Technische Universitaet Darmstadt,
7 // Universitaet Basel, and Universita della Svizzera italiana, Lugano. This
8 // source code is subject to the GNU General Public License version 3 and
9 // provided WITHOUT ANY WARRANTY, see <http://www.bembel.eu> for further
10 // information.
11 
12 #ifndef BEMBEL_SRC_HOMOGENISEDLAPLACE_COEFFICIENTS_HPP_
13 #define BEMBEL_SRC_HOMOGENISEDLAPLACE_COEFFICIENTS_HPP_
14 
15 #ifndef POINT_DEGREE
16 #define POINT_DEGREE 20
17 #endif
18 
19 #include <Eigen/Dense>
20 #include <Bembel/HomogenisedLaplace>
21 #include <Bembel/Quadrature>
22 
23 #include <functional>
24 
25 namespace Bembel {
26 
27 Eigen::VectorXd getCoefficients(double precision);
28 
29 inline unsigned int getDegree(double precision);
30 
31 Eigen::VectorXd getDisplacement(Eigen::MatrixXd ps_l, Eigen::MatrixXd ps_f,
32  Eigen::MatrixXd ps_b);
33 
34 inline double k_mod(Eigen::Vector3d in);
35 
36 inline Eigen::Vector3d Dk_mod(Eigen::Vector3d in);
37 
38 double calculateFirstCoefficient(Eigen::VectorXd cs, unsigned int deg,
39  Eigen::MatrixXd ps_l, Eigen::MatrixXd ps_f, Eigen::MatrixXd ps_b);
40 
60 Eigen::VectorXd getCoefficients(double precision) {
61  Eigen::VectorXd diff;
62  Eigen::RowVectorXd difft;
63  Eigen::Vector3d v;
64  Eigen::MatrixXd spherical_values_pre, spherical_values_pst, Dsolid_values_pre,
65  Dsolid_values_pst;
66 
67  unsigned int deg = getDegree(precision);
68  unsigned int Msquare = (POINT_DEGREE + 1) * (POINT_DEGREE + 1);
69 
70  unsigned int m, n, k;
71  double scale, fac, norm;
72 
74  Eigen::MatrixXd xs = GS[POINT_DEGREE].xi_;
75  xs -= 0.5 * Eigen::MatrixXd::Ones(xs.rows(), xs.cols());
76 
77  Eigen::Vector3d ex(1.0, 0.0, 0.0);
78  Eigen::Vector3d ey(0.0, 1.0, 0.0);
79  Eigen::Vector3d ez(0.0, 0.0, 1.0);
80 
81  Eigen::MatrixXd ps_left(3, xs.cols());
82  ps_left.row(0) = -0.5 * Eigen::VectorXd::Ones(xs.cols());
83  ps_left.block(1, 0, 2, xs.cols()) = xs.block(0, 0, 2, xs.cols());
84 
85  Eigen::MatrixXd ps_front(3, xs.cols());
86  ps_front.row(0) = xs.row(0);
87  ps_front.row(1) = -0.5 * Eigen::VectorXd::Ones(xs.cols());
88  ps_front.row(2) = xs.row(1);
89 
90  Eigen::MatrixXd ps_bottom(3, xs.cols());
91  ps_bottom.block(0, 0, 2, xs.cols()) = xs.block(0, 0, 2, xs.cols());
92  ps_bottom.row(2) = -0.5 * Eigen::VectorXd::Ones(xs.cols());
93 
94  Eigen::VectorXd displacement = getDisplacement(ps_left, ps_front, ps_bottom);
95 
96  Eigen::MatrixXd systemMatrix(6 * Msquare, ((deg + 1) * (deg + 2)) / 2 - 1);
97 
98  for (k = 0; k < Msquare; k++) {
99  /* left - right difference */
100  v = ps_left.col(k);
101  norm = v.norm();
102 
103  spherical_values_pre = spherical_harmonics_full(v, deg);
104  spherical_values_pst = spherical_harmonics_full(v + ex, deg);
105 
106  Dsolid_values_pre = Dsolid_harmonics_full(v, deg, spherical_values_pre);
107  Dsolid_values_pst = Dsolid_harmonics_full(v + ex, deg,
108  spherical_values_pst);
109 
110  for (n = 1; n <= deg; n++) {
111  scale = pow(norm, n);
112  diff = scale
113  * (spherical_values_pre.block(n * n + n, 0, n + 1, 1)
114  - spherical_values_pst.block(n * n + n, 0, n + 1, 1));
115  difft = Dsolid_values_pre.block(0, n * n + n, 1, n + 1)
116  - Dsolid_values_pst.block(0, n * n + n, 1, n + 1);
117 
118  /* adapt the scaling */
119  diff.segment(1, n) *= 2.0;
120  difft.segment(1, n) *= 2.0;
121 
122  systemMatrix.block(k, (n * (n + 1)) / 2 - 1, 1, n + 1) = diff.transpose();
123  systemMatrix.block(k + Msquare, (n * (n + 1)) / 2 - 1, 1, n + 1) = difft;
124  }
125 
126  /* front - back difference */
127  v = ps_front.col(k);
128  norm = v.norm();
129 
130  spherical_values_pre = spherical_harmonics_full(v, deg);
131  spherical_values_pst = spherical_harmonics_full(v + ey, deg);
132 
133  Dsolid_values_pre = Dsolid_harmonics_full(v, deg, spherical_values_pre);
134  Dsolid_values_pst = Dsolid_harmonics_full(v + ey, deg,
135  spherical_values_pst);
136 
137  for (n = 1; n <= deg; n++) {
138  scale = pow(norm, n);
139  diff = scale
140  * (spherical_values_pre.block(n * n + n, 0, n + 1, 1)
141  - spherical_values_pst.block(n * n + n, 0, n + 1, 1));
142  difft = Dsolid_values_pre.block(1, n * n + n, 1, n + 1)
143  - Dsolid_values_pst.block(1, n * n + n, 1, n + 1);
144 
145  /* adapt the scaling */
146  diff.segment(1, n) *= 2.0;
147  difft.segment(1, n) *= 2.0;
148 
149  systemMatrix.block(k + 2 * Msquare, (n * (n + 1)) / 2 - 1, 1, n + 1) =
150  diff.transpose();
151  systemMatrix.block(k + 3 * Msquare, (n * (n + 1)) / 2 - 1, 1, n + 1) =
152  difft;
153  }
154 
155  /* bottom - top difference */
156  v = ps_bottom.col(k);
157  norm = v.norm();
158 
159  spherical_values_pre = spherical_harmonics_full(v, deg);
160  spherical_values_pst = spherical_harmonics_full(v + ez, deg);
161 
162  Dsolid_values_pre = Dsolid_harmonics_full(v, deg, spherical_values_pre);
163  Dsolid_values_pst = Dsolid_harmonics_full(v + ez, deg,
164  spherical_values_pst);
165 
166  for (n = 1; n <= deg; n++) {
167  scale = pow(norm, n);
168  diff = scale
169  * (spherical_values_pre.block(n * n + n, 0, n + 1, 1)
170  - spherical_values_pst.block(n * n + n, 0, n + 1, 1));
171  difft = Dsolid_values_pre.block(2, n * n + n, 1, n + 1)
172  - Dsolid_values_pst.block(2, n * n + n, 1, n + 1);
173 
174  /* adapt the scaling */
175  diff.segment(1, n) *= 2.0;
176  difft.segment(1, n) *= 2.0;
177 
178  systemMatrix.block(k + 4 * Msquare, (n * (n + 1)) / 2 - 1, 1, n + 1) =
179  diff.transpose();
180  systemMatrix.block(k + 5 * Msquare, (n * (n + 1)) / 2 - 1, 1, n + 1) =
181  difft;
182  }
183  }
184 
185  /* solve the system */
186  Eigen::VectorXd coeffs(((deg + 1) * (deg + 2)) / 2);
187  coeffs.setZero();
188  coeffs.segment(1, ((deg + 1) * (deg + 2)) / 2 - 1) =
189  systemMatrix.colPivHouseholderQr().solve(-displacement);
190 
191  /* Copy the stuff into the full Coefficient list */
192  Eigen::VectorXd coeffs_full((deg + 1) * (deg + 1));
193  coeffs_full(0) = 0;
194  for (n = 1; n <= deg; n++) {
195  coeffs_full(n * n + n) = coeffs((n * (n + 1)) / 2);
196  for (m = 1; m <= n; m++) {
197  coeffs_full(n * n + n + m) = coeffs((n * (n + 1)) / 2 + m);
198  coeffs_full(n * n + n - m) = coeffs((n * (n + 1)) / 2 + m);
199  }
200  }
201 
202  /* calculate the first coefficient */
203  coeffs_full(0) = calculateFirstCoefficient(coeffs_full, deg, ps_left,
204  ps_front, ps_bottom);
205 
206  return coeffs_full;
207 }
208 
221 inline unsigned int getDegree(double precision) {
222  if (precision > 1e-4) {
223  return 4;
224  } else if (precision > 1e-6) {
225  return 8;
226  } else {
227  return 12;
228  }
229 }
230 
238 Eigen::VectorXd getDisplacement(Eigen::MatrixXd ps_l, Eigen::MatrixXd ps_f,
239  Eigen::MatrixXd ps_b) {
240 
241  std::function<double(Eigen::Vector3d)> u = [](Eigen::Vector3d in) {
242  return k_mod(in);
243  };
244  std::function<Eigen::Vector3d(Eigen::Vector3d)> Du = [](Eigen::Vector3d in) {
245  return Dk_mod(in);
246  };
247 
248  unsigned int Msquare = (POINT_DEGREE + 1) * (POINT_DEGREE + 1);
249  unsigned int k;
250 
251  Eigen::Vector3d ex(1.0, 0.0, 0.0);
252  Eigen::Vector3d ey(0.0, 1.0, 0.0);
253  Eigen::Vector3d ez(0.0, 0.0, 1.0);
254 
255  Eigen::Vector3d tmp;
256  Eigen::VectorXd d(6 * Msquare); /* the return vector */
257 
258  /* left - right displacement */
259  for (k = 0; k < Msquare; k++) {
260  tmp = Du(ps_l.col(k)) - Du(ps_l.col(k) + ex);
261  d(k) = u(ps_l.col(k)) - u(ps_l.col(k) + ex);
262  d(k + Msquare) = tmp(0);
263  }
264 
265  /* front - back displacement */
266  for (k = 0; k < Msquare; k++) {
267  tmp = Du(ps_f.col(k)) - Du(ps_f.col(k) + ey);
268  d(k + 2 * Msquare) = u(ps_f.col(k)) - u(ps_f.col(k) + ey);
269  d(k + 3 * Msquare) = tmp(1);
270  }
271 
272  /* bottom - top displacement */
273  for (k = 0; k < Msquare; k++) {
274  tmp = Du(ps_b.col(k)) - Du(ps_b.col(k) + ez);
275  d(k + 4 * Msquare) = u(ps_b.col(k)) - u(ps_b.col(k) + ez);
276  d(k + 5 * Msquare) = tmp(2);
277  }
278 
279  return d;
280 }
281 
286 inline double k_mod(Eigen::Vector3d in) {
287  double r = 0.0;
288  int i, j, k;
289  Eigen::Vector3d m;
290 
291  for (i = -1; i <= 1; i++) {
292  for (j = -1; j <= 1; j++) {
293  for (k = -1; k <= 1; k++) {
294  m = Eigen::Vector3d(i, j, k);
295  r += 1.0 / ((in - m).norm());
296  }
297  }
298  }
299 
300  r /= (4 * M_PI);
301 
302  /* the part to ensure the vanishing mean on the Laplacian */
303  r += (in.dot(in)) / 6.0;
304 
305  return r;
306 }
307 
312 inline Eigen::Vector3d Dk_mod(Eigen::Vector3d in) {
313  Eigen::Vector3d r, s;
314  double snorm;
315  r.setZero();
316 
317  int i, j, k;
318  for (i = -1; i <= 1; i++) {
319  for (j = -1; j <= 1; j++) {
320  for (k = -1; k <= 1; k++) {
321  s = in - Eigen::Vector3d(i, j, k);
322  snorm = s.norm();
323  r -= s / (snorm * snorm * snorm);
324  }
325  }
326  }
327 
328  r /= (4.0 * M_PI);
329 
330  /* the part to ensure the vanishing mean on the Laplacian */
331  r += in / 3.0;
332 
333  return r;
334 }
335 
346 double calculateFirstCoefficient(Eigen::VectorXd cs, unsigned int deg,
347  Eigen::MatrixXd ps_l, Eigen::MatrixXd ps_f, Eigen::MatrixXd ps_b) {
348  double res = 0.0;
349 
351  Eigen::VectorXd ws = GS[POINT_DEGREE].w_;
352 
353  Eigen::VectorXd cs_tmp(cs.rows());
354  cs_tmp.setZero();
355 
356  unsigned int k, n;
357  double norm;
358 
359  for (k = 0; k < ps_l.cols(); k++) {
360  norm = ps_l.col(k).norm(); /* equal for ps_f and ps_b */
361  for (n = 1; n <= deg; n++) {
362  cs_tmp.segment(n * n, 2 * n + 1) = pow(norm, n)
363  * cs.segment(n * n, 2 * n + 1);
364  }
365 
366  res += ws(k)
367  * (k_mod(ps_l.col(k))
368  + evaluate_sphericals(ps_l.col(k) / norm, cs_tmp, deg));
369  res += ws(k)
370  * (k_mod(ps_f.col(k))
371  + evaluate_sphericals(ps_f.col(k) / norm, cs_tmp, deg));
372  res += ws(k)
373  * (k_mod(ps_b.col(k))
374  + evaluate_sphericals(ps_b.col(k) / norm, cs_tmp, deg));
375  }
376 
377  res /= 3.0;
378 
379  res += (1.0 / 24);
380 
381  return -2.0 * sqrt(M_PI) * res;
382 }
383 
384 } /* namespace Bembel */
385 
386 #endif // BEMBEL_SRC_HOMOGENISEDLAPLACE_COEFFICIENTS_HPP_
Routines for the evalutation of pointwise errors.
Definition: AnsatzSpace.hpp:14
Eigen::VectorXd getCoefficients(double precision)
Calculates the coefficients for the solid harmonics expansion of the periodic kernel .
double evaluate_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs, unsigned int deg)
Evaluates the series for real coefficients, with the convenction that .
Definition: Sphericals.hpp:68
unsigned int getDegree(double precision)
Returns the degree of the sphericals expansion given a precision. Can be extended,...
Eigen::VectorXd getDisplacement(Eigen::MatrixXd ps_l, Eigen::MatrixXd ps_f, Eigen::MatrixXd ps_b)
Returns the right-hand side for the homogenised coefficient calculation.
Eigen::Vector3d Dk_mod(Eigen::Vector3d in)
Returns the gradient of the modified kernel .
double k_mod(Eigen::Vector3d in)
Returns the modified kernel .
Eigen::Matrix< double, 3, Eigen::Dynamic > Dsolid_harmonics_full(Eigen::Vector3d x, unsigned int N, Eigen::MatrixXd spherical_val)
Calculates all gradients of the solid harmonics, given the Legendre Coefficients L.
Definition: Sphericals.hpp:492
Eigen::Matrix< double, Eigen::Dynamic, 2 > spherical_harmonics_full(Eigen::Vector3d x, unsigned int N)
Calculates the the spherical harmonics , ordered by .
Definition: Sphericals.hpp:388
double calculateFirstCoefficient(Eigen::VectorXd cs, unsigned int deg, Eigen::MatrixXd ps_l, Eigen::MatrixXd ps_f, Eigen::MatrixXd ps_b)
Calculates the first coefficient such that the mean of the kernel vanishes.