ngsolve 6.2.2501.post47.dev1__cp313-cp313-macosx_10_15_universal2.whl → 6.2.2501.post55.dev1__cp313-cp313-macosx_10_15_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ngsolve might be problematic. Click here for more details.

Files changed (285) hide show
  1. netgen/include/arnoldi.hpp +55 -0
  2. netgen/include/bandmatrix.hpp +334 -0
  3. netgen/include/basematrix.hpp +957 -0
  4. netgen/include/basevector.hpp +1268 -0
  5. netgen/include/bdbequations.hpp +2752 -0
  6. netgen/include/bdbintegrator.hpp +1660 -0
  7. netgen/include/bessel.hpp +1064 -0
  8. netgen/include/bilinearform.hpp +963 -0
  9. netgen/include/bla.hpp +29 -0
  10. netgen/include/blockalloc.hpp +95 -0
  11. netgen/include/blockjacobi.hpp +316 -0
  12. netgen/include/bspline.hpp +114 -0
  13. netgen/include/calcinverse.hpp +141 -0
  14. netgen/include/cg.hpp +368 -0
  15. netgen/include/chebyshev.hpp +44 -0
  16. netgen/include/cholesky.hpp +720 -0
  17. netgen/include/clapack.h +7254 -0
  18. netgen/include/code_generation.hpp +296 -0
  19. netgen/include/coefficient.hpp +2033 -0
  20. netgen/include/coefficient_impl.hpp +19 -0
  21. netgen/include/coefficient_stdmath.hpp +167 -0
  22. netgen/include/commutingAMG.hpp +106 -0
  23. netgen/include/comp.hpp +79 -0
  24. netgen/include/compatibility.hpp +41 -0
  25. netgen/include/complex_wrapper.hpp +73 -0
  26. netgen/include/compressedfespace.hpp +110 -0
  27. netgen/include/contact.hpp +231 -0
  28. netgen/include/diagonalmatrix.hpp +154 -0
  29. netgen/include/differentialoperator.hpp +276 -0
  30. netgen/include/diffop.hpp +1286 -0
  31. netgen/include/diffop_impl.hpp +326 -0
  32. netgen/include/discontinuous.hpp +84 -0
  33. netgen/include/dump.hpp +949 -0
  34. netgen/include/eigen.hpp +60 -0
  35. netgen/include/eigensystem.hpp +18 -0
  36. netgen/include/elasticity_equations.hpp +595 -0
  37. netgen/include/elementbyelement.hpp +195 -0
  38. netgen/include/elementtopology.hpp +1760 -0
  39. netgen/include/elementtransformation.hpp +339 -0
  40. netgen/include/evalfunc.hpp +405 -0
  41. netgen/include/expr.hpp +1655 -0
  42. netgen/include/facetfe.hpp +175 -0
  43. netgen/include/facetfespace.hpp +178 -0
  44. netgen/include/facethofe.hpp +111 -0
  45. netgen/include/facetsurffespace.hpp +112 -0
  46. netgen/include/fe_interfaces.hpp +32 -0
  47. netgen/include/fem.hpp +87 -0
  48. netgen/include/fesconvert.hpp +14 -0
  49. netgen/include/fespace.hpp +1445 -0
  50. netgen/include/finiteelement.hpp +286 -0
  51. netgen/include/globalinterfacespace.hpp +77 -0
  52. netgen/include/globalspace.hpp +115 -0
  53. netgen/include/gridfunction.hpp +525 -0
  54. netgen/include/h1amg.hpp +41 -0
  55. netgen/include/h1hofe.hpp +188 -0
  56. netgen/include/h1hofe_impl.hpp +1262 -0
  57. netgen/include/h1hofefo.hpp +148 -0
  58. netgen/include/h1hofefo_impl.hpp +185 -0
  59. netgen/include/h1hofespace.hpp +167 -0
  60. netgen/include/h1lofe.hpp +1237 -0
  61. netgen/include/h1lumping.hpp +35 -0
  62. netgen/include/hcurl_equations.hpp +1352 -0
  63. netgen/include/hcurlcurlfe.hpp +2221 -0
  64. netgen/include/hcurlcurlfespace.hpp +78 -0
  65. netgen/include/hcurlfe.hpp +259 -0
  66. netgen/include/hcurlfe_utils.hpp +107 -0
  67. netgen/include/hcurlhdiv_dshape.hpp +857 -0
  68. netgen/include/hcurlhdivfes.hpp +308 -0
  69. netgen/include/hcurlhofe.hpp +175 -0
  70. netgen/include/hcurlhofe_impl.hpp +1871 -0
  71. netgen/include/hcurlhofespace.hpp +193 -0
  72. netgen/include/hcurllofe.hpp +1146 -0
  73. netgen/include/hdiv_equations.hpp +865 -0
  74. netgen/include/hdivdivfe.hpp +2923 -0
  75. netgen/include/hdivdivsurfacespace.hpp +76 -0
  76. netgen/include/hdivfe.hpp +206 -0
  77. netgen/include/hdivfe_utils.hpp +716 -0
  78. netgen/include/hdivfes.hpp +75 -0
  79. netgen/include/hdivhofe.hpp +447 -0
  80. netgen/include/hdivhofe_impl.hpp +1107 -0
  81. netgen/include/hdivhofefo.hpp +229 -0
  82. netgen/include/hdivhofespace.hpp +175 -0
  83. netgen/include/hdivhosurfacefespace.hpp +106 -0
  84. netgen/include/hdivlofe.hpp +773 -0
  85. netgen/include/hidden.hpp +74 -0
  86. netgen/include/householder.hpp +181 -0
  87. netgen/include/hypre_ams_precond.hpp +123 -0
  88. netgen/include/hypre_precond.hpp +73 -0
  89. netgen/include/integrator.hpp +2024 -0
  90. netgen/include/integratorcf.hpp +253 -0
  91. netgen/include/interpolate.hpp +49 -0
  92. netgen/include/intrule.hpp +2541 -0
  93. netgen/include/irspace.hpp +49 -0
  94. netgen/include/jacobi.hpp +136 -0
  95. netgen/include/l2hofe.hpp +193 -0
  96. netgen/include/l2hofe_impl.hpp +564 -0
  97. netgen/include/l2hofefo.hpp +542 -0
  98. netgen/include/l2hofespace.hpp +344 -0
  99. netgen/include/la.hpp +38 -0
  100. netgen/include/linearform.hpp +266 -0
  101. netgen/include/matrix.hpp +2140 -0
  102. netgen/include/memusage.hpp +41 -0
  103. netgen/include/meshaccess.hpp +1358 -0
  104. netgen/include/mgpre.hpp +204 -0
  105. netgen/include/mptools.hpp +2187 -0
  106. netgen/include/multigrid.hpp +42 -0
  107. netgen/include/multivector.hpp +447 -0
  108. netgen/include/mumpsinverse.hpp +187 -0
  109. netgen/include/mycomplex.hpp +361 -0
  110. netgen/include/ng_lapack.hpp +1661 -0
  111. netgen/include/ngblas.hpp +1109 -0
  112. netgen/include/ngs_defines.hpp +30 -0
  113. netgen/include/ngs_stdcpp_include.hpp +106 -0
  114. netgen/include/ngs_utils.hpp +121 -0
  115. netgen/include/ngsobject.hpp +1019 -0
  116. netgen/include/ngsstream.hpp +113 -0
  117. netgen/include/ngstd.hpp +72 -0
  118. netgen/include/nodalhofe.hpp +96 -0
  119. netgen/include/nodalhofe_impl.hpp +141 -0
  120. netgen/include/normalfacetfe.hpp +223 -0
  121. netgen/include/normalfacetfespace.hpp +98 -0
  122. netgen/include/normalfacetsurfacefespace.hpp +84 -0
  123. netgen/include/order.hpp +251 -0
  124. netgen/include/parallel_matrices.hpp +222 -0
  125. netgen/include/paralleldofs.hpp +340 -0
  126. netgen/include/parallelngs.hpp +23 -0
  127. netgen/include/parallelvector.hpp +269 -0
  128. netgen/include/pardisoinverse.hpp +200 -0
  129. netgen/include/periodic.hpp +125 -0
  130. netgen/include/plateaufespace.hpp +25 -0
  131. netgen/include/pml.hpp +275 -0
  132. netgen/include/pmltrafo.hpp +631 -0
  133. netgen/include/postproc.hpp +142 -0
  134. netgen/include/precomp.hpp +60 -0
  135. netgen/include/preconditioner.hpp +602 -0
  136. netgen/include/prolongation.hpp +235 -0
  137. netgen/include/python_comp.hpp +107 -0
  138. netgen/include/python_fem.hpp +89 -0
  139. netgen/include/python_linalg.hpp +58 -0
  140. netgen/include/python_ngstd.hpp +385 -0
  141. netgen/include/recursive_pol.hpp +4844 -0
  142. netgen/include/recursive_pol_tet.hpp +395 -0
  143. netgen/include/recursive_pol_trig.hpp +492 -0
  144. netgen/include/reorderedfespace.hpp +81 -0
  145. netgen/include/sample_sort.hpp +105 -0
  146. netgen/include/scalarfe.hpp +335 -0
  147. netgen/include/shapefunction_utils.hpp +113 -0
  148. netgen/include/simd_complex.hpp +284 -0
  149. netgen/include/smoother.hpp +253 -0
  150. netgen/include/solve.hpp +89 -0
  151. netgen/include/sparsecholesky.hpp +313 -0
  152. netgen/include/sparsematrix.hpp +1038 -0
  153. netgen/include/sparsematrix_dyn.hpp +91 -0
  154. netgen/include/sparsematrix_impl.hpp +920 -0
  155. netgen/include/special_matrix.hpp +461 -0
  156. netgen/include/specialelement.hpp +125 -0
  157. netgen/include/statushandler.hpp +33 -0
  158. netgen/include/stringops.hpp +12 -0
  159. netgen/include/superluinverse.hpp +136 -0
  160. netgen/include/symbolicintegrator.hpp +849 -0
  161. netgen/include/symmetricmatrix.hpp +144 -0
  162. netgen/include/tangentialfacetfe.hpp +224 -0
  163. netgen/include/tangentialfacetfespace.hpp +106 -0
  164. netgen/include/tensor.hpp +522 -0
  165. netgen/include/tensorcoefficient.hpp +446 -0
  166. netgen/include/tensorproductintegrator.hpp +113 -0
  167. netgen/include/thcurlfe.hpp +128 -0
  168. netgen/include/thcurlfe_impl.hpp +380 -0
  169. netgen/include/thdivfe.hpp +80 -0
  170. netgen/include/thdivfe_impl.hpp +426 -0
  171. netgen/include/tpdiffop.hpp +461 -0
  172. netgen/include/tpfes.hpp +133 -0
  173. netgen/include/tpintrule.hpp +224 -0
  174. netgen/include/triangular.hpp +465 -0
  175. netgen/include/tscalarfe.hpp +245 -0
  176. netgen/include/tscalarfe_impl.hpp +1029 -0
  177. netgen/include/umfpackinverse.hpp +148 -0
  178. netgen/include/vector.hpp +1219 -0
  179. netgen/include/voxelcoefficientfunction.hpp +41 -0
  180. netgen/include/vtkoutput.hpp +198 -0
  181. netgen/include/vvector.hpp +208 -0
  182. netgen/include/webgui.hpp +92 -0
  183. netgen/libngbla.dylib +0 -0
  184. netgen/libngcomp.dylib +0 -0
  185. netgen/libngfem.dylib +0 -0
  186. netgen/libngla.dylib +0 -0
  187. netgen/libngsolve.dylib +0 -0
  188. netgen/libngstd.dylib +0 -0
  189. ngsolve/__init__.pyi +231 -0
  190. ngsolve/bla.pyi +1139 -0
  191. ngsolve/bvp.pyi +32 -0
  192. ngsolve/cmake/NGSolveConfig.cmake +102 -0
  193. ngsolve/cmake/ngsolve-targets-release.cmake +69 -0
  194. ngsolve/cmake/ngsolve-targets.cmake +163 -0
  195. ngsolve/comp/__init__.pyi +5382 -0
  196. ngsolve/comp/pml.pyi +89 -0
  197. ngsolve/config/__init__.py +1 -0
  198. ngsolve/config/__init__.pyi +43 -0
  199. ngsolve/config/__main__.py +4 -0
  200. ngsolve/config/config.py +60 -0
  201. ngsolve/config/config.pyi +45 -0
  202. ngsolve/demos/TensorProduct/__init__.py +0 -0
  203. ngsolve/demos/TensorProduct/tp_dg_1d_1d.py +80 -0
  204. ngsolve/demos/TensorProduct/tp_dg_1d_2d.py +73 -0
  205. ngsolve/demos/TensorProduct/tp_dg_2d_1d.py +72 -0
  206. ngsolve/demos/TensorProduct/tp_dg_2d_2d.py +66 -0
  207. ngsolve/demos/__init__.py +0 -0
  208. ngsolve/demos/howto/__init__.py +0 -0
  209. ngsolve/demos/howto/hhj.py +44 -0
  210. ngsolve/demos/howto/hybrid_dg.py +53 -0
  211. ngsolve/demos/howto/mixed.py +30 -0
  212. ngsolve/demos/howto/nonlin.py +29 -0
  213. ngsolve/demos/howto/pickling.py +26 -0
  214. ngsolve/demos/howto/pml.py +31 -0
  215. ngsolve/demos/howto/taskmanager.py +20 -0
  216. ngsolve/demos/howto/tdnns.py +47 -0
  217. ngsolve/demos/howto/timeDG-skeleton.py +45 -0
  218. ngsolve/demos/howto/timeDG.py +38 -0
  219. ngsolve/demos/howto/timeDGlap.py +42 -0
  220. ngsolve/demos/howto/timeDGwave.py +61 -0
  221. ngsolve/demos/intro/__init__.py +0 -0
  222. ngsolve/demos/intro/adaptive.py +123 -0
  223. ngsolve/demos/intro/cmagnet.py +62 -0
  224. ngsolve/demos/intro/elasticity.py +76 -0
  225. ngsolve/demos/intro/navierstokes.py +74 -0
  226. ngsolve/demos/intro/poisson.ipynb +170 -0
  227. ngsolve/demos/intro/poisson.py +41 -0
  228. ngsolve/demos/mpi/__init__.py +0 -0
  229. ngsolve/demos/mpi/mpi_cmagnet.py +87 -0
  230. ngsolve/demos/mpi/mpi_navierstokes.py +117 -0
  231. ngsolve/demos/mpi/mpi_poisson.py +89 -0
  232. ngsolve/demos/mpi/mpi_timeDG.py +82 -0
  233. ngsolve/directsolvers.pyi +18 -0
  234. ngsolve/eigenvalues.pyi +30 -0
  235. ngsolve/fem.pyi +1707 -0
  236. ngsolve/krylovspace.pyi +309 -0
  237. ngsolve/la.pyi +1218 -0
  238. ngsolve/ngslib.so +0 -0
  239. ngsolve/ngstd.pyi +58 -0
  240. ngsolve/nonlinearsolvers.pyi +98 -0
  241. ngsolve/preconditioners.pyi +6 -0
  242. ngsolve/solve.pyi +108 -0
  243. ngsolve/solvers.pyi +14 -0
  244. ngsolve/timestepping.pyi +34 -0
  245. ngsolve/timing.pyi +57 -0
  246. ngsolve/utils.pyi +279 -0
  247. ngsolve-6.2.2501.post55.dev1.data/data/Netgen.icns +0 -0
  248. ngsolve-6.2.2501.post55.dev1.data/data/bin/ngscxx +17 -0
  249. ngsolve-6.2.2501.post55.dev1.data/data/bin/ngsld +13 -0
  250. ngsolve-6.2.2501.post55.dev1.data/data/bin/ngsolve.tcl +648 -0
  251. ngsolve-6.2.2501.post55.dev1.data/data/bin/ngspy +2 -0
  252. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/beam.geo +17 -0
  253. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/beam.vol +240 -0
  254. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/chip.in2d +41 -0
  255. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/chip.vol +614 -0
  256. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/coil.geo +12 -0
  257. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/coil.vol +2560 -0
  258. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/coilshield.geo +24 -0
  259. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/coilshield.vol +3179 -0
  260. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/cube.geo +19 -0
  261. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/cube.vol +1832 -0
  262. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d10_DGdoubleglazing.pde +50 -0
  263. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d11_chip_nitsche.pde +40 -0
  264. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d1_square.pde +43 -0
  265. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d2_chip.pde +35 -0
  266. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d3_helmholtz.pde +22 -0
  267. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d4_cube.pde +46 -0
  268. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d5_beam.pde +74 -0
  269. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d6_shaft.pde +73 -0
  270. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d7_coil.pde +50 -0
  271. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d8_coilshield.pde +49 -0
  272. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/d9_hybridDG.pde +72 -0
  273. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/doubleglazing.in2d +27 -0
  274. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/doubleglazing.vol +737 -0
  275. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/piezo2d40round4.vol.gz +0 -0
  276. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/shaft.geo +73 -0
  277. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/shaft.vol +4291 -0
  278. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/square.in2d +17 -0
  279. ngsolve-6.2.2501.post55.dev1.data/data/share/ngsolve/square.vol +149 -0
  280. {ngsolve-6.2.2501.post47.dev1.dist-info → ngsolve-6.2.2501.post55.dev1.dist-info}/METADATA +2 -2
  281. ngsolve-6.2.2501.post55.dev1.dist-info/RECORD +304 -0
  282. ngsolve-6.2.2501.post47.dev1.dist-info/RECORD +0 -25
  283. {ngsolve-6.2.2501.post47.dev1.dist-info → ngsolve-6.2.2501.post55.dev1.dist-info}/LICENSE +0 -0
  284. {ngsolve-6.2.2501.post47.dev1.dist-info → ngsolve-6.2.2501.post55.dev1.dist-info}/WHEEL +0 -0
  285. {ngsolve-6.2.2501.post47.dev1.dist-info → ngsolve-6.2.2501.post55.dev1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,2187 @@
1
+ #ifndef FILE_MPTOOLS
2
+ #define FILE_MPTOOLS
3
+
4
+ /*
5
+ tools for computing with spherical harmonics and multi-poles
6
+ */
7
+
8
+
9
+ #include <bla.hpp>
10
+ #include <coefficient.hpp>
11
+
12
+
13
+ namespace ngfem
14
+ {
15
+
16
+
17
+
18
+ class SphericalHarmonics
19
+ {
20
+ int order;
21
+ Vector<Complex> coefs;
22
+
23
+ public:
24
+ SphericalHarmonics (int aorder)
25
+ : order(aorder), coefs(sqr(order+1)) { coefs=0.0; }
26
+
27
+ int Order() const { return order; }
28
+ FlatVector<Complex> Coefs() const { return coefs; }
29
+
30
+ Complex & Coef(int n, int m)
31
+ {
32
+ return coefs(n*(n+1) + m);
33
+ }
34
+
35
+ auto CoefsN (int n) const
36
+ {
37
+ return FlatVector<Complex> (2*n+1, &coefs(n*(n+1)-n));
38
+ }
39
+
40
+ auto Polar (Vec<3> x) const
41
+ {
42
+ double phi, theta;
43
+ if (x(0) == 0 && x(1) == 0)
44
+ {
45
+ phi = 0;
46
+ theta = x(2) > 0 ? 0 : M_PI;
47
+ }
48
+ else
49
+ {
50
+ phi = atan2(x(1), x(0));
51
+ theta = acos(x(2)/L2Norm(x));
52
+ }
53
+ return tuple{theta, phi};
54
+ }
55
+
56
+ Complex Eval (Vec<3> x) const
57
+ {
58
+ auto [theta, phi] = Polar(x);
59
+ return Eval(theta, phi);
60
+ }
61
+
62
+ Complex Eval (double theta, double phi) const
63
+ {
64
+ static Timer t("mptool sh evaluate"); RegionTimer rg(t);
65
+
66
+ Matrix legfunc(order+1, order+1);
67
+ NormalizedLegendreFunctions (order, order, cos(theta), legfunc);
68
+ Vector<Complex> exp_imphi(order+1);
69
+ Complex exp_iphi(cos(phi), sin(phi));
70
+ Complex prod = 1.0;
71
+ for (int i = 0; i <= order; i++)
72
+ {
73
+ exp_imphi(i) = prod;
74
+ prod *= -exp_iphi;
75
+ }
76
+
77
+ Complex sum = 0.0;
78
+ int ii = 0;
79
+ for (int n = 0; n <= order; n++)
80
+ {
81
+ for (int m = -n; m < 0; m++, ii++)
82
+ sum += coefs(ii) * conj(exp_imphi(-m)) * legfunc(-m, n);
83
+ for (int m = 0; m <= n; m++, ii++)
84
+ sum += coefs(ii) * exp_imphi(m) * legfunc(m, n);
85
+ }
86
+
87
+ sum /= sqrt(4*M_PI);
88
+ return sum;
89
+ }
90
+
91
+
92
+
93
+ Complex EvalOrder (int n, Vec<3> x) const
94
+ {
95
+ auto [theta, phi] = Polar (x);
96
+ return EvalOrder(n, theta, phi);
97
+ }
98
+
99
+ Complex EvalOrder (int n, double theta, double phi) const
100
+ {
101
+ static Timer t("mptool sh evalorder");
102
+
103
+ RegionTimer rg(t);
104
+
105
+ Matrix legfunc(order+1, order+1);
106
+ NormalizedLegendreFunctions (order, order, cos(theta), legfunc);
107
+ Vector<Complex> exp_imphi(order+1);
108
+ Complex exp_iphi(cos(phi), sin(phi));
109
+ Complex prod = 1.0;
110
+ for (int i = 0; i <= order; i++)
111
+ {
112
+ exp_imphi(i) = prod;
113
+ prod *= -exp_iphi;
114
+ }
115
+
116
+ Complex sum = 0.0;
117
+ auto coefsn = CoefsN(n);
118
+ int ii = 0;
119
+ // for (int n = 0; n <= order; n++)
120
+ {
121
+ for (int m = -n; m < 0; m++, ii++)
122
+ sum += coefsn(ii) * conj(exp_imphi(-m)) * legfunc(-m, n);
123
+ for (int m = 0; m <= n; m++, ii++)
124
+ sum += coefsn(ii) * exp_imphi(m) * legfunc(m, n);
125
+ }
126
+
127
+ sum /= sqrt(4*M_PI);
128
+ return sum;
129
+ }
130
+
131
+
132
+
133
+ void EvalOrders (Vec<3> x, FlatVector<Complex> vals) const
134
+ {
135
+ auto [theta, phi] = Polar(x);
136
+ return EvalOrders(theta, phi, vals);
137
+ }
138
+
139
+ void EvalOrders (double theta, double phi, FlatVector<Complex> vals) const
140
+ {
141
+ static Timer ts("mptool sh evalorders small");
142
+ static Timer tl("mptool sh evalorders large");
143
+ // RegionTimer rg(order < 30 ? ts : tl);
144
+ // if (order > 30) tl.Start();
145
+
146
+ Matrix legfunc(order+1, order+1);
147
+ NormalizedLegendreFunctions (order, order, cos(theta), legfunc);
148
+ Vector<Complex> exp_imphi(order+1);
149
+ Complex exp_iphi(cos(phi), sin(phi));
150
+ Complex prod = 1.0;
151
+ for (int i = 0; i <= order; i++)
152
+ {
153
+ exp_imphi(i) = prod;
154
+ prod *= -exp_iphi;
155
+ }
156
+
157
+
158
+ for (int n = 0; n <= order; n++)
159
+ {
160
+ auto coefsn = CoefsN(n);
161
+ int ii = 0;
162
+
163
+ Complex sum = 0.0;
164
+ for (int m = -n; m < 0; m++, ii++)
165
+ sum += coefsn(ii) * conj(exp_imphi(-m)) * legfunc(-m, n);
166
+ for (int m = 0; m <= n; m++, ii++)
167
+ sum += coefsn(ii) * exp_imphi(m) * legfunc(m, n);
168
+ vals(n) = sum;
169
+ }
170
+
171
+ vals /= sqrt(4*M_PI);
172
+
173
+ // if (order > 30) tl.Stop();
174
+ }
175
+
176
+
177
+ void Calc (Vec<3> x, FlatVector<Complex> shapes)
178
+ {
179
+ auto [theta, phi] = Polar(x);
180
+
181
+ Matrix legfunc(order+1, order+1);
182
+ NormalizedLegendreFunctions (order, order, cos(theta), legfunc);
183
+ Vector<Complex> exp_imphi(order+1);
184
+ Complex exp_iphi(cos(phi), sin(phi));
185
+ Complex prod = 1.0;
186
+ for (int i = 0; i <= order; i++)
187
+ {
188
+ exp_imphi(i) = prod;
189
+ prod *= -exp_iphi;
190
+ }
191
+
192
+ int ii = 0;
193
+ for (int n = 0; n <= order; n++)
194
+ {
195
+ for (int m = -n; m < 0; m++, ii++)
196
+ shapes(ii) = conj(exp_imphi(-m)) * legfunc(-m, n);
197
+ for (int m = 0; m <= n; m++, ii++)
198
+ shapes(ii) = exp_imphi(m) * legfunc(m, n);
199
+ }
200
+
201
+ shapes /= sqrt(4*M_PI);
202
+ }
203
+
204
+
205
+ void RotateZ (double alpha)
206
+ {
207
+ // static Timer t("mptool sh RotateZ"); RegionTimer rg(t);
208
+ if (order < 0) return;
209
+
210
+ Vector<Complex> exp_imalpha(order+1);
211
+ Complex exp_ialpha(cos(alpha), sin(alpha));
212
+ Complex prod = 1.0;
213
+ for (int i = 0; i <= order; i++)
214
+ {
215
+ exp_imalpha(i) = prod;
216
+ prod *= exp_ialpha;
217
+ }
218
+
219
+ int ii = 0;
220
+ for (int n = 0; n <= order; n++)
221
+ {
222
+ for (int m = -n; m < 0; m++, ii++)
223
+ coefs(ii) *= conj(exp_imalpha(-m));
224
+ for (int m = 0; m <= n; m++, ii++)
225
+ coefs(ii) *= exp_imalpha(m);
226
+ }
227
+ }
228
+
229
+
230
+
231
+
232
+ static double CalcAmn (int m, int n)
233
+ {
234
+ if (m < 0) m=-m;
235
+ if (n < m) return 0;
236
+ return sqrt( (n+1.0+m)*(n+1.0-m) / ( (2*n+1)*(2*n+3) ));
237
+ }
238
+
239
+ static double CalcBmn (int m, int n)
240
+ {
241
+ double sgn = (m >= 0) ? 1 : -1;
242
+ if ( (m > n) || (-m > n) ) return 0;
243
+ return sgn * sqrt( (n-m-1.0)*(n-m) / ( (2*n-1.0)*(2*n+1)));
244
+ }
245
+
246
+ static double CalcDmn (int m, int n)
247
+ {
248
+ double sgn = (m >= 0) ? 1 : -1;
249
+ return sgn/2 * sqrt((n-m)*(n+m+1));
250
+ }
251
+
252
+ // Nail A. Gumerov and Ramani Duraiswami book, formula (2.2.12)
253
+ // add directional derivative divided by kappa to res, both multipoles need same scaling
254
+ void DirectionalDiffAdd (Vec<3> d, SphericalHarmonics & res, double scale = 1)
255
+ {
256
+ double fx = d(0);
257
+ double fy = d(1);
258
+ double fz = d(2);
259
+ double invscale = 1./scale;
260
+
261
+ for (int n = 0; n < order; n++)
262
+ for (int m = -n; m <= n; m++)
263
+ {
264
+ double amn = CalcAmn(m,n);
265
+ double bmn1 = CalcBmn(-m-1, n+1);
266
+ double bmn2 = CalcBmn(m-1,n+1);
267
+
268
+ res.Coef(n+1, m-1) += invscale * Complex(0.5*fx,0.5*fy)*bmn2 * Coef(n,m);
269
+ res.Coef(n+1, m ) -= invscale * fz*amn * Coef(n,m);
270
+ res.Coef(n+1, m+1) += invscale * Complex(0.5*fx,-0.5*fy)*bmn1 * Coef(n,m);
271
+
272
+ res.Coef(n, m) += scale * Complex(-0.5*fx,0.5*fy)*bmn2 * Coef(n+1,m-1);
273
+ res.Coef(n, m) += scale * fz*amn * Coef(n+1,m);
274
+ res.Coef(n, m) += scale * Complex(-0.5*fx,-0.5*fy)*bmn1 * Coef(n+1,m+1);
275
+ }
276
+ }
277
+
278
+ void RotateY (double alpha)
279
+ {
280
+ LocalHeap lh(8*6*sqr(order) + 8*15*order + 500);
281
+
282
+ static Timer t("mptool sh RotateY"); RegionTimer rg(t);
283
+ /*
284
+ static std::map<int, unique_ptr<Timer<>>> timers;
285
+ static std::map<int, unique_ptr<Timer<>>> timerstrafo;
286
+ if (timers.find(order)==timers.end())
287
+ {
288
+ timers[order] = make_unique<Timer<>> (string("mptools sh RotateY ")+ToString(order));
289
+ timerstrafo[order] = make_unique<Timer<>> (string("mptools sh RotateY trafo ")+ToString(order));
290
+ }
291
+ RegionTimer rg(*timers[order]);
292
+ */
293
+ double s = sin(alpha);
294
+ double c = cos(alpha);
295
+
296
+ FlatMatrix<> normalized_leg_func(order+2, order+2, lh);
297
+ NormalizedLegendreFunctions(order+1, order+1, c, normalized_leg_func);
298
+
299
+ if (alpha < 0)
300
+ for (int i = 1; i <= order+1; i+=2)
301
+ normalized_leg_func.Row(i) *= -1;
302
+
303
+ // cout << "leg = " << endl << normalized_leg_func << endl;
304
+ FlatVector<> Dmn(2*order+1, lh);
305
+
306
+ for (int n=1; n <= order; n++)
307
+ {
308
+ HeapReset hr(lh);
309
+
310
+ FlatMatrix<double> trafo(n+1, 2*n+1, lh);
311
+ /*
312
+ Recursive Computation of Spherical Harmonic Rotation Coefficients of Large Degree
313
+ Nail A. Gumerov and Ramani Duraiswami
314
+ within Excursions in Harmonic Analysis, Volume 3
315
+
316
+ page 130
317
+ */
318
+
319
+ // Step 2
320
+ // H(0,m)
321
+ trafo.Col(n) = 1.0/sqrt(2*n+1) * normalized_leg_func.Col(n).Range(n+1);
322
+ for (int m = 1; m <= n; m += 2) trafo(m,n) *= -1;
323
+ // Step 3
324
+ // H(1,m)
325
+ FlatVector<double> tmp = 1.0/sqrt(2*n+3) * normalized_leg_func.Col(n+1).Range(n+2) | lh;
326
+ for (int m = 1; m < tmp.Size(); m += 2) tmp(m) *= -1;
327
+ for (int m = 1; m <= n; m++)
328
+ trafo.Col(n+1)(m) = 1/CalcBmn(0,n+1) * ( CalcBmn(-m-1, n+1)*(1-c)/2 * tmp(m+1)
329
+ - CalcBmn(m-1,n+1)*(1+c)/2 * tmp(m-1)
330
+ - CalcAmn(m,n) * s*tmp(m));
331
+
332
+ // Step 4
333
+ // diamond - recursion
334
+ for (int mp = -n; mp <= n; mp++)
335
+ Dmn(order+mp) = CalcDmn(mp, n);
336
+
337
+ for (int mp = 1; mp < n; mp++)
338
+ {
339
+ double invDmn = 1.0 / Dmn(order+mp);
340
+ for (int m = mp; m < n; m++)
341
+ trafo(m, n+mp+1) = invDmn * ( Dmn(order+mp-1) *trafo(m ,n+mp-1)
342
+ -Dmn(order+m-1)*trafo(m-1,n+mp)
343
+ +Dmn(order+m) *trafo(m+1,n+mp));
344
+ int m = n;
345
+ trafo(m, n+mp+1) = invDmn * ( Dmn(order+mp-1,n)*trafo(m ,n+mp-1)
346
+ -Dmn(order+m-1,n)*trafo(m-1,n+mp));
347
+ }
348
+
349
+ // Step 5
350
+ // diamond - recursion, negative
351
+ for (int mp = 0; mp > -n; mp--)
352
+ {
353
+ double invDmn = 1.0 / Dmn(order+mp-1);
354
+ for (int m = -mp+1; m < n; m++)
355
+ trafo(m, n+mp-1) = invDmn * ( Dmn(order+mp,n)*trafo(m ,n+mp+1)
356
+ +Dmn(order+m-1,n)*trafo(m-1,n+mp)
357
+ -Dmn(order+m ,n)*trafo(m+1,n+mp));
358
+ int m = n;
359
+ trafo(m, n+mp-1) = invDmn * ( Dmn(order+mp,n)*trafo(m ,n+mp+1)
360
+ +Dmn(order+m-1,n)*trafo(m-1,n+mp));
361
+ }
362
+
363
+ // RegionTimer rgtrafo(*timerstrafo[order]);
364
+ // Step 6
365
+ // symmetries in m and mp
366
+ for (int m = 0; m <= n; m++)
367
+ {
368
+ auto dst = trafo.Row(m).Range(n+m, n+n+1);
369
+ auto src = trafo.Col(n+m).Range(m, n+1);
370
+ dst = src;
371
+ }
372
+ for (int m = 0; m <= n; m++)
373
+ {
374
+ auto dst = trafo.Row(m).Range(n-n, n-m+1).Reversed();
375
+ auto src = trafo.Col(n-m).Range(m, n+1);
376
+ dst = src;
377
+ }
378
+ /*
379
+ double errortho = L2Norm( Matrix(trafo*Trans(trafo) - Identity(n+1)));
380
+ if (errortho > 1e-10)
381
+ {
382
+ *testout << "n = " << n << " order = " << Order() << ", alpha = " << alpha << ", errortho = " << errortho << endl;
383
+ if (n < 10)
384
+ *testout << trafo*Trans(trafo) << endl;
385
+ }
386
+ */
387
+
388
+ FlatVector<Complex> cn = CoefsN(n);
389
+ FlatVector<Complex> old = cn | lh;
390
+
391
+ cn.Slice(0,1) = Trans(trafo) * old.Range(n, 2*n+1);
392
+ cn.Slice(0,1).Reversed() += Trans(trafo.Rows(1,n+1)) * old.Range(0,n).Reversed();
393
+
394
+ for (int m = 1; m <= n; m+=2)
395
+ {
396
+ cn(n+m) *= -1;
397
+ cn(n-m) *= -1;
398
+ }
399
+ }
400
+ }
401
+
402
+ };
403
+
404
+
405
+ // https://fortran-lang.discourse.group/t/looking-for-spherical-bessel-and-hankel-functions-of-first-and-second-kind-and-arbitrary-order/2308/2
406
+
407
+ // adapted from fmm3d
408
+ template <typename Tz>
409
+ void besseljs3d (int nterms, Tz z, double scale,
410
+ FlatVector<Tz> fjs, FlatVector<Tz> fjder)
411
+ {
412
+ /*
413
+ c**********************************************************************
414
+ c
415
+ c PURPOSE:
416
+ c
417
+ c This subroutine evaluates the first NTERMS spherical Bessel
418
+ c functions and if required, their derivatives.
419
+ c It incorporates a scaling parameter SCALE so that
420
+ c
421
+ c fjs_n(z)=j_n(z)/SCALE^n
422
+ c fjder_n(z)=\frac{\partial fjs_n(z)}{\partial z}
423
+ c
424
+ c INPUT:
425
+ c
426
+ c nterms (integer): order of expansion of output array fjs
427
+ c z (complex *16): argument of the spherical Bessel functions
428
+ c scale (real *8) : scaling factor (discussed above)
429
+ c ifder (integer): flag indicating whether to calculate "fjder"
430
+ c 0 NO
431
+ c 1 YES
432
+ c OUTPUT:
433
+ c fjs (complex *16): array of scaled Bessel functions.
434
+ c fjder (complex *16): array of derivs of scaled Bessel functions.
435
+ c
436
+ c
437
+ */
438
+
439
+
440
+ // c ... Initializing ...
441
+
442
+ // set to asymptotic values if argument is sufficiently small
443
+ if (abs(z) < 1e-200)
444
+ {
445
+ fjs(0) = 1;
446
+ for (int i = 1; i <= nterms; i++)
447
+ fjs(i) = 0.0;
448
+
449
+ if (fjder.Size())
450
+ {
451
+ fjder = 0.0;
452
+ fjder(1) = 1.0/(3*scale);
453
+ }
454
+ return;
455
+ }
456
+
457
+
458
+ // ... Step 1: recursion up to find ntop, starting from nterms
459
+
460
+ Tz zinv=1.0/z;
461
+
462
+ Tz fjm1 = 0.0;
463
+ Tz fj0 = 1.0;
464
+
465
+ /*
466
+ c
467
+ cc note max point for upward recurrence is
468
+ c hard coded to nterms + 1000,
469
+ c this might cause loss of accuracy for some
470
+ c arguments in the complex plane for large
471
+ c nterms. For example, it is a terrible idea
472
+ c to use this code for z>>nterms^2
473
+ */
474
+
475
+ // int lwfjs = nterms + 100000;
476
+ int ntop = nterms+1000;
477
+
478
+ for (int i = nterms; ; i++)
479
+ {
480
+ double dcoef = 2*i+1.0;
481
+ Tz fj1 = dcoef*zinv*fj0-fjm1;
482
+ double dd = sqr(abs(fj1));
483
+ if (dd > 1e40)
484
+ {
485
+ ntop=i+1;
486
+ break;
487
+ }
488
+ fjm1 = fj0;
489
+ fj0 = fj1;
490
+ if (i > nterms+100000)
491
+ throw Exception("bessel failed 1");
492
+ }
493
+
494
+ Array<bool> iscale(ntop+1);
495
+ Vector<Tz> fjtmp(ntop+1);
496
+
497
+ /*
498
+ c ... Step 2: Recursion back down to generate the unscaled jfuns:
499
+ c if magnitude exceeds UPBOUND2, rescale and continue the
500
+ c recursion (saving the order at which rescaling occurred
501
+ c in array iscale.
502
+ */
503
+
504
+ iscale = false;
505
+
506
+ fjtmp(ntop) = 0.0;
507
+ fjtmp(ntop-1) = 1.0;
508
+ for (int i = ntop-1; i>=1; i--)
509
+ {
510
+ double dcoef = 2*i+1.0;
511
+ fjtmp(i-1) = dcoef*zinv*fjtmp(i)-fjtmp(i+1);
512
+ double dd = sqr(abs(fjtmp(i-1)));
513
+ if (dd > 1e40)
514
+ {
515
+ fjtmp(i) *= 1e-40;
516
+ fjtmp(i-1) *= 1e-40;
517
+ iscale[i] = true;
518
+ }
519
+ }
520
+
521
+ /*
522
+ c
523
+ c ... Step 3: go back up to the top and make sure that all
524
+ c Bessel functions are scaled by the same factor
525
+ c (i.e. the net total of times rescaling was invoked
526
+ c on the way down in the previous loop).
527
+ c At the same time, add scaling to fjs array.
528
+ c
529
+ */
530
+
531
+ double scalinv = 1.0/scale;
532
+ double sctot = 1.0;
533
+ for (int i = 1; i <= ntop; i++)
534
+ {
535
+ sctot *= scalinv;
536
+ if (iscale[i-1])
537
+ sctot *= 1e-40;
538
+ fjtmp(i) *= sctot;
539
+ }
540
+
541
+ // Determine the normalization parameter:
542
+
543
+ fj0=sin(z)*zinv;
544
+ Tz fj1=fj0*zinv-cos(z)*zinv;
545
+
546
+ double d0=abs(fj0);
547
+ double d1=abs(fj1);
548
+ Tz zscale;
549
+ if (d1 > d0)
550
+ zscale=fj1/(fjtmp(1)*scale);
551
+ else
552
+ zscale=fj0/fjtmp(0);
553
+
554
+ // Scale the jfuns by zscale:
555
+
556
+ Tz ztmp=zscale;
557
+ for (int i = 0; i <= nterms; i++)
558
+ fjs(i)=fjtmp(i)*ztmp;
559
+
560
+
561
+ // Finally, calculate the derivatives if desired:
562
+
563
+ if (fjder.Size())
564
+ {
565
+ fjder(0) = -fjs(1)*scale;
566
+ for (int i = 1; i <= nterms; i++)
567
+ {
568
+ double dc1=i/(2*i+1.0);
569
+ double dc2=1.0-dc1;
570
+ dc1=dc1*scalinv;
571
+ dc2=dc2*scale;
572
+ fjder(i)=(dc1*fjtmp(i-1)-dc2*fjtmp(i+1))*ztmp;
573
+ }
574
+ }
575
+ }
576
+
577
+
578
+
579
+
580
+ /*
581
+ // from A. Barnett
582
+ // http://www.fresco.org.uk/functions/barnett/index.htm
583
+
584
+ spherical bessel functions of first (the j_n) and second (the y_n) kind.
585
+
586
+ j0(r) = sin(r)/r
587
+ j1(r) = (sin(r)-r cos(r)) / r**2
588
+
589
+ y0(r) = -cos(r)/r
590
+ y1(r) = (-cos(r)-r*sin(r)) / r**2
591
+ */
592
+ void SBESJY (double x, int lmax,
593
+ FlatVector<double> j,
594
+ FlatVector<double> y,
595
+ FlatVector<double> jp,
596
+ FlatVector<double> yp)
597
+ {
598
+ if (x < 1e-8)
599
+ {
600
+ cout << "TODO: special treatment for small x" << endl;
601
+ return;
602
+ }
603
+
604
+ double xinv = 1/x;
605
+
606
+ if (lmax > 0)
607
+ {
608
+ double twoxi = 2*xinv;
609
+ double sl = lmax*xinv;
610
+ double tk = 2*sl+3*xinv;
611
+ double cf1 = sl;
612
+ double den = 1;
613
+ if (abs(cf1) < 1e-8) cf1 = 1e-8;
614
+ double c = cf1;
615
+ double d = 0;
616
+ for (int l = 1; l < 10000; l++)
617
+ {
618
+ c = tk-1/c;
619
+ d = tk-d;
620
+ if (abs(c) < 1e-8) c = 1e-8;
621
+ if (abs(d) < 1e-8) d = 1e-8;
622
+ d = 1/d;
623
+ double dcf1 = d*c;
624
+ cf1 *= dcf1;
625
+ if (d < 0) den = -den;
626
+ if (abs(dcf1-1) < 1e-10)
627
+ break;
628
+ tk += twoxi;
629
+ // nfp = l;
630
+ }
631
+
632
+ j(lmax) = den;
633
+ jp(lmax) = cf1*den;
634
+ for (int l = lmax; l >= 1; l--)
635
+ {
636
+ j(l-1) = (sl+xinv)*j(l) + jp(l);
637
+ sl = sl-xinv;
638
+ jp(l-1) = sl*j(l-1) - j(l);
639
+ }
640
+ }
641
+
642
+ double j0 = j(0);
643
+ double jp0 = jp(0);
644
+
645
+ // C------ CALCULATE THE L=0 SPHERICAL BESSEL FUNCTIONS DIRECTLY
646
+ j(0) = xinv * sin(x);
647
+ y(0) = -xinv * cos(x);
648
+ jp(0) = -y(0)-xinv*j(0);
649
+ yp(0) = j(0)-xinv*y(0);
650
+
651
+ double omega = (abs(j0)>abs(jp0)) ? j(0)/j0 : jp(0) / jp0; // fix for x \approx 2 pi
652
+ double sl = 0;
653
+ for (int l = 1; l <=lmax; l++)
654
+ {
655
+ j(l) *= omega;
656
+ jp(l) *= omega;
657
+ y(l) = sl*y(l-1) - yp(l-1);
658
+ sl += xinv;
659
+ yp(l) = y(l-1) - (sl+xinv)*y(l);
660
+ }
661
+ }
662
+
663
+
664
+
665
+ template <typename T>
666
+ void SphericalBessel (int n, double rho, double scale, T && values)
667
+ {
668
+ Vector<double> j(n+1), jp(n+1);
669
+ besseljs3d<double> (n, rho, scale, j, jp);
670
+ values = j;
671
+ }
672
+
673
+
674
+ template <typename T>
675
+ void SphericalHankel1 (int n, double rho, double scale, T && values)
676
+ {
677
+ // Complex imag(0,1);
678
+ /*
679
+ if (n >= 0)
680
+ values(0) = exp(imag*rho) / (imag*rho);
681
+ if (n >= 1)
682
+ values(1) = -imag*values(0)*(1.0-1.0/(imag*rho));
683
+
684
+ for (int i = 2; i <= n; i++)
685
+ values(i) = (2*i-1)/rho * values(i-1) - values(i-2);
686
+ */
687
+
688
+ if (rho < 1e-8)
689
+ {
690
+ values = Complex(0);
691
+ return;
692
+ }
693
+ Vector j(n+1), y(n+1), jp(n+1), yp(n+1);
694
+ SBESJY (rho, n, j, y, jp, yp);
695
+
696
+ values = j + Complex(0,1) * y;
697
+ if (scale != 1.0)
698
+ {
699
+ double prod = 1.0;
700
+ for (int i = 0; i <= n; i++)
701
+ {
702
+ values(i) *= prod;
703
+ prod *= scale;
704
+ }
705
+ }
706
+ }
707
+
708
+
709
+
710
+
711
+
712
+ // hn1 = jn+ i*yn
713
+ class MPSingular
714
+ {
715
+ public:
716
+ template <typename T>
717
+ static void Eval (int order, double r, double scale, T && values)
718
+ {
719
+ SphericalHankel1(order, r, scale, values);
720
+ }
721
+ };
722
+
723
+ // jn
724
+ class MPRegular
725
+ {
726
+ public:
727
+ template <typename T>
728
+ static void Eval (int order, double r, double scale, T && values)
729
+ {
730
+ SphericalBessel (order, r, 1.0/scale, values);
731
+ }
732
+ };
733
+
734
+
735
+
736
+
737
+ template <typename RADIAL>
738
+ class MultiPole
739
+ {
740
+ SphericalHarmonics sh;
741
+ double kappa;
742
+ double scale;
743
+ public:
744
+ MultiPole (int aorder, double akappa, double ascale = 1)
745
+ : sh(aorder), kappa(akappa), scale(ascale) { }
746
+
747
+ Complex & Coef(int n, int m) { return sh.Coef(n,m); }
748
+ auto & SH() { return sh; }
749
+ const auto & SH() const { return sh; }
750
+ double Kappa() const { return kappa; }
751
+ double Scale() const { return scale; }
752
+ int Order() const { return sh.Order(); }
753
+
754
+ MultiPole<RADIAL> Truncate(int neworder) const
755
+ {
756
+ if (neworder > sh.Order()) neworder=sh.Order();
757
+ MultiPole nmp(neworder, kappa);
758
+ nmp.sh.Coefs() = sh.Coefs().Range(sqr(neworder+1));
759
+ return nmp;
760
+ }
761
+
762
+ MultiPole & operator+= (const MultiPole & mp2)
763
+ {
764
+ size_t commonsize = min(SH().Coefs().Size(), mp2.SH().Coefs().Size());
765
+ SH().Coefs().Range(commonsize) += mp2.SH().Coefs().Range(commonsize);
766
+ return *this;
767
+ }
768
+
769
+ Complex Eval (Vec<3> x) const
770
+ {
771
+ if (sh.Order() < 0) return 0;
772
+
773
+ Vector<Complex> radial(sh.Order()+1);
774
+ Vector<Complex> shvals(sh.Order()+1);
775
+
776
+ RADIAL::Eval(sh.Order(), kappa*L2Norm(x), scale, radial);
777
+ sh.EvalOrders (x, shvals);
778
+
779
+ Complex sum = 0;
780
+ for (int i = 0; i <= sh.Order(); i++)
781
+ sum += shvals(i) * radial(i);
782
+
783
+ return sum;
784
+ }
785
+
786
+ void AddCharge (Vec<3> x, Complex c)
787
+ {
788
+ if constexpr (!std::is_same<RADIAL,MPSingular>())
789
+ throw Exception("AddCharge assumes singular MP");
790
+
791
+ // static Timer t("mptool AddCharge"); RegionTimer rg(t);
792
+
793
+ if (L2Norm(x) < 1e-10)
794
+ {
795
+ sh.Coef(0,0) += c * Complex(0,1)*kappa/sqrt(4*M_PI);
796
+ return;
797
+ }
798
+
799
+ // cout << "add charge, kappa rho = " << kappa*L2Norm(x) << ", order = " << sh.Order() << endl;
800
+
801
+ Vector<Complex> radial(sh.Order()+1);
802
+ Vector<Complex> sh_shapes(sqr (sh.Order()+1));
803
+
804
+ RADIAL::Eval(sh.Order(), kappa*L2Norm(x), 1.0/scale, radial);
805
+ // cout << "radial = " << radial << endl;
806
+ sh.Calc(x, sh_shapes);
807
+
808
+ for (int i = 0; i <= sh.Order(); i++)
809
+ {
810
+ IntRange r(sqr(i), sqr(i+1));
811
+ sh.Coefs().Range(r) += c * Complex(0,1)*kappa * radial(i).real()*Conj(sh_shapes.Range(r));
812
+ }
813
+ }
814
+
815
+
816
+ void AddDipole (Vec<3> x, Vec<3> d, Complex c)
817
+ {
818
+ // static Timer t("mptool AddDipole"); RegionTimer rg(t);
819
+ /*
820
+ double eps = 1e-4;
821
+ AddCharge(x+eps*d, -c/(2*eps));
822
+ AddCharge(x-eps*d, c/(2*eps));
823
+ return;
824
+ */
825
+
826
+ if constexpr (!std::is_same<RADIAL,MPSingular>())
827
+ throw Exception("AddCharge assumes singular MP");
828
+
829
+ /*
830
+ // book, formula (2.2.20)
831
+ // dipole in origin:
832
+ MultiPole<MPSingular> tmp(1, kappa);
833
+ tmp.SH().Coef(1,1) += Complex(0,1)*sqr(kappa)*sh.CalcBmn(-1,1)/(2*sqrt(4*M_PI)) * d(0)*c;
834
+ tmp.SH().Coef(1,-1) += Complex(0,1)*sqr(kappa)*sh.CalcBmn(-1,1)/(2*sqrt(4*M_PI)) * d(0)*c;
835
+
836
+ tmp.SH().Coef(1,1) += Complex(1,0)*sqr(kappa)*sh.CalcBmn(-1,1)/(2*sqrt(4*M_PI)) * d(1)*c;
837
+ tmp.SH().Coef(1,-1) -= Complex(1,0)*sqr(kappa)*sh.CalcBmn(-1,1)/(2*sqrt(4*M_PI)) * d(1)*c;
838
+
839
+ tmp.SH().Coef(1,0) += -Complex(0,1)*kappa*kappa*sh.CalcAmn(0,0)/sqrt(4*M_PI) *d(2)*c;
840
+ tmp.TransformAdd (*this, -x);
841
+ */
842
+
843
+ MultiPole<MPSingular> tmp(Order(), kappa, Scale());
844
+ tmp.AddCharge(x, c);
845
+ tmp.SH().DirectionalDiffAdd (kappa*d, this->SH(), Scale());
846
+ }
847
+
848
+
849
+ void ChangeScaleTo (double newscale)
850
+ {
851
+ double fac = scale/newscale;
852
+ double prod = 1;
853
+ for (int n = 0; n <= sh.Order(); n++, prod*= fac)
854
+ sh.CoefsN(n) *= prod;
855
+ scale = newscale;
856
+ }
857
+
858
+ template <typename TARGET>
859
+ void Transform (MultiPole<TARGET> & target, Vec<3> dist) const
860
+ {
861
+ if (target.SH().Order() < 0) return;
862
+ if (SH().Order() < 0)
863
+ {
864
+ target.SH().Coefs() = 0.0;
865
+ return;
866
+ }
867
+
868
+ static Timer t("mptool Transform "+ToString(typeid(RADIAL).name())+ToString(typeid(TARGET).name()));
869
+ RegionTimer reg(t);
870
+
871
+ double len = L2Norm(dist);
872
+ double theta, phi;
873
+
874
+ if (len < 1e-30)
875
+ theta = 0;
876
+ else
877
+ theta = acos (dist(2) / len);
878
+
879
+ if (sqr(dist(0))+sqr(dist(1)) < 1e-30)
880
+ phi = 0;
881
+ else
882
+ phi = atan2(dist(1), dist(0));
883
+
884
+
885
+ MultiPole<RADIAL> tmp(*this);
886
+ tmp.SH().RotateZ(phi);
887
+ tmp.SH().RotateY(theta);
888
+
889
+ tmp.ShiftZ(-len, target);
890
+
891
+ target.SH().RotateY(-theta);
892
+ target.SH().RotateZ(-phi);
893
+ }
894
+
895
+ template <typename TARGET>
896
+ void TransformAdd (MultiPole<TARGET> & target, Vec<3> dist) const
897
+ {
898
+ if (SH().Order() < 0) return;
899
+ if (target.SH().Order() < 0) return;
900
+
901
+ MultiPole<TARGET> tmp{target};
902
+ Transform(tmp, dist);
903
+ target.SH().Coefs() += tmp.SH().Coefs();
904
+ }
905
+
906
+ #ifdef VER1
907
+ template <typename TARGET>
908
+ void ShiftZ (double z, MultiPole<TARGET> & target)
909
+ {
910
+ static Timer t("mptool ShiftZ"+ToString(typeid(RADIAL).name())+ToString(typeid(TARGET).name()));
911
+
912
+ RegionTimer rg(t);
913
+
914
+ int os = sh.Order();
915
+ int ot = target.SH().Order();
916
+
917
+ if (os > 100 && ot > 100 && abs(z)*kappa > 0.3*min(os,ot) && is_same<RADIAL,TARGET>())
918
+ {
919
+ MultiPole<TARGET> tmp {target};
920
+ ShiftZ(z/2, tmp);
921
+ tmp.ShiftZ(z/2, target);
922
+ return;
923
+ }
924
+
925
+
926
+ target.SH().Coefs()=0.0;
927
+
928
+ LocalHeap lh( 16*( (os+ot+1)*(os+1) + (os+1 + ot+1) ) + 8*2*(os+ot+1) + 500);
929
+
930
+ FlatMatrix<Complex> trafo(os+ot+1, os+1, lh);
931
+ FlatVector<Complex> hv1(os+1, lh), hv2(ot+1, lh);
932
+ FlatVector<double> amn(os+ot+1, lh);
933
+ FlatVector<double> inv_amn(os+ot+1, lh);
934
+
935
+ // trafo = Complex(0.0);
936
+
937
+
938
+ double tscale = target.Scale();
939
+ double inv_tscale = 1.0/tscale;
940
+
941
+
942
+ // (185) from paper 'fast, exact, stable, Gumerov+Duraiswami
943
+ // RADIAL::Eval(os+ot, kappa*abs(z), trafo.Col(0));
944
+ if (typeid(RADIAL) == typeid(TARGET))
945
+ SphericalBessel (os+ot, kappa*abs(z), tscale, trafo.Col(0));
946
+ else
947
+ SphericalHankel1 (os+ot, kappa*abs(z), inv_tscale, trafo.Col(0));
948
+
949
+ /*
950
+ if (L2Norm(trafo.Col(0)) > 1e5 || std::isnan(L2Norm(trafo.Col(0))))
951
+ {
952
+ *testout << "large Hankel: " << L2Norm(trafo.Col(0)) << endl;
953
+ *testout << "kappa z = " << kappa*z << ", os = " << os << ", ot = " << ot << endl;
954
+ }
955
+ */
956
+ // if (L2Norm(trafo.Col(0)) > 1e5)
957
+ // throw Exception ("z-shift - coefs large");
958
+
959
+ if (z < 0)
960
+ for (int l = 1; l < trafo.Height(); l+=2) trafo(l,0) *= -1;
961
+
962
+ for (int l = 0; l <= os+ot; l++)
963
+ trafo(l,0) *= sqrt(2*l+1);
964
+
965
+ if (os > 0)
966
+ {
967
+ for (int l = 1; l < os+ot; l++)
968
+ trafo(l,1) = -scale/sh.CalcAmn(0,0) * (sh.CalcAmn(0,l)*tscale*trafo(l+1,0)
969
+ -sh.CalcAmn(0,l-1)*inv_tscale*trafo(l-1,0));
970
+ trafo(0,1) = -scale*tscale*trafo(1,0);
971
+ }
972
+
973
+ for (int n = 1; n < os; n++)
974
+ {
975
+ for (int l = 1; l < os+ot-n; l++)
976
+ trafo(l,n+1) = -scale/sh.CalcAmn(0,n) * (sh.CalcAmn(0,l)*tscale*trafo(l+1,n)
977
+ -sh.CalcAmn(0,l-1)*inv_tscale*trafo(l-1,n)
978
+ -sh.CalcAmn(0,n-1)*scale*trafo(l,n-1));
979
+ trafo(0,n+1) = pow(-scale*tscale,n+1)*trafo(n+1,0);
980
+ }
981
+
982
+ cout << "m = " << 0 << endl
983
+ << trafo.Rows(0,ot+1) << endl;
984
+
985
+
986
+ for (int n = 0; n <= os; n++)
987
+ hv1(n) = sh.Coef(n,0);
988
+ hv2 = trafo.Rows(ot+1) * hv1;
989
+ for (int n = 0; n <= ot; n++)
990
+ target.SH().Coef(n,0) = hv2(n);
991
+
992
+
993
+ for (int m = 1; m <= min(os,ot); m++)
994
+ {
995
+ // fill recursive formula (187)
996
+ for (int l = m; l <= os+ot-m; l++)
997
+ trafo(l,m) = scale/sh.CalcBmn(-m, m) * (sh.CalcBmn(-m, l)*inv_tscale*trafo(l-1, m-1)
998
+ -sh.CalcBmn(m-1,l+1)*tscale*trafo(l+1,m-1));
999
+
1000
+
1001
+ /*
1002
+ cout << "m = " << m << endl;
1003
+ cout << " norm col0 = " << L2Norm(trafo.Col(m).Range(m,os+ot-m+1)) << endl;
1004
+ */
1005
+
1006
+ for (int l = m-1; l < os+ot-m; l++)
1007
+ {
1008
+ amn(l) = sh.CalcAmn(m,l);
1009
+ inv_amn(l) = scale/amn(l);
1010
+ }
1011
+
1012
+ double prod = 1;
1013
+ for (int n = m; n < os; n++)
1014
+ {
1015
+ for (int l = m+1; l < os+ot-n; l++)
1016
+ trafo(l,n+1) = -inv_amn(n) * (amn(l)*tscale*trafo(l+1,n)
1017
+ -amn(l-1)*inv_tscale*trafo(l-1,n)
1018
+ -amn(n-1)*scale*trafo(l,n-1));
1019
+
1020
+ prod *= -scale*tscale;
1021
+ trafo(m,n+1) = prod*trafo(n+1,m);
1022
+ }
1023
+
1024
+ /*
1025
+ cout << " norm trafo = "
1026
+ << L2Norm(trafo.Rows(m,ot+1).Cols(m,os+1))
1027
+ << " ortho " << L2Norm( Trans(trafo.Rows(m,ot+1).Cols(m,os+1))*trafo.Rows(m,ot+1).Cols(m,os+1)
1028
+ - Identity(os+1-m)) << endl;
1029
+ */
1030
+ /*
1031
+ *testout << "norm trafo = " << L2Norm(trafo.Rows(m, ot+1).Cols(m,os+1)) << endl;
1032
+ if ( L2Norm(trafo.Rows(m, ot+1).Cols(m,os+1)) > 1e30)
1033
+ {
1034
+ *testout << trafo.Rows(m, ot+1).Cols(m,os+1) << endl;
1035
+ for (int i = m; i < os+1; i++)
1036
+ {
1037
+ *testout << "norm col " << i << " = " << L2Norm(trafo.Col(i).Range(m,os+ot-i)) << endl;
1038
+ *testout << "col " << i << " = " << trafo.Col(i).Range(m,os+ot-i) << endl;
1039
+ }
1040
+ throw Exception("large mat");
1041
+ }
1042
+ */
1043
+
1044
+ cout << "m = " << m << endl
1045
+ << trafo.Rows(m,ot+1).Cols(m,os+1) << endl;
1046
+
1047
+ for (int n = m; n <= os; n++)
1048
+ hv1(n) = sh.Coef(n,m);
1049
+ hv2.Range(m,ot+1) = trafo.Rows(m,ot+1).Cols(m,os+1) * hv1.Range(m,os+1);
1050
+ for (int n = m; n <= ot; n++)
1051
+ target.SH().Coef(n,m) = hv2(n);
1052
+
1053
+ for (int n = m; n <= os; n++)
1054
+ hv1(n) = sh.Coef(n,-m);
1055
+ hv2.Range(m,ot+1) = trafo.Rows(m,ot+1).Cols(m,os+1) * hv1.Range(m,os+1);
1056
+ for (int n = m; n <= ot; n++)
1057
+ target.SH().Coef(n,-m) = hv2(n);
1058
+ }
1059
+ }
1060
+ #endif
1061
+
1062
+
1063
+ #ifdef VER2
1064
+
1065
+ template <typename TARGET>
1066
+ void ShiftZ (double z, MultiPole<TARGET> & target)
1067
+ {
1068
+ static Timer t("mptool ShiftZ"+ToString(typeid(RADIAL).name())+ToString(typeid(TARGET).name()));
1069
+ RegionTimer rg(t);
1070
+
1071
+ int os = sh.Order();
1072
+ int ot = target.SH().Order();
1073
+
1074
+ target.SH().Coefs()=0.0;
1075
+
1076
+ LocalHeap lh( 16*( (os+ot+1)*(os+1) + (os+1 + ot+1) ) + 8*2*(os+ot+1) + 500);
1077
+
1078
+ FlatMatrix<Complex> trafo(os+ot+1, os+1, lh);
1079
+ FlatVector<Complex> hv1(os+1, lh), hv2(ot+1, lh);
1080
+ FlatVector<double> amn(os+ot+1, lh);
1081
+ FlatVector<double> inv_amn(os+ot+1, lh);
1082
+
1083
+ // trafo = Complex(0.0);
1084
+
1085
+ double tscale = target.Scale();
1086
+ double inv_tscale = 1.0/tscale;
1087
+
1088
+
1089
+ // (185) from paper 'fast, exact, stable, Gumerov+Duraiswami
1090
+ // RADIAL::Eval(os+ot, kappa*abs(z), trafo.Col(0));
1091
+ if (typeid(RADIAL) == typeid(TARGET))
1092
+ SphericalBessel (os+ot, kappa*abs(z), tscale, trafo.Col(0));
1093
+ else
1094
+ SphericalHankel1 (os+ot, kappa*abs(z), inv_tscale, trafo.Col(0));
1095
+
1096
+ if (z < 0)
1097
+ for (int l = 1; l < trafo.Height(); l+=2) trafo(l,0) *= -1;
1098
+
1099
+ // for (int l = 0; l <= os+ot; l++)
1100
+ // trafo(l,0) *= sqrt(2*l+1);
1101
+
1102
+ if (os > 0)
1103
+ {
1104
+ for (int l = 1; l < os+ot; l++)
1105
+ {
1106
+ /*
1107
+ trafo(l,1) = -scale/sh.CalcAmn(0,0) *
1108
+ (sh.CalcAmn(0,l)*tscale*trafo(l+1,0)
1109
+ -sh.CalcAmn(0,l-1)*inv_tscale*trafo(l-1,0));
1110
+ */
1111
+
1112
+ int m = 0, n = 0;
1113
+ double fac = ((2*l+1.0)/(2*n+1.0));
1114
+ trafo(l,n+1) = -scale/ ( sqrt((n+1+m)*(n+1-m)) * fac) *
1115
+ (sqrt( (l+1+m)*(l+1-m)) * tscale * trafo(l+1,n)
1116
+ -sqrt( (l+m)*(l-m) ) * inv_tscale * trafo(l-1,n)
1117
+ -sqrt( (n+m)*(n-m) ) * fac * scale * trafo(l,n-1));
1118
+ }
1119
+ trafo(0,1) = -scale*tscale*trafo(1,0);
1120
+ }
1121
+
1122
+ for (int n = 1; n < os; n++)
1123
+ {
1124
+ for (int l = 1; l < os+ot-n; l++)
1125
+ {
1126
+ /*
1127
+ trafo(l,n+1) = -scale/sh.CalcAmn(0,n) * (sh.CalcAmn(0,l)*tscale*trafo(l+1,n)
1128
+ -sh.CalcAmn(0,l-1)*inv_tscale*trafo(l-1,n)
1129
+ -sh.CalcAmn(0,n-1)*scale*trafo(l,n-1));
1130
+ */
1131
+
1132
+ int m = 0;
1133
+ double fac = ((2*l+1.0)/(2*n+1.0));
1134
+ trafo(l,n+1) = -scale / ( sqrt((n+1+m)*(n+1-m)) * fac) *
1135
+ (sqrt( (l+1+m)*(l+1-m)) * tscale * trafo(l+1,n)
1136
+ -sqrt( (l+m)*(l-m) ) * inv_tscale * trafo(l-1,n)
1137
+ -sqrt( (n+m)*(n-m) ) * fac * scale * trafo(l,n-1));
1138
+ }
1139
+ trafo(0,n+1) = pow(-scale*tscale,n+1)*trafo(n+1,0);
1140
+ }
1141
+
1142
+
1143
+ Matrix<Complex> scaledtrafo(os+ot+1, os+1);
1144
+ for (int l = 0; l <= os+ot; l++)
1145
+ for (int n = 0; n <= os; n++)
1146
+ scaledtrafo(l,n) = trafo(l,n) * sqrt( (2*l+1)*(2*n+1) );
1147
+
1148
+ // cout << "m = " << 0 << endl
1149
+ // << scaledtrafo.Rows(0,ot+1) << endl;
1150
+
1151
+ for (int n = 0; n <= os; n++)
1152
+ hv1(n) = sh.Coef(n,0) * sqrt(2*n+1);
1153
+ hv2 = trafo.Rows(ot+1) * hv1;
1154
+ for (int n = 0; n <= ot; n++)
1155
+ target.SH().Coef(n,0) = hv2(n) * sqrt(2*n+1);
1156
+
1157
+
1158
+ for (int m = 1; m <= min(os,ot); m++)
1159
+ {
1160
+ // fill recursive formula (187)
1161
+ for (int l = m; l <= os+ot-m; l++)
1162
+ {
1163
+ trafo(l,m) = scale/sh.CalcBmn(-m, m) *
1164
+ (sh.CalcBmn(-m, l)*inv_tscale * trafo(l-1, m-1) * sqrt( (2*l-1)*(2*m-1) )
1165
+ -sh.CalcBmn(m-1,l+1)*tscale * trafo(l+1,m-1) * sqrt( (2*l+3)*(2*m-1)) );
1166
+ trafo(l,m) /= sqrt( (2*l+1)*(2*m+1) );
1167
+ }
1168
+
1169
+ cout << "m = " << m << endl;
1170
+ cout << " norm col0 = " << L2Norm(trafo.Col(m).Range(m,os+ot-m+1)) << endl;
1171
+
1172
+ for (int l = m-1; l < os+ot-m; l++)
1173
+ {
1174
+ amn(l) = sh.CalcAmn(m,l);
1175
+ inv_amn(l) = scale/amn(l);
1176
+ }
1177
+
1178
+ double prod = 1;
1179
+ for (int n = m; n < os; n++)
1180
+ {
1181
+ for (int l = m+1; l < os+ot-n; l++)
1182
+ {
1183
+ /*
1184
+ trafo(l,n+1) = -inv_amn(n) * (amn(l)*tscale*trafo(l+1,n)
1185
+ -amn(l-1)*inv_tscale*trafo(l-1,n)
1186
+ -amn(n-1)*scale*trafo(l,n-1));
1187
+ */
1188
+
1189
+
1190
+ double fac = ((2*l+1.0)/(2*n+1.0));
1191
+ trafo(l,n+1) = -scale / ( sqrt((n+1+m)*(n+1-m)) * fac) *
1192
+ (sqrt( (l+1+m)*(l+1-m)) * tscale * trafo(l+1,n)
1193
+ -sqrt( (l+m)*(l-m) ) * inv_tscale * trafo(l-1,n)
1194
+ -sqrt( (n+m)*(n-m) ) * fac * scale * trafo(l,n-1));
1195
+ }
1196
+ prod *= -scale*tscale;
1197
+ trafo(m,n+1) = prod*trafo(n+1,m);
1198
+ }
1199
+
1200
+ double normleft = 0;
1201
+ for (int l = m; l <= ot; l++)
1202
+ for (int n = m; n <= min(l,os); n++)
1203
+ normleft += sqr(abs(trafo(l,n)));
1204
+ normleft = sqrt(normleft);
1205
+ cout << " norm trafo = "
1206
+ << L2Norm(trafo.Rows(m,ot+1).Cols(m,os+1)) << ", normleft = " << normleft << endl;
1207
+
1208
+ // << " ortho " << L2Norm( Trans(trafo.Rows(m,ot+1).Cols(m,os+1))*trafo.Rows(m,ot+1).Cols(m,os+1)
1209
+ // - Identity(os+1-m)) << endl;
1210
+ /*
1211
+ *testout << "norm trafo = " << L2Norm(trafo.Rows(m, ot+1).Cols(m,os+1)) << endl;
1212
+ if ( L2Norm(trafo.Rows(m, ot+1).Cols(m,os+1)) > 1e30)
1213
+ {
1214
+ *testout << trafo.Rows(m, ot+1).Cols(m,os+1) << endl;
1215
+ for (int i = m; i < os+1; i++)
1216
+ {
1217
+ *testout << "norm col " << i << " = " << L2Norm(trafo.Col(i).Range(m,os+ot-i)) << endl;
1218
+ *testout << "col " << i << " = " << trafo.Col(i).Range(m,os+ot-i) << endl;
1219
+ }
1220
+ throw Exception("large mat");
1221
+ }
1222
+ */
1223
+
1224
+
1225
+ /*
1226
+ Matrix<Complex> scaledtrafo(os+ot+1, os+1);
1227
+ for (int l = 0; l <= os+ot; l++)
1228
+ for (int n = 0; n <= os; n++)
1229
+ scaledtrafo(l,n) = trafo(l,n) * sqrt( (2*l+1)*(2*n+1) );
1230
+
1231
+ cout << "m = " << m << endl
1232
+ << scaledtrafo.Rows(m,ot+1).Cols(m,os+1) << endl;
1233
+ */
1234
+
1235
+ for (int n = m; n <= os; n++)
1236
+ hv1(n) = sh.Coef(n,m) * sqrt(2*n+1);
1237
+ hv2.Range(m,ot+1) = trafo.Rows(m,ot+1).Cols(m,os+1) * hv1.Range(m,os+1);
1238
+ for (int n = m; n <= ot; n++)
1239
+ target.SH().Coef(n,m) = hv2(n)*sqrt(2*n+1);
1240
+
1241
+ for (int n = m; n <= os; n++)
1242
+ hv1(n) = sh.Coef(n,-m) * sqrt(2*n+1);
1243
+ hv2.Range(m,ot+1) = trafo.Rows(m,ot+1).Cols(m,os+1) * hv1.Range(m,os+1);
1244
+ for (int n = m; n <= ot; n++)
1245
+ target.SH().Coef(n,-m) = hv2(n) * sqrt(2*n+1);
1246
+ }
1247
+ }
1248
+ #endif
1249
+
1250
+
1251
+
1252
+
1253
+ template <typename TARGET>
1254
+ void ShiftZ (double z, MultiPole<TARGET> & target)
1255
+ {
1256
+ static Timer t("mptool ShiftZ"+ToString(typeid(RADIAL).name())+ToString(typeid(TARGET).name()));
1257
+ RegionTimer rg(t);
1258
+
1259
+ int os = sh.Order();
1260
+ int ot = target.SH().Order();
1261
+
1262
+ target.SH().Coefs()=0.0;
1263
+
1264
+ LocalHeap lh( 32*( (os+ot+1)*(os+ot+1) + (os+1 + ot+1) ) + 8*3*(os+ot+1) + 500);
1265
+
1266
+ FlatMatrix<Complex> trafo(os+ot+1, max(os,ot)+1, lh);
1267
+ FlatMatrix<Complex> oldtrafo(os+ot+1, max(os,ot)+1, lh);
1268
+ FlatVector<Complex> hv1(os+1, lh), hv2(ot+1, lh);
1269
+
1270
+ // trafo = Complex(0.0);
1271
+
1272
+ double tscale = target.Scale();
1273
+ double inv_tscale = 1.0/tscale;
1274
+
1275
+ FlatVector<double> amn(os+ot+1, lh);
1276
+ FlatVector<double> inv_amn(os+ot+1, lh);
1277
+
1278
+ FlatVector<double> powscale(os+1, lh);
1279
+ double prod = 1;
1280
+ for (int i = 0; i <= os; i++)
1281
+ {
1282
+ powscale(i) = prod;
1283
+ prod *= -scale*tscale;
1284
+ }
1285
+
1286
+ // (185) from paper 'fast, exact, stable, Gumerov+Duraiswami
1287
+ // RADIAL::Eval(os+ot, kappa*abs(z), trafo.Col(0));
1288
+ if (typeid(RADIAL) == typeid(TARGET))
1289
+ SphericalBessel (os+ot, kappa*abs(z), tscale, trafo.Col(0));
1290
+ else
1291
+ SphericalHankel1 (os+ot, kappa*abs(z), inv_tscale, trafo.Col(0));
1292
+
1293
+ if (z < 0)
1294
+ for (int l = 1; l < trafo.Height(); l+=2) trafo(l,0) *= -1;
1295
+
1296
+ for (int l = 0; l <= os+ot; l++)
1297
+ trafo(l,0) *= sqrt(2*l+1);
1298
+
1299
+ // for (int l = 0; l <= os+ot; l++)
1300
+ // trafo(l,0) *= sqrt(2*l+1);
1301
+
1302
+ if (os > 0)
1303
+ {
1304
+ for (int l = 1; l < os+ot; l++)
1305
+ trafo(l,1) = -scale/sh.CalcAmn(0,0) * (sh.CalcAmn(0,l)*tscale*trafo(l+1,0)
1306
+ -sh.CalcAmn(0,l-1)*inv_tscale*trafo(l-1,0));
1307
+ trafo(0,1) = -scale*tscale*trafo(1,0);
1308
+ }
1309
+
1310
+ for (int n = 1; n < trafo.Width()-1; n++)
1311
+ {
1312
+ for (int l = 1; l < os+ot-n; l++)
1313
+ trafo(l,n+1) = -scale/sh.CalcAmn(0,n) * (sh.CalcAmn(0,l)*tscale*trafo(l+1,n)
1314
+ -sh.CalcAmn(0,l-1)*inv_tscale*trafo(l-1,n)
1315
+ -sh.CalcAmn(0,n-1)*scale*trafo(l,n-1));
1316
+ trafo(0,n+1) = pow(-scale*tscale,n+1)*trafo(n+1,0);
1317
+ }
1318
+
1319
+ for (int n = 0; n <= os; n++)
1320
+ hv1(n) = sh.Coef(n,0);
1321
+ hv2 = trafo.Rows(ot+1).Cols(os+1) * hv1;
1322
+ for (int n = 0; n <= ot; n++)
1323
+ target.SH().Coef(n,0) = hv2(n);
1324
+
1325
+
1326
+ for (int m = 1; m <= min(os,ot); m++)
1327
+ {
1328
+
1329
+
1330
+ for (int l = m-1; l < os+ot-m; l++)
1331
+ {
1332
+ amn(l) = sh.CalcAmn(m,l);
1333
+ inv_amn(l) = scale/amn(l);
1334
+ }
1335
+
1336
+ if (typeid(RADIAL) != typeid(TARGET))
1337
+
1338
+ {
1339
+ // fill recursive formula (187)
1340
+ for (int l = m; l <= os+ot-m; l++)
1341
+ trafo(l,m) = scale/sh.CalcBmn(-m, m) * (sh.CalcBmn(-m, l)*inv_tscale*trafo(l-1, m-1)
1342
+ -sh.CalcBmn(m-1,l+1)*tscale*trafo(l+1,m-1));
1343
+
1344
+ for (int n = m; n < os; n++)
1345
+ {
1346
+ for (int l = n+1; l < os+ot-n; l++)
1347
+ trafo(l,n+1) = -inv_amn(n) * (amn(l)*tscale*trafo(l+1,n)
1348
+ -amn(l-1)*inv_tscale*trafo(l-1,n)
1349
+ -amn(n-1)*scale*trafo(l,n-1));
1350
+ }
1351
+ }
1352
+
1353
+ else
1354
+
1355
+ {
1356
+ /*
1357
+ for (int n = m; n < trafo.Width()-1; n++)
1358
+ {
1359
+ for (int l = n+1; l < os+ot-n; l++)
1360
+ trafo(l,n+1) = -inv_amn(n) * (amn(l)*tscale*trafo(l+1,n)
1361
+ -amn(l-1)*inv_tscale*trafo(l-1,n)
1362
+ -amn(n-1)*scale*trafo(l,n-1));
1363
+ }
1364
+ */
1365
+
1366
+ trafo.Swap (oldtrafo);
1367
+ trafo = 0.0;
1368
+
1369
+ // fill recursive formula (187)
1370
+ for (int l = m; l <= os+ot-m; l++)
1371
+ trafo(l,m) = scale/sh.CalcBmn(-m, m) * (sh.CalcBmn(-m, l)*inv_tscale*oldtrafo(l-1, m-1)
1372
+ -sh.CalcBmn(m-1,l+1)*tscale*oldtrafo(l+1,m-1));
1373
+
1374
+ for (int n = m; n < trafo.Width()-1; n++)
1375
+ {
1376
+ // int l = 2*order-n-1;
1377
+ int l = trafo.Height()-n-2;
1378
+
1379
+ trafo(l,n+1) = scale/sh.CalcBmn(-m,n+1)* (sh.CalcBmn(m-1,n) * scale*trafo(l,n-1)
1380
+ - sh.CalcBmn(m-1,l+1)*tscale*oldtrafo(l+1,n)
1381
+ + sh.CalcBmn(-m,l) * 1/tscale*oldtrafo(l-1,n) );
1382
+
1383
+ trafo(l-1,n) = tscale/amn(l-1) * (amn(l) * tscale*trafo(l+1,n)
1384
+ - amn(n-1)* scale*trafo(l,n-1)
1385
+ + amn(n)* 1/scale*trafo(l,n+1));
1386
+ }
1387
+
1388
+ // the same thing 1 row up
1389
+ for (int n = m; n < trafo.Width()-2; n++)
1390
+ {
1391
+ // int l = 2*order-n-2;
1392
+ int l = trafo.Height()-n-3;
1393
+
1394
+ trafo(l,n+1) = scale/sh.CalcBmn(-m,n+1)* (sh.CalcBmn(m-1,n) * scale*trafo(l,n-1)
1395
+ - sh.CalcBmn(m-1,l+1) * tscale* oldtrafo(l+1,n)
1396
+ + sh.CalcBmn(-m,l) * 1/tscale* oldtrafo(l-1,n) );
1397
+
1398
+ trafo(l-1,n) = tscale/amn(l-1) * (amn(l) * tscale*trafo(l+1,n)
1399
+ -amn(n-1)* scale*trafo(l,n-1) +
1400
+ amn(n) * 1/scale*trafo(l,n+1)) ;
1401
+ }
1402
+
1403
+
1404
+ // for (int l = 2*order; l >= m; l--)
1405
+ // for (int n = m+1; n < min(2*order-l,l); n++)
1406
+ for (int l = trafo.Height()-1; l >= m; l--)
1407
+ for (int n = m+1; n < min<int>(trafo.Height()-1-l,l); n++)
1408
+ {
1409
+ trafo(l-1,n) = tscale/amn(l-1)* ( amn(l) * tscale*trafo(l+1,n)
1410
+ -amn(n-1)* scale*trafo(l,n-1)
1411
+ +amn(n) * 1/scale*trafo(l,n+1)) ;
1412
+ }
1413
+ }
1414
+
1415
+
1416
+ /*
1417
+ cout << "m = " << m << endl
1418
+ << trafo << endl;
1419
+ */
1420
+ for (int n = m; n < os; n++)
1421
+ for (int l = n+1; l <= os; l++)
1422
+ trafo(n,l) = powscale(l-n) * trafo(l,n);
1423
+ // trafo(n,l) = pow(-scale*tscale, l-n) * trafo(l,n);
1424
+
1425
+ /*
1426
+ cout << " norm trafo = "
1427
+ << L2Norm(trafo.Rows(m,ot+1).Cols(m,os+1)) << endl;
1428
+ */
1429
+
1430
+ for (int n = m; n <= os; n++)
1431
+ hv1(n) = sh.Coef(n,m);
1432
+ hv2.Range(m,ot+1) = trafo.Rows(m,ot+1).Cols(m,os+1) * hv1.Range(m,os+1);
1433
+ for (int n = m; n <= ot; n++)
1434
+ target.SH().Coef(n,m) = hv2(n);
1435
+
1436
+ for (int n = m; n <= os; n++)
1437
+ hv1(n) = sh.Coef(n,-m);
1438
+ hv2.Range(m,ot+1) = trafo.Rows(m,ot+1).Cols(m,os+1) * hv1.Range(m,os+1);
1439
+ for (int n = m; n <= ot; n++)
1440
+ target.SH().Coef(n,-m) = hv2(n);
1441
+ }
1442
+ }
1443
+
1444
+
1445
+
1446
+
1447
+
1448
+
1449
+ };
1450
+
1451
+
1452
+
1453
+ // ***************** parameters ****************
1454
+
1455
+ static int MPOrder (double rho_kappa)
1456
+ {
1457
+ return max (20, int(2*rho_kappa));
1458
+ }
1459
+ static constexpr int maxdirect = 100;
1460
+
1461
+ class SingularMLMultiPole
1462
+ {
1463
+ static Array<size_t> nodes_on_level;
1464
+
1465
+ struct Node
1466
+ {
1467
+ Vec<3> center;
1468
+ double r;
1469
+ int level;
1470
+ std::array<unique_ptr<Node>,8> childs;
1471
+ MultiPole<MPSingular> mp;
1472
+
1473
+ Array<tuple<Vec<3>, Complex>> charges;
1474
+ Array<tuple<Vec<3>, Vec<3>, Complex>> dipoles;
1475
+ int total_sources;
1476
+
1477
+ Node (Vec<3> acenter, double ar, int alevel, int order, double kappa)
1478
+ : center(acenter), r(ar), level(alevel), mp(MPOrder(ar*kappa), kappa, min(1.0, 1*r*kappa))
1479
+ // : center(acenter), r(ar), level(alevel), mp(MPOrder(ar*kappa), kappa, 1.0)
1480
+ {
1481
+ // cout << "singml, add node, level = " << level << endl;
1482
+ if (level < nodes_on_level.Size())
1483
+ nodes_on_level[level]++;
1484
+ }
1485
+
1486
+
1487
+ void CreateChilds()
1488
+ {
1489
+ if (childs[0]) throw Exception("have already childs");
1490
+ for (int i = 0; i < 8; i++)
1491
+ {
1492
+ Vec<3> cc = center;
1493
+ cc(0) += (i&1) ? r/2 : -r/2;
1494
+ cc(1) += (i&2) ? r/2 : -r/2;
1495
+ cc(2) += (i&4) ? r/2 : -r/2;
1496
+ childs[i] = make_unique<Node> (cc, r/2, level+1, max(mp.SH().Order()/2, 8), mp.Kappa());
1497
+ }
1498
+ }
1499
+
1500
+
1501
+ void AddCharge (Vec<3> x, Complex c)
1502
+ {
1503
+ if (childs[0])
1504
+ {
1505
+ // directly send to childs:
1506
+ int childnum = 0;
1507
+ if (x(0) > center(0)) childnum += 1;
1508
+ if (x(1) > center(1)) childnum += 2;
1509
+ if (x(2) > center(2)) childnum += 4;
1510
+ childs[childnum] -> AddCharge(x, c);
1511
+ return;
1512
+ }
1513
+
1514
+ charges.Append( tuple{x,c} );
1515
+
1516
+ if (r*mp.Kappa() < 1e-8) return;
1517
+ if (charges.Size() < maxdirect && r*mp.Kappa() < 1)
1518
+ return;
1519
+
1520
+ CreateChilds();
1521
+
1522
+ for (auto [x,c] : charges)
1523
+ AddCharge (x,c);
1524
+ for (auto [x,d,c] : dipoles)
1525
+ AddDipole (x,d,c);
1526
+
1527
+ charges.SetSize0();
1528
+ dipoles.SetSize0();
1529
+ }
1530
+
1531
+
1532
+ void AddDipole (Vec<3> x, Vec<3> d, Complex c)
1533
+ {
1534
+ if (childs[0])
1535
+ {
1536
+ // directly send to childs:
1537
+
1538
+ int childnum = 0;
1539
+ if (x(0) > center(0)) childnum += 1;
1540
+ if (x(1) > center(1)) childnum += 2;
1541
+ if (x(2) > center(2)) childnum += 4;
1542
+ childs[childnum] -> AddDipole(x, d, c);
1543
+ return;
1544
+ }
1545
+
1546
+ dipoles.Append (tuple{x,d,c});
1547
+
1548
+ if (dipoles.Size() < maxdirect || r < 1e-8)
1549
+ return;
1550
+
1551
+ CreateChilds();
1552
+
1553
+ for (auto [x,c] : charges)
1554
+ AddCharge (x,c);
1555
+ for (auto [x,d,c] : dipoles)
1556
+ AddDipole (x,d,c);
1557
+
1558
+ charges.SetSize0();
1559
+ dipoles.SetSize0();
1560
+ }
1561
+
1562
+
1563
+ Complex Evaluate(Vec<3> p) const
1564
+ {
1565
+ Complex sum = 0;
1566
+ if (childs[0])
1567
+ {
1568
+ for (auto & child : childs)
1569
+ sum += child->Evaluate(p);
1570
+ return sum;
1571
+ }
1572
+
1573
+ for (auto [x,c] : charges)
1574
+ if (double rho = L2Norm(p-x); rho > 0)
1575
+ sum += c*(1/(4*M_PI))*exp(Complex(0,rho*mp.Kappa())) / rho;
1576
+
1577
+ for (auto [x,d,c] : dipoles)
1578
+ if (double rho = L2Norm(p-x); rho > 0)
1579
+ {
1580
+ Vec<3> drhodp = 1.0/rho * (p-x);
1581
+ Complex dGdrho = c*(1/(4*M_PI))*exp(Complex(0,rho*mp.Kappa())) *
1582
+ (Complex(0, mp.Kappa())/rho - 1.0/sqr(rho));
1583
+ sum += dGdrho * InnerProduct(drhodp, d);
1584
+ }
1585
+
1586
+ return sum;
1587
+ }
1588
+
1589
+ void CalcTotalSources()
1590
+ {
1591
+ total_sources = charges.Size() + dipoles.Size();
1592
+ for (auto & child : childs)
1593
+ if (child)
1594
+ {
1595
+ child->CalcTotalSources();
1596
+ total_sources += child->total_sources;
1597
+ }
1598
+ }
1599
+
1600
+ void CalcMP()
1601
+ {
1602
+ mp.SH().Coefs() = 0.0;
1603
+ if (childs[0])
1604
+ {
1605
+ if (total_sources < 1000)
1606
+ for (auto & child : childs)
1607
+ child->CalcMP();
1608
+ else
1609
+ ParallelFor (8, [&] (int nr)
1610
+ {
1611
+ childs[nr] -> CalcMP();
1612
+ });
1613
+
1614
+
1615
+ for (auto & child : childs)
1616
+ child->mp.TransformAdd(mp, center-child->center);
1617
+ }
1618
+ else
1619
+ {
1620
+ if (charges.Size()+dipoles.Size() == 0)
1621
+ {
1622
+ mp = MultiPole<MPSingular> (-1, mp.Kappa());
1623
+ return;
1624
+ }
1625
+
1626
+ for (auto [x,c] : charges)
1627
+ mp.AddCharge (x-center,c);
1628
+
1629
+ for (auto [x,d,c] : dipoles)
1630
+ mp.AddDipole (x-center, d, c);
1631
+ }
1632
+ }
1633
+
1634
+ Complex EvaluateMP(Vec<3> p) const
1635
+ {
1636
+ if (charges.Size() || dipoles.Size())
1637
+ return Evaluate(p);
1638
+
1639
+ if (L2Norm(p-center) > 3*r)
1640
+ return mp.Eval(p-center);
1641
+
1642
+ if (!childs[0]) // || level==1)
1643
+ return Evaluate(p);
1644
+
1645
+ Complex sum = 0.0;
1646
+ for (auto & child : childs)
1647
+ sum += child->EvaluateMP(p);
1648
+ return sum;
1649
+ }
1650
+
1651
+
1652
+ void Print (ostream & ost) const
1653
+ {
1654
+ ost << "c = " << center << ", r = " << r << endl;
1655
+ // for (int i = 0; i < loc_pnts.Size(); i++)
1656
+ for (auto [x,c] : charges)
1657
+ ost << "xi = " << x << ", ci = " << c << endl;
1658
+
1659
+ for (int i = 0; i < 8; i++)
1660
+ if (childs[i]) childs[i] -> Print (ost);
1661
+ }
1662
+
1663
+ double Norm () const
1664
+ {
1665
+ double norm = L2Norm(mp.SH().Coefs());
1666
+ if (childs[0])
1667
+ for (auto & ch : childs)
1668
+ norm += ch->Norm();
1669
+ return norm;
1670
+ }
1671
+
1672
+ size_t NumCoefficients() const
1673
+ {
1674
+ size_t num = sqr(mp.SH().Order()+1);
1675
+ if (childs[0])
1676
+ for (auto & ch : childs)
1677
+ num += ch->NumCoefficients();
1678
+ return num;
1679
+ }
1680
+ };
1681
+
1682
+ Node root;
1683
+ bool havemp = false;
1684
+
1685
+ public:
1686
+ SingularMLMultiPole (Vec<3> center, double r, int order, double kappa)
1687
+ : root(center, r, 0, order, kappa)
1688
+ {
1689
+ nodes_on_level = 0;
1690
+ nodes_on_level[0] = 1;
1691
+ }
1692
+
1693
+ double Kappa() const { return root.mp.Kappa(); }
1694
+
1695
+ void AddCharge(Vec<3> x, Complex c)
1696
+ {
1697
+ root.AddCharge(x, c);
1698
+ }
1699
+
1700
+ void AddDipole(Vec<3> x, Vec<3> d, Complex c)
1701
+ {
1702
+ root.AddDipole(x, d, c);
1703
+ }
1704
+
1705
+ void Print (ostream & ost) const
1706
+ {
1707
+ root.Print(ost);
1708
+ }
1709
+
1710
+ double Norm() const
1711
+ {
1712
+ return root.Norm();
1713
+ }
1714
+
1715
+ size_t NumCoefficients() const
1716
+ {
1717
+ return root.NumCoefficients();
1718
+ }
1719
+
1720
+ void CalcMP()
1721
+ {
1722
+ static Timer t("mptool compute singular MLMP"); RegionTimer rg(t);
1723
+
1724
+ /*
1725
+ int maxlevel = 0;
1726
+ for (auto [i,num] : Enumerate(nodes_on_level))
1727
+ if (num > 0) maxlevel = i;
1728
+
1729
+ for (int i = 0; i <= maxlevel; i++)
1730
+ cout << "sing " << i << ": " << nodes_on_level[i] << endl;
1731
+ */
1732
+ root.CalcTotalSources();
1733
+ root.CalcMP();
1734
+
1735
+ havemp = true;
1736
+ }
1737
+
1738
+ Complex Evaluate (Vec<3> p) const
1739
+ {
1740
+ if (havemp)
1741
+ return root.EvaluateMP(p);
1742
+ else
1743
+ return root.Evaluate(p);
1744
+ }
1745
+
1746
+ friend class RegularMLMultiPole;
1747
+ };
1748
+
1749
+
1750
+ inline ostream & operator<< (ostream & ost, const SingularMLMultiPole & mlmp)
1751
+ {
1752
+ mlmp.Print(ost);
1753
+ return ost;
1754
+ }
1755
+
1756
+
1757
+ class RegularMLMultiPole
1758
+ {
1759
+ static Array<size_t> nodes_on_level;
1760
+
1761
+ struct Node
1762
+ {
1763
+ Vec<3> center;
1764
+ double r;
1765
+ int level;
1766
+ std::array<unique_ptr<Node>,8> childs;
1767
+ MultiPole<MPRegular> mp;
1768
+ Array<Vec<3>> targets;
1769
+ int total_targets;
1770
+
1771
+ Array<const SingularMLMultiPole::Node*> singnodes;
1772
+
1773
+ Node (Vec<3> acenter, double ar, int alevel, int order, double kappa)
1774
+ : center(acenter), r(ar), level(alevel), mp(MPOrder(ar*kappa), kappa, 1.0/min(1.0, 0.25*r*kappa))
1775
+ // : center(acenter), r(ar), level(alevel), mp(MPOrder(ar*kappa), kappa, 1.0)
1776
+ {
1777
+ if (level < nodes_on_level.Size())
1778
+ nodes_on_level[level]++;
1779
+ }
1780
+
1781
+
1782
+ void CreateChilds()
1783
+ {
1784
+ if (childs[0]) throw Exception("have already childs");
1785
+ // create children nodes:
1786
+ for (int i = 0; i < 8; i++)
1787
+ {
1788
+ Vec<3> cc = center;
1789
+ cc(0) += (i&1) ? r/2 : -r/2;
1790
+ cc(1) += (i&2) ? r/2 : -r/2;
1791
+ cc(2) += (i&4) ? r/2 : -r/2;
1792
+ childs[i] = make_unique<Node> (cc, r/2, level+1, max(mp.SH().Order()/2, 8), mp.Kappa());
1793
+ }
1794
+ }
1795
+
1796
+ void AddSingularNode (const SingularMLMultiPole::Node & singnode, bool allow_refine)
1797
+ {
1798
+ if (mp.SH().Order() < 0) return;
1799
+ if (singnode.mp.SH().Order() < 0) return;
1800
+ if (L2Norm(singnode.mp.SH().Coefs()) == 0) return;
1801
+ if (level > 20)
1802
+ {
1803
+ singnodes.Append(&singnode);
1804
+ return;
1805
+ }
1806
+
1807
+ // static Timer t("AddSingularNode"); RegionTimer reg(t);
1808
+
1809
+ Vec<3> dist = center-singnode.center;
1810
+
1811
+ // if (L2Norm(dist)*mp.Kappa() > (mp.Order()+singnode.mp.Order()))
1812
+ if (L2Norm(dist) > 2*(r + singnode.r))
1813
+ {
1814
+ if (singnode.mp.Order() > 2 * mp.Order() &&
1815
+ singnode.childs[0] &&
1816
+ singnode.childs[0]->mp.Order() < singnode.mp.Order())
1817
+ {
1818
+ for (auto & child : singnode.childs)
1819
+ AddSingularNode (*child, allow_refine);
1820
+ return;
1821
+ }
1822
+
1823
+ // static Timer t("mptool transform Helmholtz-criterion"); RegionTimer r(t);
1824
+ singnode.mp.TransformAdd(mp, dist);
1825
+ return;
1826
+ }
1827
+
1828
+
1829
+ if ( singnode.childs[0]==nullptr )
1830
+ {
1831
+ singnodes.Append(&singnode);
1832
+ return;
1833
+ }
1834
+
1835
+ if (r > singnode.r)
1836
+ {
1837
+ if (allow_refine)
1838
+ {
1839
+ if (!childs[0])
1840
+ CreateChilds();
1841
+
1842
+ for (auto & ch : childs)
1843
+ ch -> AddSingularNode (singnode, allow_refine);
1844
+ }
1845
+ else
1846
+ {
1847
+ if (total_targets < 1000)
1848
+ {
1849
+ for (auto & ch : childs)
1850
+ if (ch)
1851
+ ch -> AddSingularNode (singnode, allow_refine);
1852
+ }
1853
+ else
1854
+ ParallelFor (8, [&] (int nr)
1855
+ {
1856
+ if (childs[nr])
1857
+ childs[nr] -> AddSingularNode (singnode, allow_refine);
1858
+ });
1859
+
1860
+ if (targets.Size())
1861
+ singnodes.Append(&singnode);
1862
+ }
1863
+ }
1864
+ else
1865
+ {
1866
+ for (auto & childsing : singnode.childs)
1867
+ AddSingularNode (*childsing, allow_refine);
1868
+ }
1869
+ }
1870
+
1871
+ void LocalizeExpansion(bool allow_refine)
1872
+ {
1873
+ if (allow_refine)
1874
+ if (mp.Order() > 20 && !childs[0])
1875
+ CreateChilds();
1876
+
1877
+ if (childs[0])
1878
+ {
1879
+ for (auto & ch : childs)
1880
+ {
1881
+ if (L2Norm(mp.SH().Coefs()) > 0)
1882
+ mp.TransformAdd (ch->mp, ch->center-center);
1883
+ ch->LocalizeExpansion(allow_refine);
1884
+ }
1885
+ mp = MultiPole<MPRegular>(-1, mp.Kappa());
1886
+ //mp.SH().Coefs()=0.0;
1887
+ }
1888
+ }
1889
+
1890
+ Complex Evaluate (Vec<3> p) const
1891
+ {
1892
+ // *testout << "eval p = " << p << ", level = " << level << ", center = " << center << ", r = " << r << endl;
1893
+ Complex sum = 0.0;
1894
+ /*
1895
+ if (childs[0])
1896
+ {
1897
+ int childnum = 0;
1898
+ if (p(0) > center(0)) childnum += 1;
1899
+ if (p(1) > center(1)) childnum += 2;
1900
+ if (p(2) > center(2)) childnum += 4;
1901
+ sum = childs[childnum]->Evaluate(p);
1902
+ }
1903
+ */
1904
+ int childnum = 0;
1905
+ if (p(0) > center(0)) childnum += 1;
1906
+ if (p(1) > center(1)) childnum += 2;
1907
+ if (p(2) > center(2)) childnum += 4;
1908
+ if (childs[childnum])
1909
+ sum = childs[childnum]->Evaluate(p);
1910
+ else
1911
+ sum = mp.Eval(p-center);
1912
+
1913
+
1914
+ static Timer t("mptool direct evaluate"); RegionTimer r(t);
1915
+ for (auto sn : singnodes)
1916
+ sum += sn->EvaluateMP(p);
1917
+
1918
+ return sum;
1919
+ }
1920
+
1921
+ double Norm() const
1922
+ {
1923
+ double norm = L2Norm(mp.SH().Coefs());
1924
+ if (childs[0])
1925
+ for (auto & ch : childs)
1926
+ norm += ch->Norm();
1927
+ return norm;
1928
+ }
1929
+
1930
+ size_t NumCoefficients() const
1931
+ {
1932
+ size_t num = sqr(mp.SH().Order()+1);
1933
+ if (childs[0])
1934
+ for (auto & ch : childs)
1935
+ num += ch->NumCoefficients();
1936
+ return num;
1937
+ }
1938
+
1939
+ void AddTarget (Vec<3> x)
1940
+ {
1941
+ if (childs[0])
1942
+ {
1943
+ // directly send to childs:
1944
+ int childnum = 0;
1945
+ if (x(0) > center(0)) childnum += 1;
1946
+ if (x(1) > center(1)) childnum += 2;
1947
+ if (x(2) > center(2)) childnum += 4;
1948
+ childs[childnum] -> AddTarget( x );
1949
+ return;
1950
+ }
1951
+
1952
+ targets.Append( x );
1953
+
1954
+ if (r*mp.Kappa() < 1e-8) return;
1955
+ if (targets.Size() < maxdirect && r*mp.Kappa() < 1)
1956
+ return;
1957
+
1958
+ CreateChilds();
1959
+
1960
+ for (auto t : targets)
1961
+ AddTarget (t);
1962
+ targets.SetSize0();
1963
+ }
1964
+
1965
+ void CalcTotalTargets()
1966
+ {
1967
+ total_targets = targets.Size();
1968
+ for (auto & child : childs)
1969
+ if (child)
1970
+ {
1971
+ child->CalcTotalTargets();
1972
+ total_targets += child->total_targets;
1973
+ }
1974
+ }
1975
+
1976
+ void RemoveEmptyTrees()
1977
+ {
1978
+ for (auto & child : childs)
1979
+ if (child)
1980
+ {
1981
+ child->RemoveEmptyTrees();
1982
+ // if (child->total_targets == 0)
1983
+ // child = nullptr;
1984
+ }
1985
+
1986
+ if (total_targets == 0)
1987
+ mp = MultiPole<MPRegular>(-1, mp.Kappa());
1988
+ }
1989
+
1990
+ };
1991
+
1992
+ Node root;
1993
+ shared_ptr<SingularMLMultiPole> singmp;
1994
+
1995
+ public:
1996
+ RegularMLMultiPole (shared_ptr<SingularMLMultiPole> asingmp, Vec<3> center, double r, int order)
1997
+ : root(center, r, 0, order, asingmp->Kappa()), singmp(asingmp)
1998
+ {
1999
+ if (!singmp->havemp) throw Exception("first call Calc for singular MP");
2000
+
2001
+ nodes_on_level = 0;
2002
+ nodes_on_level[0] = 1;
2003
+ {
2004
+ static Timer t("mptool compute regular MLMP"); RegionTimer rg(t);
2005
+ root.AddSingularNode(singmp->root, true);
2006
+ // cout << "norm after S->R conversion: " << root.Norm() << endl;
2007
+ }
2008
+
2009
+
2010
+ /*
2011
+ int maxlevel = 0;
2012
+ for (auto [i,num] : Enumerate(nodes_on_level))
2013
+ if (num > 0) maxlevel = i;
2014
+
2015
+ for (int i = 0; i <= maxlevel; i++)
2016
+ cout << "reg " << i << ": " << nodes_on_level[i] << endl;
2017
+ */
2018
+
2019
+ {
2020
+ static Timer t("mptool expand regular MLMP"); RegionTimer rg(t);
2021
+ root.LocalizeExpansion(true);
2022
+ // cout << "norm after local expansion: " << root.Norm() << endl;
2023
+ }
2024
+ }
2025
+
2026
+ RegularMLMultiPole (Vec<3> center, double r, int order, double kappa)
2027
+ : root(center, r, 0, order, kappa)
2028
+ {
2029
+ nodes_on_level = 0;
2030
+ nodes_on_level[0] = 1;
2031
+ }
2032
+
2033
+ void AddTarget (Vec<3> t)
2034
+ {
2035
+ root.AddTarget (t);
2036
+ }
2037
+
2038
+ void CalcMP(shared_ptr<SingularMLMultiPole> asingmp)
2039
+ {
2040
+ singmp = asingmp;
2041
+
2042
+ root.CalcTotalTargets();
2043
+ root.RemoveEmptyTrees();
2044
+
2045
+ root.AddSingularNode(singmp->root, false);
2046
+
2047
+ /*
2048
+ int maxlevel = 0;
2049
+ for (auto [i,num] : Enumerate(RegularMLMultiPole::nodes_on_level))
2050
+ if (num > 0) maxlevel = i;
2051
+
2052
+ for (int i = 0; i <= maxlevel; i++)
2053
+ cout << "reg " << i << ": " << RegularMLMultiPole::nodes_on_level[i] << endl;
2054
+ */
2055
+
2056
+ root.LocalizeExpansion(false);
2057
+ }
2058
+
2059
+ double Norm() const
2060
+ {
2061
+ return root.Norm();
2062
+ }
2063
+
2064
+ size_t NumCoefficients() const
2065
+ {
2066
+ return root.NumCoefficients();
2067
+ }
2068
+
2069
+ Complex Evaluate (Vec<3> p) const
2070
+ {
2071
+ // static Timer t("mptool Eval MLMP regular"); RegionTimer r(t);
2072
+ if (L2Norm(p-root.center) > root.r) return 0.0;
2073
+ return root.Evaluate(p);
2074
+ }
2075
+
2076
+
2077
+ };
2078
+
2079
+ Array<size_t> RegularMLMultiPole::nodes_on_level(100);
2080
+ Array<size_t> SingularMLMultiPole::nodes_on_level(100);
2081
+
2082
+ // ******************** Coefficient Functions *********************
2083
+
2084
+ class SphericalHarmonicsCF : public CoefficientFunction
2085
+ {
2086
+ SphericalHarmonics sh;
2087
+ public:
2088
+ SphericalHarmonicsCF (int order)
2089
+ : CoefficientFunction(1, true), sh(order) { }
2090
+ Complex & Coef(int n, int m) { return sh.Coef(n,m); }
2091
+
2092
+ virtual double Evaluate (const BaseMappedIntegrationPoint & ip) const override
2093
+ { throw Exception("real eval not available"); }
2094
+
2095
+ virtual void Evaluate (const BaseMappedIntegrationPoint & mip, FlatVector<Complex> values) const override
2096
+ {
2097
+ values(0) = sh.Eval(mip.GetPoint());
2098
+ }
2099
+
2100
+ virtual void Evaluate (const BaseMappedIntegrationRule & ir, BareSliceMatrix<Complex> values) const override
2101
+ {
2102
+ for (int i = 0; i < ir.Size(); i++)
2103
+ {
2104
+ auto & mip = ir[i];
2105
+ values(i,0) = sh.Eval(mip.GetPoint());
2106
+ }
2107
+ }
2108
+
2109
+ auto & SH() { return sh; }
2110
+ };
2111
+
2112
+
2113
+
2114
+ template <typename RADIAL>
2115
+ class MultiPoleCF : public CoefficientFunction
2116
+ {
2117
+ MultiPole<RADIAL> mp;
2118
+ Vec<3> center;
2119
+ public:
2120
+ MultiPoleCF (int order, double kappa, Vec<3> acenter, double scale = 1)
2121
+ : CoefficientFunction(1, true), mp(order, kappa, scale), center(acenter) { }
2122
+
2123
+ Complex & Coef(int n, int m) { return mp.Coef(n,m); }
2124
+ auto & SH() { return mp.SH(); }
2125
+ auto & MP() { return mp; }
2126
+ Vec<3> Center() const { return center; }
2127
+
2128
+ virtual double Evaluate (const BaseMappedIntegrationPoint & ip) const override
2129
+ { throw Exception("real eval not available"); }
2130
+
2131
+ virtual void Evaluate (const BaseMappedIntegrationPoint & mip, FlatVector<Complex> values) const override
2132
+ {
2133
+ values(0) = mp.Eval(mip.GetPoint()-center);
2134
+ }
2135
+
2136
+ template <typename TARGET>
2137
+ void ShiftZ (double z, MultiPole<TARGET> & target) { mp.ShiftZ(z, target); }
2138
+
2139
+ using CoefficientFunction::Transform;
2140
+ template <typename TARGET>
2141
+ void Transform (MultiPoleCF<TARGET> & target)
2142
+ {
2143
+ mp.Transform (target.MP(), target.Center()-center);
2144
+ }
2145
+ };
2146
+
2147
+
2148
+ class SingularMLMultiPoleCF : public CoefficientFunction
2149
+ {
2150
+ shared_ptr<SingularMLMultiPole> mlmp;
2151
+ public:
2152
+ SingularMLMultiPoleCF (Vec<3> center, double r, int order, double kappa)
2153
+ : CoefficientFunction(1, true), mlmp{make_shared<SingularMLMultiPole>(center, r, order, kappa)} { }
2154
+
2155
+ virtual double Evaluate (const BaseMappedIntegrationPoint & ip) const override
2156
+ { throw Exception("real eval not available"); }
2157
+
2158
+ virtual void Evaluate (const BaseMappedIntegrationPoint & mip, FlatVector<Complex> values) const override
2159
+ {
2160
+ values(0) = mlmp->Evaluate(mip.GetPoint());
2161
+ }
2162
+
2163
+ shared_ptr<SingularMLMultiPole> MLMP() { return mlmp; }
2164
+ };
2165
+
2166
+
2167
+ class RegularMLMultiPoleCF : public CoefficientFunction
2168
+ {
2169
+ RegularMLMultiPole mlmp;
2170
+ public:
2171
+ RegularMLMultiPoleCF (shared_ptr<SingularMLMultiPoleCF> asingmp, Vec<3> center, double r, int order)
2172
+ : CoefficientFunction(1, true), mlmp(asingmp->MLMP(), center, r, order) { }
2173
+
2174
+ virtual double Evaluate (const BaseMappedIntegrationPoint & ip) const override
2175
+ { throw Exception("real eval not available"); }
2176
+
2177
+ virtual void Evaluate (const BaseMappedIntegrationPoint & mip, FlatVector<Complex> values) const override
2178
+ {
2179
+ values(0) = mlmp.Evaluate(mip.GetPoint());
2180
+ }
2181
+
2182
+ RegularMLMultiPole & MLMP() { return mlmp; }
2183
+ };
2184
+
2185
+
2186
+ }
2187
+ #endif