Bembel
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 
20 namespace Bembel {
21 
22 double evaluate_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs,
23  unsigned int deg);
24 
25 double evaluate_solid_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs,
26  unsigned int deg, bool grad);
27 
28 Eigen::Vector3d evaluate_dsphericals(Eigen::Vector3d x, Eigen::VectorXd cs,
29  unsigned int deg);
30 
31 Eigen::Vector3d evaluate_dsolid_sphericals(Eigen::Vector3d x,
32  Eigen::VectorXd cs, unsigned int deg);
33 
34 Eigen::Matrix<double, Eigen::Dynamic, 2> spherical_harmonics_full(
35  Eigen::Vector3d x, unsigned int N);
36 
37 inline Eigen::Vector2d spherical_prev(Eigen::Vector3d x, int m, int n,
38  Eigen::Vector2d y1, Eigen::Vector2d y2);
39 
40 Eigen::Matrix<double, 3, Eigen::Dynamic> Dsolid_harmonics_full(
41  Eigen::Vector3d x, unsigned int N, Eigen::MatrixXd spherical_val);
42 
43 Eigen::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 
46 Eigen::VectorXd legendreFull(unsigned int N, double t);
47 
48 double constant(int m, unsigned int n);
49 
50 inline Eigen::Matrix3d functionalMatrix(Eigen::Vector3d z);
51 
52 inline double pow_int(double x, int n);
53 
68 double 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 
128 double 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 
213 Eigen::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 
302 Eigen::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 
388 Eigen::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 
463 inline 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 
492 Eigen::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 
532 inline 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 
593 Eigen::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;
607  z_harm = dspherical_prev(x, m_tilde, n, L);
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 
619  A = functionalMatrix(y);
620  z_harm = r_n3 * (A * z_harm);
621 
622  } else {
623  m_tilde = -m;
624  z_harm = dspherical_prev(x, m_tilde, n, L);
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
638  A = functionalMatrix(y);
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 
677 Eigen::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 
709 double 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 
723 inline 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 
742 inline 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.
Definition: AnsatzSpace.hpp:14
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
Eigen::Vector3d evaluate_dsolid_sphericals(Eigen::Vector3d x, Eigen::VectorXd cs, unsigned int deg)
Evaluates the series for real coefficients.
Definition: Sphericals.hpp:302
Eigen::VectorXd legendreFull(unsigned int N, double t)
Returns the values of the spherical polynomials .
Definition: Sphericals.hpp:677
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.
Definition: Sphericals.hpp:593
double pow_int(double x, int n)
returns the -th power of without using pow.
Definition: Sphericals.hpp:742
double constant(int m, unsigned int n)
gives the norming factor of the spherical polynomials
Definition: Sphericals.hpp:709
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...
Definition: Sphericals.hpp:128
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::Vector3d evaluate_dsphericals(Eigen::Vector3d x, Eigen::VectorXd cs, unsigned int deg)
Evaluates the series for real coefficients.
Definition: Sphericals.hpp:213
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
Eigen::Matrix3d functionalMatrix(Eigen::Vector3d z)
gives times the Jacobi Matrix of the transformation
Definition: Sphericals.hpp:723
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.
Definition: Sphericals.hpp:463
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.
Definition: Sphericals.hpp:532