sph_harm.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. #ifndef _SCTL_SPH_HARM_HPP_
  2. #define _SCTL_SPH_HARM_HPP_
  3. #define SCTL_SHMAXDEG 1024
  4. #include <sctl/common.hpp>
  5. #include SCTL_INCLUDE(math_utils.hpp)
  6. #include SCTL_INCLUDE(mem_mgr.hpp)
  7. #include <vector>
  8. namespace SCTL_NAMESPACE {
  9. class Comm;
  10. template <class ValueType> class Vector;
  11. template <class ValueType> class Matrix;
  12. template <class ValueType> class FFT;
  13. enum class SHCArrange {
  14. // (p+1) x (p+1) complex elements in row-major order.
  15. // A : { A(0,0), A(0,1), ... A(0,p), A(1,0), ... A(p,p) }
  16. // where, A(n,m) = { Ar(n,m), Ai(n,m) } (real and imaginary parts)
  17. ALL,
  18. // (p+1)(p+2)/2 complex elements in row-major order (lower triangular part)
  19. // A : { A(0,0), A(1,0), A(1,1), A(2,0), A(2,1), A(2,2), ... A(p,p) }
  20. // where, A(n,m) = { Ar(n,m), Ai(n,m) } (real and imaginary parts)
  21. ROW_MAJOR,
  22. // (p+1)(p+1) real elements in col-major order (non-zero lower triangular part)
  23. // A : { Ar(0,0), Ar(1,0), ... Ar(p,0), Ar(1,1), ... Ar(p,1), Ai(1,1), ... Ai(p,1), ..., Ar(p,p), Ai(p,p)
  24. // where, A(n,m) = { Ar(n,m), Ai(n,m) } (real and imaginary parts)
  25. COL_MAJOR_NONZERO
  26. };
  27. template <class Real> class SphericalHarmonics{
  28. static constexpr Integer COORD_DIM = 3;
  29. public:
  30. // Scalar Spherical Harmonics
  31. /**
  32. * \brief Compute spherical harmonic coefficients from grid values.
  33. * \param[in] X Grid values {X(t0,p0), X(t0,p1), ... , X(t1,p0), X(t1,p1), ... }, where, {cos(t0), cos(t1), ... } are the Gauss-Legendre nodes of order (Nt-1) in the interval [-1,1] and {p0, p1, ... } are equispaced in [0, 2*pi].
  34. * \param[in] Nt Number of grid points \theta \in (0,pi).
  35. * \param[in] Np Number of grid points \phi \in (0,2*pi).
  36. * \param[in] p Order of spherical harmonic expansion.
  37. * \param[in] arrange Arrangement of the coefficients.
  38. * \param[out] S Spherical harmonic coefficients.
  39. */
  40. static void Grid2SHC(const Vector<Real>& X, Long Nt, Long Np, Long p, Vector<Real>& S, SHCArrange arrange);
  41. /**
  42. * \brief Evaluate grid values from spherical harmonic coefficients.
  43. * \param[in] S Spherical harmonic coefficients.
  44. * \param[in] arrange Arrangement of the coefficients.
  45. * \param[in] p Order of spherical harmonic expansion.
  46. * \param[in] Nt Number of grid points \theta \in (0,pi).
  47. * \param[in] Np Number of grid points \phi \in (0,2*pi).
  48. * \param[out] X Grid values {X(t0,p0), X(t0,p1), ... , X(t1,p0), X(t1,p1), ... }, where, {cos(t0), cos(t1), ... } are the Gauss-Legendre nodes of order (Nt-1) in the interval [-1,1] and {p0, p1, ... } are equispaced in [0, 2*pi].
  49. * \param[out] X_theta \theta derivative of X evaluated at grid points.
  50. * \param[out] X_phi \phi derivative of X evaluated at grid points.
  51. */
  52. static void SHC2Grid(const Vector<Real>& S, SHCArrange arrange, Long p, Long Nt, Long Np, Vector<Real>* X, Vector<Real>* X_theta=nullptr, Vector<Real>* X_phi=nullptr);
  53. /**
  54. * \brief Evaluate point values from spherical harmonic coefficients.
  55. * \param[in] S Spherical harmonic coefficients.
  56. * \param[in] arrange Arrangement of the coefficients.
  57. * \param[in] p Order of spherical harmonic expansion.
  58. * \param[in] theta_phi Evaluation coordinates given as {t0,p0, t1,p1, ... }.
  59. * \param[out] X Evaluated values {X0, X1, ... }.
  60. */
  61. static void SHCEval(const Vector<Real>& S, SHCArrange arrange, Long p, const Vector<Real>& theta_phi, Vector<Real>& X);
  62. static void SHC2Pole(const Vector<Real>& S, SHCArrange arrange, Long p, Vector<Real>& P);
  63. static void WriteVTK(const char* fname, const Vector<Real>* S, const Vector<Real>* f_val, SHCArrange arrange, Long p_in, Long p_out, Real period=0, const Comm& comm = Comm::World());
  64. // Vector Spherical Harmonics
  65. /**
  66. * \brief Compute vector spherical harmonic coefficients from grid values.
  67. * \param[in] X Grid values {X(t0,p0), X(t0,p1), ... , X(t1,p0), ... , Y(t0,p0), ... , Z(t0,p0), ... }, where, {cos(t0), cos(t1), ... } are the Gauss-Legendre nodes of order (Nt-1) in the interval [-1,1] and {p0, p1, ... } are equispaced in [0, 2*pi].
  68. * \param[in] Nt Number of grid points \theta \in (0,pi).
  69. * \param[in] Np Number of grid points \phi \in (0,2*pi).
  70. * \param[in] p Order of spherical harmonic expansion.
  71. * \param[in] arrange Arrangement of the coefficients.
  72. * \param[out] S Vector spherical harmonic coefficients.
  73. */
  74. static void Grid2VecSHC(const Vector<Real>& X, Long Nt, Long Np, Long p, Vector<Real>& S, SHCArrange arrange);
  75. /**
  76. * \brief Evaluate grid values from vector spherical harmonic coefficients.
  77. * \param[in] S Vector spherical harmonic coefficients.
  78. * \param[in] arrange Arrangement of the coefficients.
  79. * \param[in] p Order of spherical harmonic expansion.
  80. * \param[in] Nt Number of grid points \theta \in (0,pi).
  81. * \param[in] Np Number of grid points \phi \in (0,2*pi).
  82. * \param[out] X Grid values {X(t0,p0), X(t0,p1), ... , X(t1,p0), X(t1,p1), ... , Y(t0,p0), ... , Z(t0,p0), ... }, where, {cos(t0), cos(t1), ... } are the Gauss-Legendre nodes of order (Nt-1) in the interval [-1,1] and {p0, p1, ... } are equispaced in [0, 2*pi].
  83. */
  84. static void VecSHC2Grid(const Vector<Real>& S, SHCArrange arrange, Long p, Long Nt, Long Np, Vector<Real>& X);
  85. /**
  86. * \brief Evaluate point values from vector spherical harmonic coefficients.
  87. * \param[in] S Vector spherical harmonic coefficients.
  88. * \param[in] arrange Arrangement of the coefficients.
  89. * \param[in] p Order of spherical harmonic expansion.
  90. * \param[in] theta_phi Evaluation coordinates given as {t0,p0, t1,p1, ... }.
  91. * \param[out] X Evaluated values {X0,Y0,Z0, X1,Y1,Z1, ... }.
  92. */
  93. static void VecSHCEval(const Vector<Real>& S, SHCArrange arrange, Long p, const Vector<Real>& theta_phi, Vector<Real>& X);
  94. /**
  95. * \brief Evaluate Stokes single-layer operator at point values from the vector spherical harmonic coefficients for the density.
  96. * \param[in] S Vector spherical harmonic coefficients.
  97. * \param[in] arrange Arrangement of the coefficients.
  98. * \param[in] p Order of spherical harmonic expansion.
  99. * \param[in] Evaluation coordinates given as {x0,y0,z0, x1,y1,z1, ... }.
  100. * \param[out] U Evaluated values {Ux0,Uy0,Uz0, Ux1,Uy1,Uz1, ... }.
  101. */
  102. static void StokesEvalSL(const Vector<Real>& S, SHCArrange arrange, Long p, const Vector<Real>& coord, bool interior, Vector<Real>& U);
  103. /**
  104. * \brief Evaluate Stokes double-layer operator at point values from the vector spherical harmonic coefficients for the density.
  105. * \param[in] S Vector spherical harmonic coefficients.
  106. * \param[in] arrange Arrangement of the coefficients.
  107. * \param[in] p Order of spherical harmonic expansion.
  108. * \param[in] Evaluation coordinates given as {x0,y0,z0, x1,y1,z1, ... }.
  109. * \param[out] U Evaluated values {Ux0,Uy0,Uz0, Ux1,Uy1,Uz1, ... }.
  110. */
  111. static void StokesEvalDL(const Vector<Real>& S, SHCArrange arrange, Long p, const Vector<Real>& coord, bool interior, Vector<Real>& U);
  112. static void StokesEvalKL(const Vector<Real>& S, SHCArrange arrange, Long p, const Vector<Real>& coord, const Vector<Real>& norm, bool interior, Vector<Real>& U);
  113. static void StokesEvalKSelf(const Vector<Real>& S, SHCArrange arrange, Long p, const Vector<Real>& coord, bool interior, Vector<Real>& U);
  114. /**
  115. * \brief Nodes and weights for Gauss-Legendre quadrature rule
  116. */
  117. static const Vector<Real>& LegendreNodes(Long p1);
  118. static const Vector<Real>& LegendreWeights(Long p1);
  119. static void test_stokes() {
  120. int p = 6;
  121. int dof = 3;
  122. int Nt = 100, Np = 200;
  123. auto print_coeff = [&](Vector<Real> S) {
  124. Long idx=0;
  125. for (Long k=0;k<dof;k++) {
  126. for (Long n=0;n<=p;n++) {
  127. std::cout<<Vector<Real>(2*n+2, S.begin()+idx);
  128. idx+=2*n+2;
  129. }
  130. }
  131. std::cout<<'\n';
  132. };
  133. Vector<Real> Fcoeff(dof*(p+1)*(p+2));
  134. for (Long i=0;i<Fcoeff.Dim();i++) Fcoeff[i]=i+1;
  135. //Fcoeff = 0; Fcoeff[2] = 1;
  136. print_coeff(Fcoeff);
  137. Vector<Real> Fgrid;
  138. VecSHC2Grid(Fcoeff, sctl::SHCArrange::ROW_MAJOR, p, Nt, Np, Fgrid);
  139. Matrix<Real>(Fgrid.Dim()/3,3,Fgrid.begin(),false) = Matrix<Real>(3,Fgrid.Dim()/3,Fgrid.begin(),false).Transpose();
  140. const Vector<Real> CosTheta = LegendreNodes(Nt-1);
  141. const Vector<Real> LWeights = LegendreWeights(Nt-1);
  142. auto stokes_evalSL = [&](const Vector<Real>& trg, Vector<Real>& Sf) {
  143. Sf.ReInit(3);
  144. Sf=0;
  145. Real s = 1/(8*const_pi<Real>());
  146. for (Long i=0;i<Nt;i++) {
  147. Real cos_theta = CosTheta[i];
  148. Real sin_theta = sqrt(1-cos_theta*cos_theta);
  149. for (Long j=0;j<Np;j++) {
  150. Real cos_phi = cos(2*const_pi<Real>()*j/Np);
  151. Real sin_phi = sin(2*const_pi<Real>()*j/Np);
  152. Real qw = LWeights[i]*2*const_pi<Real>()/Np;
  153. Real x[3], dr[3], f[3];
  154. f[0] = Fgrid[(i*Np+j)*3+0];
  155. f[1] = Fgrid[(i*Np+j)*3+1];
  156. f[2] = Fgrid[(i*Np+j)*3+2];
  157. x[0] = sin_theta*cos_phi;
  158. x[1] = sin_theta*sin_phi;
  159. x[2] = cos_theta;
  160. dr[0] = x[0]-trg[0];
  161. dr[1] = x[1]-trg[1];
  162. dr[2] = x[2]-trg[2];
  163. Real oor2 = 1/(dr[0]*dr[0] + dr[1]*dr[1] + dr[2]*dr[2]);
  164. Real oor1 = sqrt(oor2);
  165. Real oor3 = oor2*oor1;
  166. Real rdotf = dr[0]*f[0]+dr[1]*f[1]+dr[2]*f[2];
  167. Sf[0] += s*(f[0]*oor1 + dr[0]*rdotf*oor3) * qw;
  168. Sf[1] += s*(f[1]*oor1 + dr[1]*rdotf*oor3) * qw;
  169. Sf[2] += s*(f[2]*oor1 + dr[2]*rdotf*oor3) * qw;
  170. }
  171. }
  172. };
  173. auto stokes_evalDL = [&](const Vector<Real>& trg, Vector<Real>& Sf) {
  174. Sf.ReInit(3);
  175. Sf=0;
  176. Real s = 6/(8*const_pi<Real>());
  177. for (Long i=0;i<Nt;i++) {
  178. Real cos_theta = CosTheta[i];
  179. Real sin_theta = sqrt(1-cos_theta*cos_theta);
  180. for (Long j=0;j<Np;j++) {
  181. Real cos_phi = cos(2*const_pi<Real>()*j/Np);
  182. Real sin_phi = sin(2*const_pi<Real>()*j/Np);
  183. Real qw = LWeights[i]*2*const_pi<Real>()/Np;
  184. Real x[3], dr[3], f[3], n[3];
  185. f[0] = Fgrid[(i*Np+j)*3+0];
  186. f[1] = Fgrid[(i*Np+j)*3+1];
  187. f[2] = Fgrid[(i*Np+j)*3+2];
  188. x[0] = sin_theta*cos_phi;
  189. x[1] = sin_theta*sin_phi;
  190. x[2] = cos_theta;
  191. dr[0] = x[0]-trg[0];
  192. dr[1] = x[1]-trg[1];
  193. dr[2] = x[2]-trg[2];
  194. n[0] = x[0];
  195. n[1] = x[1];
  196. n[2] = x[2];
  197. Real oor2 = 1/(dr[0]*dr[0] + dr[1]*dr[1] + dr[2]*dr[2]);
  198. Real oor5 = oor2*oor2*sqrt(oor2);
  199. Real rdotn = dr[0]*n[0]+dr[1]*n[1]+dr[2]*n[2];
  200. Real rdotf = dr[0]*f[0]+dr[1]*f[1]+dr[2]*f[2];
  201. Sf[0] += -s*dr[0]*rdotn*rdotf*oor5 * qw;
  202. Sf[1] += -s*dr[1]*rdotn*rdotf*oor5 * qw;
  203. Sf[2] += -s*dr[2]*rdotn*rdotf*oor5 * qw;
  204. }
  205. }
  206. };
  207. auto stokes_evalKL = [&](const Vector<Real>& trg, const Vector<Real>& nor, Vector<Real>& Sf) {
  208. Sf.ReInit(3);
  209. Sf=0;
  210. Real scal = 1/(8*const_pi<Real>());
  211. for (Long i=0;i<Nt;i++) {
  212. Real cos_theta = CosTheta[i];
  213. Real sin_theta = sqrt(1-cos_theta*cos_theta);
  214. for (Long j=0;j<Np;j++) {
  215. Real cos_phi = cos(2*const_pi<Real>()*j/Np);
  216. Real sin_phi = sin(2*const_pi<Real>()*j/Np);
  217. Real qw = LWeights[i]*2*const_pi<Real>()/Np; // quadrature weights * area-element
  218. Real f[3]; // source density
  219. f[0] = Fgrid[(i*Np+j)*3+0];
  220. f[1] = Fgrid[(i*Np+j)*3+1];
  221. f[2] = Fgrid[(i*Np+j)*3+2];
  222. Real x[3]; // source coordinates
  223. x[0] = sin_theta*cos_phi;
  224. x[1] = sin_theta*sin_phi;
  225. x[2] = cos_theta;
  226. Real dr[3];
  227. dr[0] = trg[0] - x[0];
  228. dr[1] = trg[1] - x[1];
  229. dr[2] = trg[2] - x[2];
  230. Real invr = 1 / sqrt(dr[0]*dr[0] + dr[1]*dr[1] + dr[2]*dr[2]);
  231. Real invr2 = invr*invr;
  232. Real invr3 = invr2*invr;
  233. Real invr5 = invr2*invr3;
  234. Real fdotr = dr[0]*f[0]+dr[1]*f[1]+dr[2]*f[2];
  235. Real du[9];
  236. du[0] = ( fdotr*invr3 - 3*dr[0]*dr[0]*fdotr*invr5) * scal;
  237. du[1] = ((dr[0]*f[1]-dr[1]*f[0])*invr3 - 3*dr[0]*dr[1]*fdotr*invr5) * scal;
  238. du[2] = ((dr[0]*f[2]-dr[2]*f[0])*invr3 - 3*dr[0]*dr[2]*fdotr*invr5) * scal;
  239. du[3] = ((dr[1]*f[0]-dr[0]*f[1])*invr3 - 3*dr[1]*dr[0]*fdotr*invr5) * scal;
  240. du[4] = ( fdotr*invr3 - 3*dr[1]*dr[1]*fdotr*invr5) * scal;
  241. du[5] = ((dr[1]*f[2]-dr[2]*f[1])*invr3 - 3*dr[1]*dr[2]*fdotr*invr5) * scal;
  242. du[6] = ((dr[2]*f[0]-dr[0]*f[2])*invr3 - 3*dr[2]*dr[0]*fdotr*invr5) * scal;
  243. du[7] = ((dr[2]*f[1]-dr[1]*f[2])*invr3 - 3*dr[2]*dr[1]*fdotr*invr5) * scal;
  244. du[8] = ( fdotr*invr3 - 3*dr[2]*dr[2]*fdotr*invr5) * scal;
  245. Real p = (2*fdotr*invr3) * scal;
  246. Real K[9];
  247. K[0] = du[0] + du[0] - p; K[1] = du[1] + du[3] - 0; K[2] = du[2] + du[6] - 0;
  248. K[3] = du[3] + du[1] - 0; K[4] = du[4] + du[4] - p; K[5] = du[5] + du[7] - 0;
  249. K[6] = du[6] + du[2] - 0; K[7] = du[7] + du[5] - 0; K[8] = du[8] + du[8] - p;
  250. Sf[0] += (K[0]*nor[0] + K[1]*nor[1] + K[2]*nor[2]) * qw;
  251. Sf[1] += (K[3]*nor[0] + K[4]*nor[1] + K[5]*nor[2]) * qw;
  252. Sf[2] += (K[6]*nor[0] + K[7]*nor[1] + K[8]*nor[2]) * qw;
  253. }
  254. }
  255. };
  256. for (Long i = 0; i < 40; i++) { // Evaluate
  257. Real R0 = (0.01 + i/20.0);
  258. Vector<Real> x(3), n(3);
  259. x[0] = drand48()-0.5;
  260. x[1] = drand48()-0.5;
  261. x[2] = drand48()-0.5;
  262. n[0] = drand48()-0.5;
  263. n[1] = drand48()-0.5;
  264. n[2] = drand48()-0.5;
  265. Real R = sqrt<Real>(x[0]*x[0]+x[1]*x[1]+x[2]*x[2]);
  266. x[0] *= R0 / R;
  267. x[1] *= R0 / R;
  268. x[2] *= R0 / R;
  269. Vector<Real> Sf, Sf_;
  270. Vector<Real> Df, Df_;
  271. Vector<Real> Kf, Kf_;
  272. StokesEvalSL(Fcoeff, sctl::SHCArrange::ROW_MAJOR, p, x, R0<1, Sf);
  273. StokesEvalDL(Fcoeff, sctl::SHCArrange::ROW_MAJOR, p, x, R0<1, Df);
  274. StokesEvalKL(Fcoeff, sctl::SHCArrange::ROW_MAJOR, p, x, n, R0<1, Kf);
  275. stokes_evalSL(x, Sf_);
  276. stokes_evalDL(x, Df_);
  277. stokes_evalKL(x, n, Kf_);
  278. auto max_val = [](const Vector<Real>& v) {
  279. Real max_v = 0;
  280. for (auto& x : v) max_v = std::max(max_v, fabs(x));
  281. return max_v;
  282. };
  283. auto errSL = (Sf-Sf_)/max_val(Sf+0.01);
  284. auto errDL = (Df-Df_)/max_val(Df+0.01);
  285. auto errKL = (Kf-Kf_)/max_val(Kf+0.01);
  286. for (auto& x:errSL) x=log(fabs(x))/log(10);
  287. for (auto& x:errDL) x=log(fabs(x))/log(10);
  288. for (auto& x:errKL) x=log(fabs(x))/log(10);
  289. std::cout<<"R = "<<(0.01 + i/20.0)<<"; SL-error = ";
  290. std::cout<<errSL;
  291. std::cout<<"R = "<<(0.01 + i/20.0)<<"; DL-error = ";
  292. std::cout<<errDL;
  293. std::cout<<"R = "<<(0.01 + i/20.0)<<"; KL-error = ";
  294. std::cout<<errKL;
  295. }
  296. Clear();
  297. }
  298. static void test() {
  299. int p = 3;
  300. int dof = 1;
  301. int Nt = p+1, Np = 2*p+1;
  302. auto print_coeff = [&](Vector<Real> S) {
  303. Long idx=0;
  304. for (Long k=0;k<dof;k++) {
  305. for (Long n=0;n<=p;n++) {
  306. std::cout<<Vector<Real>(2*n+2, S.begin()+idx);
  307. idx+=2*n+2;
  308. }
  309. }
  310. std::cout<<'\n';
  311. };
  312. Vector<Real> theta_phi;
  313. { // Set theta_phi
  314. Vector<Real> leg_nodes = LegendreNodes(Nt-1);
  315. for (Long i=0;i<Nt;i++) {
  316. for (Long j=0;j<Np;j++) {
  317. theta_phi.PushBack(acos(leg_nodes[i]));
  318. theta_phi.PushBack(j * 2 * const_pi<Real>() / Np);
  319. }
  320. }
  321. }
  322. int Ncoeff = (p + 1) * (p + 1);
  323. Vector<Real> Xcoeff(dof * Ncoeff), Xgrid;
  324. for (int i=0;i<Xcoeff.Dim();i++) Xcoeff[i]=i+1;
  325. SHC2Grid(Xcoeff, sctl::SHCArrange::COL_MAJOR_NONZERO, p, Nt, Np, &Xgrid);
  326. std::cout<<Matrix<Real>(Nt*dof, Np, Xgrid.begin())<<'\n';
  327. {
  328. Vector<Real> val;
  329. SHCEval(Xcoeff, sctl::SHCArrange::COL_MAJOR_NONZERO, p, theta_phi, val);
  330. Matrix<Real>(dof, val.Dim()/dof, val.begin(), false) = Matrix<Real>(val.Dim()/dof, dof, val.begin()).Transpose();
  331. std::cout<<Matrix<Real>(val.Dim()/Np, Np, val.begin()) - Matrix<Real>(Nt*dof, Np, Xgrid.begin())+1e-10<<'\n';
  332. }
  333. Grid2SHC(Xgrid, Nt, Np, p, Xcoeff, sctl::SHCArrange::ROW_MAJOR);
  334. print_coeff(Xcoeff);
  335. //SphericalHarmonics<Real>::WriteVTK("test", nullptr, &Xcoeff, sctl::SHCArrange::ROW_MAJOR, p, 32);
  336. Clear();
  337. }
  338. /**
  339. * \brief Clear all precomputed data. This must be done before the program exits to avoid memory leaks.
  340. */
  341. static void Clear() { MatrixStore().Resize(0); }
  342. private:
  343. // Probably don't work anymore, need to be updated :(
  344. static void SHC2GridTranspose(const Vector<Real>& X, Long p0, Long p1, Vector<Real>& S);
  345. static void RotateAll(const Vector<Real>& S, Long p0, Long dof, Vector<Real>& S_);
  346. static void RotateTranspose(const Vector<Real>& S_, Long p0, Long dof, Vector<Real>& S);
  347. static void StokesSingularInteg(const Vector<Real>& S, Long p0, Long p1, Vector<Real>* SLMatrix=nullptr, Vector<Real>* DLMatrix=nullptr);
  348. static void Grid2SHC_(const Vector<Real>& X, Long Nt, Long Np, Long p, Vector<Real>& B1);
  349. static void SHCArrange0(const Vector<Real>& B1, Long p, Vector<Real>& S, SHCArrange arrange);
  350. static void SHC2Grid_(const Vector<Real>& S, Long p, Long Nt, Long Np, Vector<Real>* X, Vector<Real>* X_theta=nullptr, Vector<Real>* X_phi=nullptr);
  351. static void SHCArrange1(const Vector<Real>& S_in, SHCArrange arrange_out, Long p, Vector<Real>& S_out);
  352. /**
  353. * \brief Computes all the Associated Legendre Polynomials (normalized) up to the specified degree.
  354. * \param[in] degree The degree up to which the Legendre polynomials have to be computed.
  355. * \param[in] X The input values for which the polynomials have to be computed.
  356. * \param[in] N The number of input points.
  357. * \param[out] poly_val The output array of size (degree+1)*(degree+2)*N/2 containing the computed polynomial values.
  358. * The output values are in the order:
  359. * P(n,m)[i] => {P(0,0)[0], P(0,0)[1], ..., P(0,0)[N-1], P(1,0)[0], ..., P(1,0)[N-1],
  360. * P(2,0)[0], ..., P(degree,0)[N-1], P(1,1)[0], ...,P(2,1)[0], ..., P(degree,degree)[N-1]}
  361. */
  362. static void LegPoly(Vector<Real>& poly_val, const Vector<Real>& X, Long degree);
  363. static void LegPoly_(Vector<Real>& poly_val, const Vector<Real>& theta, Long degree);
  364. static void LegPolyDeriv(Vector<Real>& poly_val, const Vector<Real>& X, Long degree);
  365. static void LegPolyDeriv_(Vector<Real>& poly_val, const Vector<Real>& X, Long degree);
  366. static const Vector<Real>& SingularWeights(Long p1);
  367. static const Matrix<Real>& MatFourier(Long p0, Long p1);
  368. static const Matrix<Real>& MatFourierInv(Long p0, Long p1);
  369. static const Matrix<Real>& MatFourierGrad(Long p0, Long p1);
  370. static const FFT<Real>& OpFourier(Long Np);
  371. static const FFT<Real>& OpFourierInv(Long Np);
  372. static const std::vector<Matrix<Real>>& MatLegendre(Long p0, Long p1);
  373. static const std::vector<Matrix<Real>>& MatLegendreInv(Long p0, Long p1);
  374. static const std::vector<Matrix<Real>>& MatLegendreGrad(Long p0, Long p1);
  375. // Evaluate all Spherical Harmonic basis functions up to order p at (theta, phi) coordinates.
  376. static void SHBasisEval(Long p, const Vector<Real>& theta_phi, Matrix<Real>& M);
  377. static void VecSHBasisEval(Long p, const Vector<Real>& theta_phi, Matrix<Real>& M);
  378. static const std::vector<Matrix<Real>>& MatRotate(Long p0);
  379. template <bool SLayer, bool DLayer> static void StokesSingularInteg_(const Vector<Real>& X0, Long p0, Long p1, Vector<Real>& SL, Vector<Real>& DL);
  380. struct MatrixStorage{
  381. MatrixStorage() : Mfft_(NullIterator<FFT<Real>>()), Mfftinv_(NullIterator<FFT<Real>>()) {
  382. Resize(SCTL_SHMAXDEG);
  383. }
  384. ~MatrixStorage() {
  385. Resize(0);
  386. }
  387. MatrixStorage(const MatrixStorage&) = delete;
  388. MatrixStorage& operator=(const MatrixStorage&) = delete;
  389. void Resize(Long size){
  390. Qx_ .resize(size);
  391. Qw_ .resize(size);
  392. Sw_ .resize(size);
  393. Mf_ .resize(size*size);
  394. Mdf_.resize(size*size);
  395. Ml_ .resize(size*size);
  396. Mdl_.resize(size*size);
  397. Mr_ .resize(size);
  398. Mfinv_ .resize(size*size);
  399. Mlinv_ .resize(size*size);
  400. aligned_delete(Mfft_);
  401. aligned_delete(Mfftinv_);
  402. if (size) {
  403. Mfft_ = aligned_new<FFT<Real>>(size);
  404. Mfftinv_ = aligned_new<FFT<Real>>(size);
  405. } else {
  406. Mfft_ = NullIterator<FFT<Real>>();
  407. Mfftinv_ = NullIterator<FFT<Real>>();
  408. }
  409. }
  410. std::vector<Vector<Real>> Qx_;
  411. std::vector<Vector<Real>> Qw_;
  412. std::vector<Vector<Real>> Sw_;
  413. std::vector<Matrix<Real>> Mf_ ;
  414. std::vector<Matrix<Real>> Mdf_;
  415. std::vector<std::vector<Matrix<Real>>> Ml_ ;
  416. std::vector<std::vector<Matrix<Real>>> Mdl_;
  417. std::vector<std::vector<Matrix<Real>>> Mr_;
  418. std::vector<Matrix<Real>> Mfinv_ ;
  419. std::vector<std::vector<Matrix<Real>>> Mlinv_ ;
  420. Iterator<FFT<Real>> Mfft_;
  421. Iterator<FFT<Real>> Mfftinv_;
  422. };
  423. static MatrixStorage& MatrixStore(){
  424. static MatrixStorage storage;
  425. if (!storage.Qx_.size()) storage.Resize(SCTL_SHMAXDEG);
  426. return storage;
  427. }
  428. };
  429. //template class SphericalHarmonics<double>;
  430. } // end namespace
  431. #include SCTL_INCLUDE(sph_harm.txx)
  432. #endif // _SCTL_SPH_HARM_HPP_