#ifndef _SCTL_SPH_HARM_HPP_ #define _SCTL_SPH_HARM_HPP_ #define SCTL_SHMAXDEG 1024 #include SCTL_INCLUDE(matrix.hpp) #include SCTL_INCLUDE(fft_wrapper.hpp) #include SCTL_INCLUDE(common.hpp) namespace SCTL_NAMESPACE { enum class SHCArrange { // (p+1) x (p+1) complex elements in row-major order. // A : { A(0,0), A(0,1), ... A(0,p), A(1,0), ... A(p,p) } // where, A(n,m) = { Ar(n,m), Ai(n,m) } (real and imaginary parts) ALL, // (p+1)(p+2)/2 complex elements in row-major order (lower triangular part) // A : { A(0,0), A(1,0), A(1,1), A(2,0), A(2,1), A(2,2), ... A(p,p) } // where, A(n,m) = { Ar(n,m), Ai(n,m) } (real and imaginary parts) ROW_MAJOR, // (p+1)(p+1) real elements in col-major order (non-zero lower triangular part) // 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) // where, A(n,m) = { Ar(n,m), Ai(n,m) } (real and imaginary parts) COL_MAJOR_NONZERO }; template class SphericalHarmonics{ static constexpr Integer COORD_DIM = 3; public: // Scalar Spherical Harmonics static void Grid2SHC(const Vector& X_in, Long Nt_in, Long Np_in, Long p_out, Vector& S_out, SHCArrange arrange_out); static void SHC2Grid(const Vector& S_in, SHCArrange arrange_in, Long p_in, Long Nt_out, Long Np_out, Vector* X_out, Vector* X_theta_out=nullptr, Vector* X_phi_out=nullptr); static void SHCEval(const Vector& S_in, SHCArrange arrange_in, Long p_in, const Vector& theta_phi_in, Vector& X_out); static void SHC2Pole(const Vector& S_in, SHCArrange arrange_in, Long p_in, Vector& P_out); static void WriteVTK(const char* fname, const Vector* S, const Vector* f_val, SHCArrange arrange, Long p_in, Long p_out, Real period=0, const Comm& comm = Comm::World()); // Vector Spherical Harmonics static void Grid2VecSHC(const Vector& X_in, Long Nt_in, Long Np_in, Long p_out, Vector& S_out, SHCArrange arrange_out); static void VecSHC2Grid(const Vector& S_in, SHCArrange arrange_in, Long p_in, Long Nt_out, Long Np_out, Vector& X_out); static void VecSHCEval(const Vector& S_in, SHCArrange arrange_in, Long p_in, const Vector& theta_phi_in, Vector& X_out); static void StokesEvalSL(const Vector& S_in, SHCArrange arrange_in, Long p_in, const Vector& theta_phi_in, Vector& X_out); static void StokesEvalDL(const Vector& S_in, SHCArrange arrange_in, Long p_in, const Vector& theta_phi_in, Vector& X_out); static void test3() { int k=0, n=1, m=0; for (int k=0;k<3;k++) { for (int n=0;n<3;n++) { for (int m=0;m<=n;m++) { int p=5; int dof = 1; int Nt = p+1; int Np = 2*p+2; int Ngrid = Nt * Np; int Ncoeff = (p + 1) * (p + 2); Vector sin_theta, cos_theta = LegendreNodes(Nt-1); for (Long i=0;i(1-cos_theta[i]*cos_theta[i])); auto print_coeff = [&](Vector S) { Long idx=0; Long dof = S.Dim() / Ncoeff; for (Long k=0;k(2*n+2, S.begin()+idx); idx+=2*n+2; } } std::cout<<'\n'; }; Vector coeff(dof*Ncoeff); coeff=0; auto write_coeff = [&](Vector& coeff, Complex c, Long k, Long n, Long m) { if (0 <= m && m <= n && n <= p) { Long idx = k*Ncoeff + n*(n+1)+ 2*m; coeff[idx+0] = c.real; coeff[idx+1] = c.imag; } }; write_coeff(coeff, Complex(1,1.3),0,n,m); //print_coeff(coeff); Vector Y, Yt, Yp; SHC2Grid(coeff, SHCArrange::ROW_MAJOR, p, Nt, Np, &Y, &Yt, &Yp); //std::cout<(Nt, Np, Y.begin())<<'\n'; //std::cout<(Nt, Np, Yt.begin())<<'\n'; //std::cout<(Nt, Np, Yp.begin())<<'\n'; Vector gradYt = Yt; Vector gradYp = Yp; for (Long i=0;i Vr, Vt, Vp; Vector Wr, Wt, Wp; Vector Xr, Xt, Xp; Vr = Y*(-n-1); Vt = gradYt; Vp = gradYp; Wr = Y*n; Wt = gradYt; Wp = gradYp; Xr = Y*0; Xt = gradYp; Xp = gradYt * (-1); Vector SS(COORD_DIM * Ngrid); SS=0; if (k == 0) { Vector(Ngrid, SS.begin() + 0*Ngrid, false) = Vr; Vector(Ngrid, SS.begin() + 1*Ngrid, false) = Vt; Vector(Ngrid, SS.begin() + 2*Ngrid, false) = Vp; } if (k == 1) { Vector(Ngrid, SS.begin() + 0*Ngrid, false) = Wr; Vector(Ngrid, SS.begin() + 1*Ngrid, false) = Wt; Vector(Ngrid, SS.begin() + 2*Ngrid, false) = Wp; } if (k == 2) { Vector(Ngrid, SS.begin() + 0*Ngrid, false) = Xr; Vector(Ngrid, SS.begin() + 1*Ngrid, false) = Xt; Vector(Ngrid, SS.begin() + 2*Ngrid, false) = Xp; } Vector SSS; { Vector coeff(COORD_DIM*Ncoeff); coeff=0; write_coeff(coeff, Complex(1,1.3),k,n,m); //print_coeff(coeff); VecSHC2Grid(coeff, SHCArrange::ROW_MAJOR, p, Nt, Np, SSS); } //std::cout<(COORD_DIM*Nt, Np, SS.begin())<<'\n'; //std::cout<(COORD_DIM*Nt, Np, SSS.begin())<<'\n'; //std::cout<(COORD_DIM*Nt, Np, SSS.begin()) - Matrix(COORD_DIM*Nt, Np, SS.begin())<<'\n'; auto err=SSS-SS; Real max_err=0; for (auto x:err) max_err=std::max(max_err, fabs(x)); std::cout< S) { Long idx=0; for (Long k=0;k(2*n+2, S.begin()+idx); idx+=2*n+2; } } std::cout<<'\n'; }; Vector f(dof * Nt * Np); { // Set f Vector leg_nodes = LegendreNodes(Nt-1); for (Long i = 0; i < Nt; i++) { for (Long j = 0; j < Np; j++) { Real cos_theta = leg_nodes[i]; Real sin_theta = sqrt(1-cos_theta*cos_theta); Real phi = 2 * const_pi() * j / Np; Real r = 1; Real x = r * cos_theta; Real y = r * sin_theta * cos(phi); Real z = r * sin_theta * sin(phi); // Unit vectors in polar coordinates Real xr = cos_theta; Real yr = sin_theta * cos(phi); Real zr = sin_theta * sin(phi); Real xt = - sin_theta; Real yt = cos_theta * cos(phi); Real zt = cos_theta * sin(phi); Real xp = 0; Real yp = - sin(phi); Real zp = cos(phi); //////////////////////////////////// Real fx = 0; Real fy = 0; Real fz = 1; f[(0 * Nt + i) * Np + j] = (fx * xr + fy * yr + fz * zr); f[(1 * Nt + i) * Np + j] = (fx * xt + fy * yt + fz * zt); f[(2 * Nt + i) * Np + j] = (fx * xp + fy * yp + fz * zp); f[(0 * Nt + i) * Np + j] = 0; //sin(phi) * sin_theta; f[(1 * Nt + i) * Np + j] = 1; //sin(phi) * cos_theta; // * sin_theta; f[(2 * Nt + i) * Np + j] = 1; //cos(phi) ; // * sin_theta; } } } Vector f_coeff; Grid2VecSHC(f, Nt, Np, p, f_coeff, sctl::SHCArrange::ROW_MAJOR); if(0){ Vector f_, f_coeff_, f__; SHC2Grid(f_coeff, sctl::SHCArrange::ROW_MAJOR, p, Nt, Np, &f_); Grid2SHC(f_, Nt, Np, p, f_coeff_, sctl::SHCArrange::ROW_MAJOR); SHC2Grid(f_coeff_, sctl::SHCArrange::ROW_MAJOR, p, Nt, Np, &f__); std::cout<(dof*Nt, Np, f.begin())-Matrix(dof*Nt, Np, f_.begin())<<'\n'; std::cout<(dof*Nt, Np, f_.begin())-Matrix(dof*Nt, Np, f__.begin())<<'\n'; } if(0) for (Long i = 0; i < 20; i++) { // Evaluate Vector r_cos_theta_phi; r_cos_theta_phi.PushBack(drand48() + (i<10?0:2)); // [1, inf) r_cos_theta_phi.PushBack(drand48() * 2 - 1); // [-1, 1] r_cos_theta_phi.PushBack(drand48() * 2 * const_pi()); // [0, 2*pi] Vector Df; StokesEvalDL(f_coeff, sctl::SHCArrange::ROW_MAJOR, p, r_cos_theta_phi, Df); //VecSHCEval(f_coeff, sctl::SHCArrange::ROW_MAJOR, p, r_cos_theta_phi, Df); //std::cout< S) { Long idx=0; for (Long k=0;k(2*n+2, S.begin()+idx); idx+=2*n+2; } } std::cout<<'\n'; }; Vector r_theta_phi, theta_phi; { // Set r_theta_phi, theta_phi Vector leg_nodes = LegendreNodes(Nt-1); for (Long i=0;i() / Np); theta_phi.PushBack(leg_nodes[i]); theta_phi.PushBack(j * 2 * const_pi() / Np); } } } int Ncoeff = (p + 1) * (p + 1); Vector Xcoeff(dof * Ncoeff), Xgrid; for (int i=0;i(Nt, Np, Xgrid.begin()+Nt*Np*0)<<"];\n"; std::cout<<"Y1_=[\n"; std::cout<(Nt, Np, Xgrid.begin()+Nt*Np*1)<<"];\n"; std::cout<<"Y2_=[\n"; std::cout<(Nt, Np, Xgrid.begin()+Nt*Np*2)<<"];\n"; if (0) { Vector val; VecSHCEval(Xcoeff, sctl::SHCArrange::COL_MAJOR_NONZERO, p, theta_phi, val); //StokesEvalSL(Xcoeff, sctl::SHCArrange::COL_MAJOR_NONZERO, p, r_theta_phi, val); Matrix(dof, val.Dim()/dof, val.begin(), false) = Matrix(val.Dim()/dof, dof, val.begin()).Transpose(); std::cout<(val.Dim()/Np, Np, val.begin())<<'\n'; std::cout<(val.Dim()/Np, Np, val.begin()) - Matrix(Nt*dof, Np, Xgrid.begin())<<'\n'; } Grid2VecSHC(Xgrid, Nt, Np, p, Xcoeff, sctl::SHCArrange::ROW_MAJOR); print_coeff(Xcoeff); //SphericalHarmonics::WriteVTK("test", nullptr, &Xcoeff, sctl::SHCArrange::ROW_MAJOR, p, 32); Clear(); } static void Clear() { MatrixStore().Resize(0); } private: // Probably don't work anymore, need to be updated :( static void SHC2GridTranspose(const Vector& X, Long p0, Long p1, Vector& S); static void RotateAll(const Vector& S, Long p0, Long dof, Vector& S_); static void RotateTranspose(const Vector& S_, Long p0, Long dof, Vector& S); static void StokesSingularInteg(const Vector& S, Long p0, Long p1, Vector* SLMatrix=nullptr, Vector* DLMatrix=nullptr); static void Grid2SHC_(const Vector& X, Long Nt, Long Np, Long p, Vector& B1); static void SHCArrange0(const Vector& B1, Long p, Vector& S, SHCArrange arrange); static void SHC2Grid_(const Vector& S, Long p, Long Nt, Long Np, Vector* X, Vector* X_theta=nullptr, Vector* X_phi=nullptr); static void SHCArrange1(const Vector& S_in, SHCArrange arrange_out, Long p, Vector& S_out); /** * \brief Computes all the Associated Legendre Polynomials (normalized) up to the specified degree. * \param[in] degree The degree up to which the Legendre polynomials have to be computed. * \param[in] X The input values for which the polynomials have to be computed. * \param[in] N The number of input points. * \param[out] poly_val The output array of size (degree+1)*(degree+2)*N/2 containing the computed polynomial values. * The output values are in the order: * 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], * P(2,0)[0], ..., P(degree,0)[N-1], P(1,1)[0], ...,P(2,1)[0], ..., P(degree,degree)[N-1]} */ static void LegPoly(Vector& poly_val, const Vector& X, Long degree); static void LegPolyDeriv(Vector& poly_val, const Vector& X, Long degree); static const Vector& LegendreNodes(Long p1); static const Vector& LegendreWeights(Long p1); static const Vector& SingularWeights(Long p1); static const Matrix& MatFourier(Long p0, Long p1); static const Matrix& MatFourierInv(Long p0, Long p1); static const Matrix& MatFourierGrad(Long p0, Long p1); static const FFT& OpFourier(Long Np); static const FFT& OpFourierInv(Long Np); static const std::vector>& MatLegendre(Long p0, Long p1); static const std::vector>& MatLegendreInv(Long p0, Long p1); static const std::vector>& MatLegendreGrad(Long p0, Long p1); // Evaluate all Spherical Harmonic basis functions up to order p at (theta, phi) coordinates. static void SHBasisEval(Long p, const Vector& cos_theta_phi, Matrix& M); static void VecSHBasisEval(Long p, const Vector& cos_theta_phi, Matrix& M); static const std::vector>& MatRotate(Long p0); template static void StokesSingularInteg_(const Vector& X0, Long p0, Long p1, Vector& SL, Vector& DL); struct MatrixStorage{ MatrixStorage(){ const Long size = SCTL_SHMAXDEG; Resize(size); } void Resize(Long size){ Qx_ .resize(size); Qw_ .resize(size); Sw_ .resize(size); Mf_ .resize(size*size); Mdf_.resize(size*size); Ml_ .resize(size*size); Mdl_.resize(size*size); Mr_ .resize(size); Mfinv_ .resize(size*size); Mlinv_ .resize(size*size); Mfft_.resize(size); Mfftinv_.resize(size); } std::vector> Qx_; std::vector> Qw_; std::vector> Sw_; std::vector> Mf_ ; std::vector> Mdf_; std::vector>> Ml_ ; std::vector>> Mdl_; std::vector>> Mr_; std::vector> Mfinv_ ; std::vector>> Mlinv_ ; std::vector> Mfft_; std::vector> Mfftinv_; }; static MatrixStorage& MatrixStore(){ static MatrixStorage storage; return storage; } }; template class SphericalHarmonics; } // end namespace #include SCTL_INCLUDE(sph_harm.txx) #endif // _SCTL_SPH_HARM_HPP_