sph_harm.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  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. static void Grid2SHC(const Vector<Real>& X_in, Long Nt_in, Long Np_in, Long p_out, Vector<Real>& S_out, SHCArrange arrange_out);
  27. static void SHC2Grid(const Vector<Real>& S_in, SHCArrange arrange_in, Long p_in, Long Nt_out, Long Np_out, Vector<Real>* X_out, Vector<Real>* X_theta_out=nullptr, Vector<Real>* X_phi_out=nullptr);
  28. static void SHCEval(const Vector<Real>& S_in, SHCArrange arrange_in, Long p_in, const Vector<Real>& theta_phi_in, Vector<Real>& X_out);
  29. static void SHC2Pole(const Vector<Real>& S_in, SHCArrange arrange_in, Long p_in, Vector<Real>& P_out);
  30. 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());
  31. // Vector Spherical Harmonics
  32. static void Grid2VecSHC(const Vector<Real>& X_in, Long Nt_in, Long Np_in, Long p_out, Vector<Real>& S_out, SHCArrange arrange_out);
  33. static void VecSHC2Grid(const Vector<Real>& S_in, SHCArrange arrange_in, Long p_in, Long Nt_out, Long Np_out, Vector<Real>& X_out);
  34. static void VecSHCEval(const Vector<Real>& S_in, SHCArrange arrange_in, Long p_in, const Vector<Real>& theta_phi_in, Vector<Real>& X_out);
  35. static void StokesEvalSL(const Vector<Real>& S_in, SHCArrange arrange_in, Long p_in, const Vector<Real>& theta_phi_in, Vector<Real>& X_out);
  36. static void StokesEvalDL(const Vector<Real>& S_in, SHCArrange arrange_in, Long p_in, const Vector<Real>& theta_phi_in, Vector<Real>& X_out);
  37. static void test3() {
  38. int k=0, n=1, m=0;
  39. for (int k=0;k<3;k++) {
  40. for (int n=0;n<3;n++) {
  41. for (int m=0;m<=n;m++) {
  42. int p=5;
  43. int dof = 1;
  44. int Nt = p+1;
  45. int Np = 2*p+2;
  46. int Ngrid = Nt * Np;
  47. int Ncoeff = (p + 1) * (p + 2);
  48. Vector<Real> sin_theta, cos_theta = LegendreNodes(Nt-1);
  49. for (Long i=0;i<cos_theta.Dim();i++) sin_theta.PushBack(sqrt<Real>(1-cos_theta[i]*cos_theta[i]));
  50. auto print_coeff = [&](Vector<Real> S) {
  51. Long idx=0;
  52. Long dof = S.Dim() / Ncoeff;
  53. for (Long k=0;k<dof;k++) {
  54. for (Long n=0;n<=p;n++) {
  55. std::cout<<Vector<Real>(2*n+2, S.begin()+idx);
  56. idx+=2*n+2;
  57. }
  58. }
  59. std::cout<<'\n';
  60. };
  61. Vector<Real> coeff(dof*Ncoeff); coeff=0;
  62. auto write_coeff = [&](Vector<Real>& coeff, Complex<Real> c, Long k, Long n, Long m) {
  63. if (0 <= m && m <= n && n <= p) {
  64. Long idx = k*Ncoeff + n*(n+1)+ 2*m;
  65. coeff[idx+0] = c.real;
  66. coeff[idx+1] = c.imag;
  67. }
  68. };
  69. write_coeff(coeff, Complex<Real>(1,1.3),0,n,m);
  70. //print_coeff(coeff);
  71. Vector<Real> Y, Yt, Yp;
  72. SHC2Grid(coeff, SHCArrange::ROW_MAJOR, p, Nt, Np, &Y, &Yt, &Yp);
  73. //std::cout<<Matrix<Real>(Nt, Np, Y.begin())<<'\n';
  74. //std::cout<<Matrix<Real>(Nt, Np, Yt.begin())<<'\n';
  75. //std::cout<<Matrix<Real>(Nt, Np, Yp.begin())<<'\n';
  76. Vector<Real> gradYt = Yt;
  77. Vector<Real> gradYp = Yp;
  78. for (Long i=0;i<Nt;i++) {
  79. for (Long j=0;j<Np;j++) {
  80. gradYp[i*Np+j] /= sin_theta[i];
  81. }
  82. }
  83. Vector<Real> Vr, Vt, Vp;
  84. Vector<Real> Wr, Wt, Wp;
  85. Vector<Real> Xr, Xt, Xp;
  86. Vr = Y*(-n-1);
  87. Vt = gradYt;
  88. Vp = gradYp;
  89. Wr = Y*n;
  90. Wt = gradYt;
  91. Wp = gradYp;
  92. Xr = Y*0;
  93. Xt = gradYp;
  94. Xp = gradYt * (-1);
  95. Vector<Real> SS(COORD_DIM * Ngrid);
  96. SS=0;
  97. if (k == 0) {
  98. Vector<Real>(Ngrid, SS.begin() + 0*Ngrid, false) = Vr;
  99. Vector<Real>(Ngrid, SS.begin() + 1*Ngrid, false) = Vt;
  100. Vector<Real>(Ngrid, SS.begin() + 2*Ngrid, false) = Vp;
  101. }
  102. if (k == 1) {
  103. Vector<Real>(Ngrid, SS.begin() + 0*Ngrid, false) = Wr;
  104. Vector<Real>(Ngrid, SS.begin() + 1*Ngrid, false) = Wt;
  105. Vector<Real>(Ngrid, SS.begin() + 2*Ngrid, false) = Wp;
  106. }
  107. if (k == 2) {
  108. Vector<Real>(Ngrid, SS.begin() + 0*Ngrid, false) = Xr;
  109. Vector<Real>(Ngrid, SS.begin() + 1*Ngrid, false) = Xt;
  110. Vector<Real>(Ngrid, SS.begin() + 2*Ngrid, false) = Xp;
  111. }
  112. Vector<Real> SSS;
  113. {
  114. Vector<Real> coeff(COORD_DIM*Ncoeff);
  115. coeff=0;
  116. write_coeff(coeff, Complex<Real>(1,1.3),k,n,m);
  117. //print_coeff(coeff);
  118. VecSHC2Grid(coeff, SHCArrange::ROW_MAJOR, p, Nt, Np, SSS);
  119. }
  120. //std::cout<<Matrix<Real>(COORD_DIM*Nt, Np, SS.begin())<<'\n';
  121. //std::cout<<Matrix<Real>(COORD_DIM*Nt, Np, SSS.begin())<<'\n';
  122. //std::cout<<Matrix<Real>(COORD_DIM*Nt, Np, SSS.begin()) - Matrix<Real>(COORD_DIM*Nt, Np, SS.begin())<<'\n';
  123. auto err=SSS-SS;
  124. Real max_err=0;
  125. for (auto x:err) max_err=std::max(max_err, fabs(x));
  126. std::cout<<max_err<<' ';
  127. }
  128. std::cout<<'\n';
  129. }
  130. std::cout<<'\n';
  131. }
  132. Clear();
  133. }
  134. static void test2() {
  135. int p = 6;
  136. int dof = 3;
  137. int Nt = p+1, Np = 2*p+2;
  138. auto print_coeff = [&](Vector<Real> S) {
  139. Long idx=0;
  140. for (Long k=0;k<dof;k++) {
  141. for (Long n=0;n<=p;n++) {
  142. std::cout<<Vector<Real>(2*n+2, S.begin()+idx);
  143. idx+=2*n+2;
  144. }
  145. }
  146. std::cout<<'\n';
  147. };
  148. Vector<Real> f(dof * Nt * Np);
  149. { // Set f
  150. Vector<Real> leg_nodes = LegendreNodes(Nt-1);
  151. for (Long i = 0; i < Nt; i++) {
  152. for (Long j = 0; j < Np; j++) {
  153. Real cos_theta = leg_nodes[i];
  154. Real sin_theta = sqrt<Real>(1-cos_theta*cos_theta);
  155. Real phi = 2 * const_pi<Real>() * j / Np;
  156. Real r = 1;
  157. Real x = r * cos_theta;
  158. Real y = r * sin_theta * cos<Real>(phi);
  159. Real z = r * sin_theta * sin<Real>(phi);
  160. // Unit vectors in polar coordinates
  161. Real xr = cos_theta;
  162. Real yr = sin_theta * cos<Real>(phi);
  163. Real zr = sin_theta * sin<Real>(phi);
  164. Real xt = - sin_theta;
  165. Real yt = cos_theta * cos<Real>(phi);
  166. Real zt = cos_theta * sin<Real>(phi);
  167. Real xp = 0;
  168. Real yp = - sin<Real>(phi);
  169. Real zp = cos<Real>(phi);
  170. ////////////////////////////////////
  171. Real fx = 0;
  172. Real fy = 0;
  173. Real fz = 1;
  174. f[(0 * Nt + i) * Np + j] = (fx * xr + fy * yr + fz * zr);
  175. f[(1 * Nt + i) * Np + j] = (fx * xt + fy * yt + fz * zt);
  176. f[(2 * Nt + i) * Np + j] = (fx * xp + fy * yp + fz * zp);
  177. f[(0 * Nt + i) * Np + j] = 0; //sin(phi) * sin_theta;
  178. f[(1 * Nt + i) * Np + j] = 1; //sin(phi) * cos_theta; // * sin_theta;
  179. f[(2 * Nt + i) * Np + j] = 1; //cos(phi) ; // * sin_theta;
  180. }
  181. }
  182. }
  183. Vector<Real> f_coeff;
  184. Grid2VecSHC(f, Nt, Np, p, f_coeff, sctl::SHCArrange::ROW_MAJOR);
  185. if(0){
  186. Vector<Real> f_, f_coeff_, f__;
  187. SHC2Grid(f_coeff, sctl::SHCArrange::ROW_MAJOR, p, Nt, Np, &f_);
  188. Grid2SHC(f_, Nt, Np, p, f_coeff_, sctl::SHCArrange::ROW_MAJOR);
  189. SHC2Grid(f_coeff_, sctl::SHCArrange::ROW_MAJOR, p, Nt, Np, &f__);
  190. std::cout<<Matrix<Real>(dof*Nt, Np, f.begin())-Matrix<Real>(dof*Nt, Np, f_.begin())<<'\n';
  191. std::cout<<Matrix<Real>(dof*Nt, Np, f_.begin())-Matrix<Real>(dof*Nt, Np, f__.begin())<<'\n';
  192. }
  193. if(0)
  194. for (Long i = 0; i < 20; i++) { // Evaluate
  195. Vector<Real> r_cos_theta_phi;
  196. r_cos_theta_phi.PushBack(drand48() + (i<10?0:2)); // [1, inf)
  197. r_cos_theta_phi.PushBack(drand48() * 2 - 1); // [-1, 1]
  198. r_cos_theta_phi.PushBack(drand48() * 2 * const_pi<Real>()); // [0, 2*pi]
  199. Vector<Real> Df;
  200. StokesEvalDL(f_coeff, sctl::SHCArrange::ROW_MAJOR, p, r_cos_theta_phi, Df);
  201. //VecSHCEval(f_coeff, sctl::SHCArrange::ROW_MAJOR, p, r_cos_theta_phi, Df);
  202. //std::cout<<r_cos_theta_phi;
  203. std::cout<<Df[0]*Df[0] + Df[1]*Df[1] + Df[2]*Df[2]<<'\n';
  204. }
  205. print_coeff(f_coeff);
  206. Clear();
  207. }
  208. static void test() {
  209. int p = 4;
  210. int dof = 3;
  211. int Nt = p+1, Np = 2*p;
  212. auto print_coeff = [&](Vector<Real> S) {
  213. Long idx=0;
  214. for (Long k=0;k<dof;k++) {
  215. for (Long n=0;n<=p;n++) {
  216. std::cout<<Vector<Real>(2*n+2, S.begin()+idx);
  217. idx+=2*n+2;
  218. }
  219. }
  220. std::cout<<'\n';
  221. };
  222. Vector<Real> r_theta_phi, theta_phi;
  223. { // Set r_theta_phi, theta_phi
  224. Vector<Real> leg_nodes = LegendreNodes(Nt-1);
  225. for (Long i=0;i<Nt;i++) {
  226. for (Long j=0;j<Np;j++) {
  227. r_theta_phi.PushBack(1);
  228. r_theta_phi.PushBack(leg_nodes[i]);
  229. r_theta_phi.PushBack(j * 2 * const_pi<Real>() / Np);
  230. theta_phi.PushBack(leg_nodes[i]);
  231. theta_phi.PushBack(j * 2 * const_pi<Real>() / Np);
  232. }
  233. }
  234. }
  235. int Ncoeff = (p + 1) * (p + 1);
  236. Vector<Real> Xcoeff(dof * Ncoeff), Xgrid;
  237. for (int i=0;i<Xcoeff.Dim();i++) Xcoeff[i]=i+1;
  238. Xcoeff=0;
  239. Xcoeff[0]=1;
  240. //Xcoeff[12]=1;
  241. //for (int i=0*Ncoeff;i<1*Ncoeff;i++) Xcoeff[i]=i+1;
  242. VecSHC2Grid(Xcoeff, sctl::SHCArrange::COL_MAJOR_NONZERO, p, Nt, Np, Xgrid);
  243. std::cout<<"Y0_=[\n";
  244. std::cout<<Matrix<Real>(Nt, Np, Xgrid.begin()+Nt*Np*0)<<"];\n";
  245. std::cout<<"Y1_=[\n";
  246. std::cout<<Matrix<Real>(Nt, Np, Xgrid.begin()+Nt*Np*1)<<"];\n";
  247. std::cout<<"Y2_=[\n";
  248. std::cout<<Matrix<Real>(Nt, Np, Xgrid.begin()+Nt*Np*2)<<"];\n";
  249. if (0) {
  250. Vector<Real> val;
  251. VecSHCEval(Xcoeff, sctl::SHCArrange::COL_MAJOR_NONZERO, p, theta_phi, val);
  252. //StokesEvalSL(Xcoeff, sctl::SHCArrange::COL_MAJOR_NONZERO, p, r_theta_phi, val);
  253. Matrix<Real>(dof, val.Dim()/dof, val.begin(), false) = Matrix<Real>(val.Dim()/dof, dof, val.begin()).Transpose();
  254. std::cout<<Matrix<Real>(val.Dim()/Np, Np, val.begin())<<'\n';
  255. std::cout<<Matrix<Real>(val.Dim()/Np, Np, val.begin()) - Matrix<Real>(Nt*dof, Np, Xgrid.begin())<<'\n';
  256. }
  257. Grid2VecSHC(Xgrid, Nt, Np, p, Xcoeff, sctl::SHCArrange::ROW_MAJOR);
  258. print_coeff(Xcoeff);
  259. //SphericalHarmonics<Real>::WriteVTK("test", nullptr, &Xcoeff, sctl::SHCArrange::ROW_MAJOR, p, 32);
  260. Clear();
  261. }
  262. static void Clear() { MatrixStore().Resize(0); }
  263. private:
  264. // Probably don't work anymore, need to be updated :(
  265. static void SHC2GridTranspose(const Vector<Real>& X, Long p0, Long p1, Vector<Real>& S);
  266. static void RotateAll(const Vector<Real>& S, Long p0, Long dof, Vector<Real>& S_);
  267. static void RotateTranspose(const Vector<Real>& S_, Long p0, Long dof, Vector<Real>& S);
  268. static void StokesSingularInteg(const Vector<Real>& S, Long p0, Long p1, Vector<Real>* SLMatrix=nullptr, Vector<Real>* DLMatrix=nullptr);
  269. static void Grid2SHC_(const Vector<Real>& X, Long Nt, Long Np, Long p, Vector<Real>& B1);
  270. static void SHCArrange0(const Vector<Real>& B1, Long p, Vector<Real>& S, SHCArrange arrange);
  271. 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);
  272. static void SHCArrange1(const Vector<Real>& S_in, SHCArrange arrange_out, Long p, Vector<Real>& S_out);
  273. /**
  274. * \brief Computes all the Associated Legendre Polynomials (normalized) up to the specified degree.
  275. * \param[in] degree The degree up to which the Legendre polynomials have to be computed.
  276. * \param[in] X The input values for which the polynomials have to be computed.
  277. * \param[in] N The number of input points.
  278. * \param[out] poly_val The output array of size (degree+1)*(degree+2)*N/2 containing the computed polynomial values.
  279. * The output values are in the order:
  280. * 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],
  281. * P(2,0)[0], ..., P(degree,0)[N-1], P(1,1)[0], ...,P(2,1)[0], ..., P(degree,degree)[N-1]}
  282. */
  283. static void LegPoly(Vector<Real>& poly_val, const Vector<Real>& X, Long degree);
  284. static void LegPolyDeriv(Vector<Real>& poly_val, const Vector<Real>& X, Long degree);
  285. static const Vector<Real>& LegendreNodes(Long p1);
  286. static const Vector<Real>& LegendreWeights(Long p1);
  287. static const Vector<Real>& SingularWeights(Long p1);
  288. static const Matrix<Real>& MatFourier(Long p0, Long p1);
  289. static const Matrix<Real>& MatFourierInv(Long p0, Long p1);
  290. static const Matrix<Real>& MatFourierGrad(Long p0, Long p1);
  291. static const FFT<Real>& OpFourier(Long Np);
  292. static const FFT<Real>& OpFourierInv(Long Np);
  293. static const std::vector<Matrix<Real>>& MatLegendre(Long p0, Long p1);
  294. static const std::vector<Matrix<Real>>& MatLegendreInv(Long p0, Long p1);
  295. static const std::vector<Matrix<Real>>& MatLegendreGrad(Long p0, Long p1);
  296. // Evaluate all Spherical Harmonic basis functions up to order p at (theta, phi) coordinates.
  297. static void SHBasisEval(Long p, const Vector<Real>& cos_theta_phi, Matrix<Real>& M);
  298. static void VecSHBasisEval(Long p, const Vector<Real>& cos_theta_phi, Matrix<Real>& M);
  299. static const std::vector<Matrix<Real>>& MatRotate(Long p0);
  300. template <bool SLayer, bool DLayer> static void StokesSingularInteg_(const Vector<Real>& X0, Long p0, Long p1, Vector<Real>& SL, Vector<Real>& DL);
  301. struct MatrixStorage{
  302. MatrixStorage(){
  303. const Long size = SCTL_SHMAXDEG;
  304. Resize(size);
  305. }
  306. void Resize(Long size){
  307. Qx_ .resize(size);
  308. Qw_ .resize(size);
  309. Sw_ .resize(size);
  310. Mf_ .resize(size*size);
  311. Mdf_.resize(size*size);
  312. Ml_ .resize(size*size);
  313. Mdl_.resize(size*size);
  314. Mr_ .resize(size);
  315. Mfinv_ .resize(size*size);
  316. Mlinv_ .resize(size*size);
  317. Mfft_.resize(size);
  318. Mfftinv_.resize(size);
  319. }
  320. std::vector<Vector<Real>> Qx_;
  321. std::vector<Vector<Real>> Qw_;
  322. std::vector<Vector<Real>> Sw_;
  323. std::vector<Matrix<Real>> Mf_ ;
  324. std::vector<Matrix<Real>> Mdf_;
  325. std::vector<std::vector<Matrix<Real>>> Ml_ ;
  326. std::vector<std::vector<Matrix<Real>>> Mdl_;
  327. std::vector<std::vector<Matrix<Real>>> Mr_;
  328. std::vector<Matrix<Real>> Mfinv_ ;
  329. std::vector<std::vector<Matrix<Real>>> Mlinv_ ;
  330. std::vector<FFT<Real>> Mfft_;
  331. std::vector<FFT<Real>> Mfftinv_;
  332. };
  333. static MatrixStorage& MatrixStore(){
  334. static MatrixStorage storage;
  335. return storage;
  336. }
  337. };
  338. template class SphericalHarmonics<double>;
  339. } // end namespace
  340. #include SCTL_INCLUDE(sph_harm.txx)
  341. #endif // _SCTL_SPH_HARM_HPP_