sph_harm.hpp 17 KB

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