Bembel
 
Loading...
Searching...
No Matches
Sphericals.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_UTIL_SPHERICALS_HPP_
13#define BEMBEL_SRC_UTIL_SPHERICALS_HPP_
14#include <Eigen/Dense>
15
16#if !defined pi
17#define pi BEMBEL_PI
18#endif
19
20namespace Bembel {
21
22double evaluate_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs,
23 unsigned int deg);
24
25double evaluate_solid_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs,
26 unsigned int deg, bool grad);
27
28Eigen::Vector3d evaluate_dsphericals(Eigen::Vector3d x, Eigen::VectorXd cs,
29 unsigned int deg);
30
31Eigen::Vector3d evaluate_dsolid_sphericals(Eigen::Vector3d x,
32 Eigen::VectorXd cs, unsigned int deg);
33
34Eigen::Matrix<double, Eigen::Dynamic, 2> spherical_harmonics_full(
35 Eigen::Vector3d x, unsigned int N);
36
37inline Eigen::Vector2d spherical_prev(Eigen::Vector3d x, int m, int n,
38 Eigen::Vector2d y1, Eigen::Vector2d y2);
39
40Eigen::Matrix<double, 3, Eigen::Dynamic> Dsolid_harmonics_full(
41 Eigen::Vector3d x, unsigned int N, Eigen::MatrixXd spherical_val);
42
43Eigen::Matrix<double, 3, 2> dsolid_spherical_prev(Eigen::Vector3d y, int m,
44 unsigned int n, Eigen::VectorXd L, double y_re, double y_im);
45
46Eigen::VectorXd legendreFull(unsigned int N, double t);
47
48double constant(int m, unsigned int n);
49
50inline Eigen::Matrix3d functionalMatrix(Eigen::Vector3d z);
51
52inline double pow_int(double x, int n);
53
68double evaluate_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs,
69 unsigned int deg) {
70 unsigned int m, n;
71 double z1[2], z2[2], z3[2], z1_start[2];
72 double r, fac, rootTimesZ, root_2, root_3;
73
74 assert(abs(x.norm() - 1) < Constants::generic_tolerance);
75
76 r = z1[1] = 0;
77 z1[0] = 0.5 / sqrt(pi);
78 if (deg <= 1) {
79 return cs(0) * z1[0];
80 }
81
82 for (m = 0; m < deg - 1; m++) {
83 if (m == 0) {
84 fac = 1.0;
85 } else {
86 fac = 2.0;
87 }
88
89 z1_start[0] = z1[0];
90 z1_start[1] = z1[1];
91 rootTimesZ = sqrt(2 * m + 3) * x(2);
92 z2[0] = rootTimesZ * z1[0];
93 z2[1] = rootTimesZ * z1[1];
94 r += fac * cs(m * (m + 1) + m) * z1[0]; // + k[ m *(m+1)-m]*z1[1];
95 r += fac * cs((m + 1) * (m + 2) + m) * z2[0]; // + k[(m+1)*(m+2)-m]*z2[1];
96 for (n = m + 2; n < deg; n++) {
97 root_2 = sqrt((2 * n + 1.0) / ((n - m) * (n + m)));
98 rootTimesZ = sqrt(2 * n - 1) * x(2);
99 root_3 = sqrt(((n + m - 1.0) * (n - m - 1.0)) / (2 * n - 3));
100 z3[0] = root_2 * (rootTimesZ * z2[0] - root_3 * z1[0]);
101 z3[1] = root_2 * (rootTimesZ * z2[1] - root_3 * z1[1]);
102 r += fac * cs(n * (n + 1) + m) * z3[0]; // + k[n*(n+1)-m]*z3[1];
103 z1[0] = z2[0];
104 z1[1] = z2[1];
105 z2[0] = z3[0];
106 z2[1] = z3[1];
107 }
108 root_2 = sqrt((2 * m + 3.0) / (2 * m + 2));
109 z1[0] = root_2 * (x(0) * z1_start[0] - x(1) * z1_start[1]);
110 z1[1] = root_2 * (x(0) * z1_start[1] + x(1) * z1_start[0]);
111 }
112 r += 2 * cs((deg - 1) * (deg + 1)) * z1[0]; // + k[(nk-1)*(nk-1)]*z1[1];
113 return r;
114}
115
128double evaluate_solid_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs,
129 unsigned int deg, bool grad) {
130 unsigned int m, n;
131 double z1[2], z2[2], z3[2], z1_start[2];
132 double r, fac, rootTimesZ, root_2, root_3, norm, fac_tot;
133 Eigen::Vector3d y;
134
135 r = z1[1] = 0;
136 z1[0] = 0.5 / sqrt(pi);
137 if (grad && deg <= 1) {
138 return 0.0;
139 } else if (!grad && deg <= 1) {
140 return cs(0) * z1[0];
141 }
142
143 norm = x.norm();
144 y = x / norm;
145
146 for (m = 0; m < deg - 1; m++) {
147 if (m == 0) {
148 fac = 1.0;
149 } else {
150 fac = 2.0;
151 }
152
153 if (grad) {
154 fac_tot = fac * m * pow_int(norm, m - 1);
155 } else {
156 fac_tot = fac * pow_int(norm, m);
157 }
158
159 z1_start[0] = z1[0];
160 z1_start[1] = z1[1];
161 rootTimesZ = sqrt(2 * m + 3) * y(2);
162 z2[0] = rootTimesZ * z1[0];
163 z2[1] = rootTimesZ * z1[1];
164 r += fac_tot * cs(m * (m + 1) + m) * z1[0];
165
166 if (grad) {
167 fac_tot = fac * (m + 1) * pow_int(norm, m);
168 } else {
169 fac_tot *= norm;
170 }
171 r += fac_tot * cs((m + 1) * (m + 2) + m) * z2[0];
172
173 for (n = m + 2; n < deg; n++) {
174 if (grad) {
175 fac_tot = fac * n * pow_int(norm, n - 1);
176 } else {
177 fac_tot *= norm;
178 }
179 root_2 = sqrt((2 * n + 1.0) / ((n - m) * (n + m)));
180 rootTimesZ = sqrt(2 * n - 1) * y(2);
181 root_3 = sqrt(((n + m - 1.0) * (n - m - 1.0)) / (2 * n - 3));
182 z3[0] = root_2 * (rootTimesZ * z2[0] - root_3 * z1[0]);
183 z3[1] = root_2 * (rootTimesZ * z2[1] - root_3 * z1[1]);
184 r += fac_tot * cs(n * (n + 1) + m) * z3[0];
185 z1[0] = z2[0];
186 z1[1] = z2[1];
187 z2[0] = z3[0];
188 z2[1] = z3[1];
189 }
190 root_2 = sqrt((2 * m + 3.0) / (2 * m + 2));
191 z1[0] = root_2 * (y(0) * z1_start[0] - y(1) * z1_start[1]);
192 z1[1] = root_2 * (y(0) * z1_start[1] + y(1) * z1_start[0]);
193 }
194
195 if (grad) {
196 fac_tot = fac * deg * pow_int(norm, deg - 1);
197 } else {
198 fac_tot *= norm;
199 }
200 r += fac_tot * cs((deg - 1) * (deg + 1)) * z1[0];
201 return r;
202}
203
213Eigen::Vector3d evaluate_dsphericals(Eigen::Vector3d x, Eigen::VectorXd cs,
214 unsigned int deg) {
215 unsigned int m, n;
216 double z1[2], z2[2], z3[2], z1_start[2];
217 Eigen::Vector3d dr;
218 double fac, rootTimesZ, root_2, root_3;
219
220 assert(abs(x.norm() - 1) < Constants::generic_tolerance);
221
222 z1[0] = sqrt(0.375 / pi);
223 z1[1] = 0;
224
225 dr = Eigen::Vector3d(0.0, 0.0, 0.0);
226
227 if (deg == 1) {
228 return dr;
229 }
230
231 for (m = 1; m < deg - 1; m++) {
232 z1_start[0] = z1[0];
233 z1_start[1] = z1[1];
234 rootTimesZ = sqrt(2 * m + 3) * x(2);
235 z2[0] = rootTimesZ * z1[0];
236 z2[1] = rootTimesZ * z1[1];
237 dr(0) += 2 * m * (cs(m * (m + 1) + m) * z1[0]);
238 // + a[ m *(m+1)-m]*z1[1]) for imaginary coefficients;
239 dr(0) += 2 * m * (cs((m + 1) * (m + 2) + m) * z2[0]);
240 // + a[(m+1)*(m+2)-m]*z2[1]) for imaginary coefficients;
241 dr(1) -= 2 * m * (cs(m * (m + 1) + m) * z1[1]);
242 // - a[ m *(m+1)-m]*z1[0]) for imaginary coefficients;
243 dr(1) -= 2 * m * (cs((m + 1) * (m + 2) + m) * z2[1]);
244 // - a[(m+1)*(m+2)-m]*z2[0]) for imaginary coefficients;
245
246 if (m == 1) {
247 fac = 1.0;
248 dr(2) += fac * sqrt(2 * m)
249 * (cs(m * (m + 1) + (m - 1)) * z1[0]
250 + cs(m * (m + 1) - (m - 1)) * z1[1]);
251 dr(2) += fac * sqrt(4 * m + 2)
252 * (cs((m + 1) * (m + 2) + (m - 1)) * z2[0]
253 + cs((m + 1) * (m + 2) - (m - 1)) * z2[1]);
254 } else {
255 fac = 2.0;
256 dr(2) += fac * sqrt(2 * m) * (cs(m * (m + 1) + (m - 1)) * z1[0]);
257 // + a[ m *(m+1)-(m-1)]*z1[1]) for imaginary coefficients;
258 dr(2) += fac * sqrt(4 * m + 2)
259 * (cs((m + 1) * (m + 2) + (m - 1)) * z2[0]);
260 // + a[(m+1)*(m+2)-(m-1)]*z2[1]) for imaginary coefficients;
261 }
262
263 for (n = m + 2; n < deg; n++) {
264 root_2 = sqrt((2 * n + 1.0) / ((n - m) * (n + m)));
265 rootTimesZ = sqrt(2 * n - 1) * x(2);
266 root_3 = sqrt(((n + m - 1.0) * (n - m - 1.0)) / (2 * n - 3));
267 z3[0] = root_2 * (rootTimesZ * z2[0] - root_3 * z1[0]);
268 z3[1] = root_2 * (rootTimesZ * z2[1] - root_3 * z1[1]);
269 dr(0) += 2 * m * (cs(n * (n + 1) + m) * z3[0]); // + a[n*(n+1)-m]*z3[1]);
270 dr(1) -= 2 * m * (cs(n * (n + 1) + m) * z3[1]); // - a[n*(n+1)-m]*z3[0]);
271 dr(2) += fac * sqrt((n + m) * (n - m + 1))
272 * (cs(n * (n + 1) + (m - 1)) * z3[0]); // + a[n*(n+1)-(m-1)]*z3[1]);
273 z1[0] = z2[0];
274 z1[1] = z2[1];
275 z2[0] = z3[0];
276 z2[1] = z3[1];
277 }
278 root_2 = sqrt((2 * m + 3.0) / (2 * m + 2));
279 z1[0] = root_2 * (x(0) * z1_start[0] - x(1) * z1_start[1]);
280 z1[1] = root_2 * (x(0) * z1_start[1] + x(1) * z1_start[0]);
281 }
282 dr(0) += (deg - 1) * (2 * cs((deg - 1) * (deg + 1)) * z1[0]);
283 // + a[(na-1)*(na-1)]*z1[1]) for imaginary coefficients;
284 dr(1) -= (deg - 1) * (2 * cs((deg - 1) * (deg + 1)) * z1[1]);
285 // - a[(na-1)*(na-1)]*z1[0]) for imaginary coefficients;
286 dr(2) += 2 * sqrt(2 * (deg - 1))
287 * (cs(deg * (deg - 1) + (deg - 2)) * z1[0]
288 + cs(deg * (deg - 1) - (deg - 2)) * z1[1]);
289
290 return dr;
291}
292
302Eigen::Vector3d evaluate_dsolid_sphericals(Eigen::Vector3d x,
303 Eigen::VectorXd cs, unsigned int deg) {
304 unsigned int m, n;
305 double z1[2], z2[2], z3[2], z1_start[2];
306 Eigen::Vector3d dr, y;
307 double fac, rootTimesZ, root_2, root_3, norm, r_n3;
308
309 z1[0] = sqrt(0.375 / pi);
310 z1[1] = 0;
311
312 dr = Eigen::Vector3d(0.0, 0.0, 0.0);
313 norm = x.norm();
314 y = x / norm;
315
316 if (deg == 1) {
317 return dr;
318 }
319
320 for (m = 1; m < deg - 1; m++) {
321 r_n3 = pow_int(norm, m - 3);
322
323 z1_start[0] = z1[0];
324 z1_start[1] = z1[1];
325 rootTimesZ = sqrt(2 * m + 3) * y(2);
326 z2[0] = rootTimesZ * z1[0];
327 z2[1] = rootTimesZ * z1[1];
328 dr(0) += 2 * m * r_n3 * (cs(m * (m + 1) + m) * z1[0]);
329 dr(0) += 2 * m * r_n3 * norm * (cs((m + 1) * (m + 2) + m) * z2[0]);
330 dr(1) -= 2 * m * r_n3 * (cs(m * (m + 1) + m) * z1[1]);
331 dr(1) -= 2 * m * r_n3 * norm * (cs((m + 1) * (m + 2) + m) * z2[1]);
332
333 if (m == 1) {
334 fac = 1.0;
335 dr(2) += fac * r_n3 * sqrt(2 * m)
336 * (cs(m * (m + 1) + (m - 1)) * z1[0]
337 + cs(m * (m + 1) - (m - 1)) * z1[1]);
338 dr(2) += fac * r_n3 * norm * sqrt(4 * m + 2)
339 * (cs((m + 1) * (m + 2) + (m - 1)) * z2[0]
340 + cs((m + 1) * (m + 2) - (m - 1)) * z2[1]);
341 } else {
342 fac = 2.0;
343 dr(2) += fac * r_n3 * sqrt(2 * m) * (cs(m * (m + 1) + (m - 1)) * z1[0]);
344 dr(2) += fac * r_n3 * norm * sqrt(4 * m + 2)
345 * (cs((m + 1) * (m + 2) + (m - 1)) * z2[0]);
346 }
347
348 r_n3 *= norm;
349
350 for (n = m + 2; n < deg; n++) {
351 r_n3 *= norm;
352 root_2 = sqrt((2 * n + 1.0) / ((n - m) * (n + m)));
353 rootTimesZ = sqrt(2 * n - 1) * y(2);
354 root_3 = sqrt(((n + m - 1.0) * (n - m - 1.0)) / (2 * n - 3));
355 z3[0] = root_2 * (rootTimesZ * z2[0] - root_3 * z1[0]);
356 z3[1] = root_2 * (rootTimesZ * z2[1] - root_3 * z1[1]);
357 dr(0) += 2 * m * r_n3 * (cs(n * (n + 1) + m) * z3[0]);
358 dr(1) -= 2 * m * r_n3 * (cs(n * (n + 1) + m) * z3[1]);
359 dr(2) += fac * r_n3 * sqrt((n + m) * (n - m + 1))
360 * (cs(n * (n + 1) + (m - 1)) * z3[0]);
361 z1[0] = z2[0];
362 z1[1] = z2[1];
363 z2[0] = z3[0];
364 z2[1] = z3[1];
365 }
366 root_2 = sqrt((2 * m + 3.0) / (2 * m + 2));
367 z1[0] = root_2 * (y(0) * z1_start[0] - y(1) * z1_start[1]);
368 z1[1] = root_2 * (y(0) * z1_start[1] + y(1) * z1_start[0]);
369 }
370 r_n3 *= norm;
371 dr(0) += (deg - 1) * r_n3 * (2 * cs((deg - 1) * (deg + 1)) * z1[0]);
372 dr(1) -= (deg - 1) * r_n3 * (2 * cs((deg - 1) * (deg + 1)) * z1[1]);
373 dr(2) += 2 * r_n3 * sqrt(2 * (deg - 1))
374 * (cs(deg * (deg - 1) + (deg - 2)) * z1[0]
375 + cs(deg * (deg - 1) - (deg - 2)) * z1[1]);
376
377 return dr;
378}
379
388Eigen::Matrix<double, Eigen::Dynamic, 2> spherical_harmonics_full(
389 Eigen::Vector3d x, unsigned int N) {
390
391 Eigen::VectorXd real((N + 1) * (N + 1));
392 Eigen::VectorXd imag((N + 1) * (N + 1));
393
394 Eigen::Vector3d y = x / x.norm();
395
396 int m, n;
397
398 /* handle n = 0 separately */
399 real(0) = 0.5 / sqrt(pi);
400 imag(0) = 0.0;
401
402 /* assemble two temporary vectors */
403 Eigen::Vector2d z1, z2, tmp;
404
405 for (n = 1; n <= N; n++) {
406 for (m = 0; m < n - 1; m++) {
407 z1(0) = real((n - 1) * (n - 1) + n - 1 + m);
408 z1(1) = real((n - 2) * (n - 2) + n - 2 + m);
409 z2(0) = imag((n - 1) * (n - 1) + n - 1 + m);
410 z2(1) = imag((n - 2) * (n - 2) + n - 2 + m);
411
412 tmp = spherical_prev(y, m, n, z1, z2);
413
414 real(n * n + n + m) = tmp(0);
415 imag(n * n + n + m) = tmp(1);
416
417 // if m > 0, copy the value with the prefactor
418 if (m > 0) {
419 real(n * n + n - m) = tmp(0);
420 imag(n * n + n - m) = -tmp(1);
421 }
422 }
423
424 /* for m = n-1 */
425 m = n - 1;
426 z1(0) = real(m * m + m + m);
427 z2(0) = imag(m * m + m + m);
428
429 tmp = spherical_prev(y, m, n, z1, z2);
430 real(n * n + n + m) = tmp(0);
431 imag(n * n + n + m) = tmp(1);
432
433 if (m > 0) {
434 real(n * n + n - m) = tmp(0);
435 imag(n * n + n - m) = -tmp(1);
436 }
437
438 /* for m = n */
439 m = n;
440 z1(0) = real((n - 1) * (n - 1) + n - 1 + n - 1);
441 z2(0) = imag((n - 1) * (n - 1) + n - 1 + n - 1);
442
443 tmp = spherical_prev(y, m, n, z1, z2);
444 real(n * n + n + m) = tmp(0);
445 imag(n * n + n + m) = tmp(1);
446
447 if (m > 0) {
448 real(n * n + n - m) = tmp(0);
449 imag(n * n + n - m) = -tmp(1);
450 }
451 }
452
453 Eigen::MatrixXd res((N + 1) * (N + 1), 2);
454 res.col(0) = real;
455 res.col(1) = imag;
456
457 return res;
458}
459
463inline Eigen::Vector2d spherical_prev(Eigen::Vector3d x, int m, int n,
464 Eigen::Vector2d y1, Eigen::Vector2d y2) {
465 Eigen::Vector2d z;
466
467 assert(abs(x.norm() - 1) < 1e-14);
468
469 if ((m == 0) && (n == 0)) {
470 z(0) = 0.5 / sqrt(pi);
471 z(1) = 0;
472 } else if (m == n) {
473 z(0) = sqrt((2 * m + 1.0) / (2 * m)) * (x(0) * y1(0) - x(1) * y2(0));
474 z(1) = sqrt((2 * m + 1.0) / (2 * m)) * (x(0) * y2(0) + x(1) * y1(0));
475 } else if (m + 1 == n) {
476 z(0) = y1(0) * sqrt(2 * m + 3) * x(2);
477 z(1) = y2(0) * sqrt(2 * m + 3) * x(2);
478 } else {
479 z(0) = sqrt((2 * n + 1.0) / ((n - m) * (n + m)))
480 * (sqrt(2 * n - 1) * x(2) * y1(0)
481 - sqrt(((n + m - 1.0) * (n - m - 1.0)) / (2 * n - 3)) * y1(1));
482 z(1) = sqrt((2 * n + 1.0) / ((n - m) * (n + m)))
483 * (sqrt(2 * n - 1) * x(2) * y2(0)
484 - sqrt(((n + m - 1.0) * (n - m - 1.0)) / (2 * n - 3)) * y2(1));
485 }
486 return z;
487}
488
492Eigen::Matrix<double, 3, Eigen::Dynamic> Dsolid_harmonics_full(
493 Eigen::Vector3d x, unsigned int N, Eigen::MatrixXd spherical_val) {
494
495 Eigen::VectorXd L = legendreFull(N, x(2) / x.norm());
496 Eigen::Matrix<double, 3, 2> z;
497
498 Eigen::MatrixXd reals(3, (N + 1) * (N + 1)), imags(3, (N + 1) * (N + 1));
499
500 int m, n;
501
502 for (n = 0; n <= N; n++) {
503 for (m = 0; m <= n; m++) {
504 z = dsolid_spherical_prev(x, m, n, L, spherical_val(n * n + n + m, 0),
505 spherical_val(n * n + n + m, 1));
506
507 reals(0, n * n + n + m) = z(0, 0);
508 reals(1, n * n + n + m) = z(1, 0);
509 reals(2, n * n + n + m) = z(2, 0);
510
511 imags(0, n * n + n + m) = z(0, 1);
512 imags(1, n * n + n + m) = z(1, 1);
513 imags(2, n * n + n + m) = z(2, 1);
514
515 if (m > 0) {
516 reals(0, n * n + n - m) = z(0, 0);
517 reals(1, n * n + n - m) = z(1, 0);
518 reals(2, n * n + n - m) = z(2, 0);
519
520 imags(0, n * n + n - m) = -z(0, 1);
521 imags(1, n * n + n - m) = -z(1, 1);
522 imags(2, n * n + n - m) = -z(2, 1);
523 }
524 }
525 }
526 return reals;
527}
528
532inline Eigen::Matrix<double, 3, 2> dspherical_prev(Eigen::Vector3d x, int m,
533 unsigned int n, Eigen::VectorXd L) {
534
535 double c;
536 unsigned int i;
537
538 assert(abs(x.norm() - 1) < 1e-14);
539
540 Eigen::Matrix<double, 3, 2> z;
541
542 if (m == 0) {
543 z(0, 0) = z(0, 1) = z(2, 1) = 0;
544 if (1 > n) {
545 z(2, 0) = 0;
546 } else {
547 z(2, 0) = L((n * (n + 1)) / 2 + 1);
548 }
549 } else {
550 if (m > n) {
551 z(0, 0) = 0;
552 } else {
553 z(0, 0) = L((n * (n + 1)) / 2 + m);
554 }
555
556 if (m + 1 > n) {
557 z(2, 0) = 0;
558 } else {
559 z(2, 0) = L((n * (n + 1)) / 2 + m + 1);
560 }
561 z(0, 1) = z(2, 1) = 0;
562
563 c = x(0) * z(2, 0) - x(1) * z(2, 1);
564 z(2, 1) = x(0) * z(2, 1) + x(1) * z(2, 0);
565 z(2, 0) = c;
566
567 for (i = 1; i < m; i++) {
568 c = x(0) * z(0, 0) - x(1) * z(0, 1);
569 z(0, 1) = x(0) * z(0, 1) + x(1) * z(0, 0);
570 z(0, 0) = c;
571
572 c = x(0) * z(2, 0) - x(1) * z(2, 1);
573 z(2, 1) = x(0) * z(2, 1) + x(1) * z(2, 0);
574 z(2, 0) = c;
575 }
576 }
577
578 c = constant(m, n);
579 z(0, 0) *= m * c;
580 z(0, 1) *= m * c;
581 z(1, 0) = -z(0, 1);
582 z(1, 1) = +z(0, 0);
583 z(2, 0) *= c;
584 z(2, 1) *= c;
585
586 return z;
587}
588
593Eigen::Matrix<double, 3, 2> dsolid_spherical_prev(Eigen::Vector3d y, int m,
594 unsigned int n, Eigen::VectorXd L, double y_re, double y_im) {
595
596 // normalise y
597 // x denotes the normalised y and is passed to the original functions
598 double r = y.norm();
599 Eigen::Vector3d x = y / r;
600 Eigen::Matrix<double, 3, 2> z_harm;
601 Eigen::Matrix3d A;
602 unsigned int m_tilde;
603
604 // part with the gradient of Y
605 if (m >= 0) {
606 m_tilde = m;
608
609 // multiply the gradient of Y by r^(n-3) and the matrix A
610 double r_n3;
611 if (n > 3) {
612 r_n3 = pow(r, n - 3);
613 } else if (n == 3) {
614 r_n3 = 1.0;
615 } else {
616 r_n3 = pow(1.0 / r, 3 - n);
617 }
618
620 z_harm = r_n3 * (A * z_harm);
621
622 } else {
623 m_tilde = -m;
625
626 // define r^(n-3) as above
627 double r_n3;
628 if (n > 3) {
629 r_n3 = pow(r, n - 3);
630 } else if (n == 3) {
631 r_n3 = 1.0;
632 } else {
633 r_n3 = pow(1 / r, 3 - n);
634 }
635
636 // get the part of the gradient of Y,
637 // conjugate and multiply by the functional matrix
639 z_harm = r_n3 * (A * z_harm);
640
641 // get the complex conjugation
642 z_harm.col(1) *= -1.0;
643 }
644
645 // calculate the radial part and multiply with n*r^{n-1}
646 Eigen::Matrix<double, 3, 2> z_rad;
647
648 // case n = 0: r^n = 1 is constant
649 if (n == 0) {
650 z_rad.setZero();
651 } else {
652 // the gradient of r^n is n*r^(n-2)*y = nr^(n-1)*x
653 // again adapt the power
654 double nr_n1;
655 if (n == 1) {
656 nr_n1 = 1.0;
657 } else {
658 nr_n1 = n * pow(r, n - 1);
659 }
660
661 double y_imt;
662 if (m >= 0) {
663 y_imt = y_im;
664 } else {
665 y_imt = -y_im;
666 }
667
668 z_rad.col(0) = y_re * nr_n1 * x;
669 z_rad.col(1) = y_imt * nr_n1 * x;
670 }
671 return z_harm + z_rad;
672}
673
677Eigen::VectorXd legendreFull(unsigned int N, double t) {
678 Eigen::VectorXd L(((N + 1) * (N + 2)) / 2);
679 int n, s, m;
680
681 /* n = 0 */
682 L(0) = 1;
683
684 /* n = 1 */
685 L(1) = t;
686 L(2) = 1;
687
688 for (n = 2; n <= N; n++) {
689 s = (n * (n + 1)) / 2;
690 for (m = 0; m < n - 1; m++) {
691 L(s + m) = ((2 * n - 1) * t * L((n * (n - 1)) / 2 + m)
692 - (n + m - 1) * L(((n - 1) * (n - 2)) / 2 + m)) / (n - m);
693 }
694
695 /* m = n-1 */
696 m = n - 1;
697 L(s + m) = (2 * m + 1) * t * L((m * (m + 1)) / 2 + m);
698
699 /* m = n */
700 m = n;
701 L(s + m) = (2 * m - 1) * L((n * (n - 1)) / 2 + n - 1);
702 }
703 return L;
704}
705
709double constant(int m, unsigned int n) {
710 unsigned int i;
711 double c = sqrt((2 * n + 1) / (4 * pi));
712
713 for (i = 0; i < m; i++) {
714 c *= 1 / sqrt((n + i + 1) * (n - i));
715 }
716
717 return c;
718}
719
723inline Eigen::Matrix3d functionalMatrix(Eigen::Vector3d z) {
724 Eigen::Matrix3d M;
725
726 double r = z.norm();
727
728 M(0, 0) = r * r - z(0) * z(0);
729 M(1, 1) = r * r - z(1) * z(1);
730 M(2, 2) = r * r - z(2) * z(2);
731
732 M(1, 0) = M(0, 1) = -z(0) * z(1);
733 M(2, 0) = M(0, 2) = -z(0) * z(2);
734 M(2, 1) = M(1, 2) = -z(1) * z(2);
735
736 return M;
737}
738
742inline double pow_int(double x, int n) {
743 if (n == 0) {
744 return 1.0;
745 }
746
747 unsigned int ul, k;
748 if (n > 0) {
749 ul = n;
750 } else {
751 ul = -n;
752 }
753
754 double res = x;
755 for (k = 1; k < ul; k++) {
756 res *= x;
757 }
758
759 if (n > 0) {
760 return res;
761 } else {
762 return 1.0 / res;
763 }
764}
765
766} // namespace Bembel
767
768#endif // BEMBEL_SRC_UTIL_SPHERICALS_HPP_
Routines for the evalutation of pointwise errors.
double evaluate_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs, unsigned int deg)
Evaluates the series for real coefficients, with the convenction that .
Eigen::Vector3d evaluate_dsolid_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs, unsigned int deg)
Evaluates the series for real coefficients.
Eigen::VectorXd legendreFull(unsigned int N, double t)
Returns the values of the spherical polynomials .
Eigen::Matrix< double, 3, 2 > dsolid_spherical_prev(Eigen::Vector3d y, int m, unsigned int n, Eigen::VectorXd L, double y_re, double y_im)
Calculates the derivative of the solid harmonics function at the point with the spherical values.
double pow_int(double x, int n)
returns the -th power of without using pow.
double constant(int m, unsigned int n)
gives the norming factor of the spherical polynomials
double evaluate_solid_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs, unsigned int deg, bool grad)
Evaluates the series for real coefficients cs if grad is false, and for real coefficients cs if gra...
Eigen::Matrix< double, 3, 2 > dspherical_prev(Eigen::Vector3d x, int m, unsigned int n, Eigen::VectorXd L)
Calculates based on the Legendre Coefficients L.
constexpr int getFunctionSpaceOutputDimension()
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.
Eigen::Vector3d evaluate_dsphericals(Eigen::Vector3d x, Eigen::VectorXd cs, unsigned int deg)
Evaluates the series for real coefficients.
Eigen::Matrix< double, Eigen::Dynamic, 2 > spherical_harmonics_full(Eigen::Vector3d x, unsigned int N)
Calculates the the spherical harmonics , ordered by .
Eigen::Matrix3d functionalMatrix(Eigen::Vector3d z)
gives times the Jacobi Matrix of the transformation
Eigen::Vector2d spherical_prev(Eigen::Vector3d x, int m, int n, Eigen::Vector2d y1, Eigen::Vector2d y2)
Calculates the spherical harmonic based on previous values.