/** * \file cheb_utils.txx * \author Dhairya Malhotra, dhairya.malhotra@gmail.com * \date 2-11-2011 * \brief This file contains chebyshev related functions. */ #include #include #include #include #include namespace pvfmm{ template T machine_eps(){ T eps=1.0; while(eps+(T)1.0>1.0) eps*=0.5; return eps; } /** * \brief Returns the values of all chebyshev polynomials up to degree d, * evaluated at points in the input vector. Output format: * { T0[in[0]], ..., T0[in[n-1]], T1[in[0]], ..., T(d-1)[in[n-1]] } */ template inline void cheb_poly(int d, T* in, int n, T* out){ if(d==0){ for(int i=0;i T cheb_err(T* cheb_coeff, int deg, int dof){ T err=0; int indx=0; for(int l=0;l T cheb_approx(T* fn_v, int cheb_deg, int dof, T* out){ //T eps=machine_eps()*64; int d=cheb_deg+1; static std::vector > precomp; static std::vector > precomp_; Matrix* Mp ; Matrix* Mp_; #pragma omp critical (CHEB_APPROX) { if(precomp.size()<=(size_t)d){ precomp .resize(d+1); precomp_.resize(d+1); } if(precomp [d].Dim(0)==0 && precomp [d].Dim(1)==0){ std::vector x(d); for(int i=0;i()/d); std::vector p(d*d); cheb_poly(d-1,&x[0],d,&p[0]); for(int i=0;i Mp1(d,d,&p[0],false); Matrix Mp1_=Mp1.Transpose(); precomp [d]=Mp1 ; precomp_[d]=Mp1_; } Mp =&precomp [d]; Mp_=&precomp_[d]; } std::vector fn_v0(d*d*d*dof); std::vector fn_v1(d*d*d); std::vector fn_v2(d*d*d); std::vector fn_v3(d*d*d); for(size_t i=0;i<(size_t)(d*d*d*dof);i++) fn_v0[i]=fn_v[i]; int indx=0; for(int l=0;l M0(d*d,d,&fn_v0[d*d*d*l],false); Matrix M1(d*d,d,&fn_v1[0],false); M1=M0*(*Mp_); } { Matrix M0(d,d*d,&fn_v1[0],false); Matrix M1(d,d*d,&fn_v2[0],false); M1=(*Mp)*M0; } for(int i=0;i M0(d,d,&fn_v2[d*d*i],false); Matrix M1(d,d,&fn_v3[d*d*i],false); M1=(*Mp)*M0; } for(int i=0;i inline void legn_poly(int d, T* in, int n, T* out){ if(d==0){ for(int i=0;i void gll_quadrature(int deg, T* x_, T* w){//* T eps=machine_eps()*64; int d=deg+1; assert(d>1); int N=d-1; Vector x(d,x_,false); for(int i=0;i()*i)/N); Matrix P(d,d); P.SetZero(); T err=1; Vector xold(d); while(err>eps){ xold=x; for(int i=0;i T gll2cheb(T* fn_v, int deg, int dof, T* out){//* //T eps=machine_eps()*64; int d=deg+1; static std::vector > precomp; static std::vector > precomp_; Matrix* Mp ; Matrix* Mp_; #pragma omp critical (GLL_TO_CHEB) { if(precomp.size()<=(size_t)d){ precomp .resize(d+1); precomp_.resize(d+1); std::vector x(d); //Cheb nodes. for(int i=0;i()/d); Vector w(d); Vector x_legn(d); // GLL nodes. gll_quadrature(d-1, &x_legn[0], &w[0]); Matrix P(d,d); //GLL node 2 GLL coeff. legn_poly(d-1,&x_legn[0],d,&P[0][0]); for(int i=0;i M_gll2cheb(d,d); //GLL coeff 2 cheb node. legn_poly(d-1,&x[0],d,&M_gll2cheb[0][0]); Matrix M_g2c; //GLL node to cheb node. M_g2c=M_gll2cheb.Transpose()*P; std::vector p(d*d); cheb_poly(d-1,&x[0],d,&p[0]); for(int i=0;i Mp1(d,d,&p[0],false); Mp1=Mp1*M_g2c; Matrix Mp1_=Mp1.Transpose(); precomp [d]=Mp1 ; precomp_[d]=Mp1_; } Mp =&precomp [d]; Mp_=&precomp_[d]; } std::vector fn_v0(d*d*d*dof); std::vector fn_v1(d*d*d); std::vector fn_v2(d*d*d); std::vector fn_v3(d*d*d); for(size_t i=0;i<(size_t)(d*d*d*dof);i++) fn_v0[i]=fn_v[i]; int indx=0; for(int l=0;l M0(d*d,d,&fn_v0[d*d*d*l],false); Matrix M1(d*d,d,&fn_v1[0],false); M1=M0*(*Mp_); } { Matrix M0(d,d*d,&fn_v1[0],false); Matrix M1(d,d*d,&fn_v2[0],false); M1=(*Mp)*M0; } for(int i=0;i M0(d,d,&fn_v2[d*d*i],false); Matrix M1(d,d,&fn_v3[d*d*i],false); M1=(*Mp)*M0; } for(int i=0;i T cheb_approx(T (*fn)(T,T,T), int cheb_deg, T* coord, T s, std::vector& out){ int d=cheb_deg+1; std::vector x(d); for(int i=0;i()/d); std::vector p; cheb_poly(d-1,&x[0],d,&p[0]); std::vector x1(d); std::vector x2(d); std::vector x3(d); for(int i=0;i fn_v(d*d*d); T* fn_p=&fn_v[0]; for(int i=0;i void cheb_eval(Vector& coeff_, int cheb_deg, std::vector& in_x, std::vector& in_y, std::vector& in_z, Vector& out){ int d=cheb_deg+1; int dof=coeff_.Dim()/((d*(d+1)*(d+2))/6); assert(coeff_.Dim()==(size_t)(d*(d+1)*(d+2)*dof)/6); std::vector coeff(d*d*d*dof); {// Rearrange data int indx=0; for(int l=0;l p1(n1*d); std::vector p2(n2*d); std::vector p3(n3*d); cheb_poly(d-1,&in_x[0],n1,&p1[0]); cheb_poly(d-1,&in_y[0],n2,&p2[0]); cheb_poly(d-1,&in_z[0],n3,&p3[0]); std::vector fn_v1(n1*d *d ); std::vector fn_v2(n1*d *n3); std::vector fn_v3(n1*n2*n3); Matrix Mp1(d,n1,&p1[0],false); Matrix Mp2(d,n2,&p2[0],false); Matrix Mp3(d,n3,&p3[0],false); Matrix Mp2_=Mp2.Transpose(); Matrix Mp3_=Mp3.Transpose(); for(int k=0;k M0(d*d,d,&coeff[k*d*d*d],false); Matrix M1(d*d,n1,&fn_v1[0],false); M1=M0*Mp1; } { Matrix M0(d,d*n1,&fn_v1[0],false); Matrix M1(n3,d*n1,&fn_v2[0],false); M1=Mp3_*M0; } { int dn1=d*n1; int n2n1=n2*n1; for(int i=0;i M0(d,n1,&fn_v2[i*dn1],false); Matrix M1(n2,n1,&fn_v3[i*n2n1],false); M1=Mp2_*M0; } } mem::memcopy(&out[n1*n2*n3*k],&fn_v3[0],n1*n2*n3*sizeof(T)); } } /** * \brief Evaluates polynomial values from input coefficients at points * in the coord vector. */ template inline void cheb_eval(Vector& coeff_, int cheb_deg, std::vector& coord, Vector& out){ int dim=3; int d=cheb_deg+1; int n=coord.size()/dim; int dof=coeff_.Dim()/((d*(d+1)*(d+2))/6); assert(coeff_.Dim()==(size_t)(d*(d+1)*(d+2)*dof)/6); std::vector coeff(d*d*d*dof); {// Rearrange data int indx=0; for(int l=0;l coord_(n,dim,&coord[0]); coord_=coord_.Transpose(); Matrix px(d,n); Matrix py(d,n); Matrix pz(d,n); cheb_poly(d-1,&(coord_[0][0]),n,&(px[0][0])); cheb_poly(d-1,&(coord_[1][0]),n,&(py[0][0])); cheb_poly(d-1,&(coord_[2][0]),n,&(pz[0][0])); Matrix M_coeff0(d*d*dof, d, &coeff[0], false); Matrix M0 = (M_coeff0 * px).Transpose(); // {n, dof*d*d} py = py.Transpose(); pz = pz.Transpose(); out.Resize(n*dof); for(int i=0; i M0_ (d, d, &(M0[i][ j*d*d]), false); Matrix py_ (d, 1, &(py[i][ 0]), false); Matrix pz_ (1, d, &(pz[i][ 0]), false); Matrix M_out(1, 1, &( out[i*dof+j]), false); M_out += pz_ * M0_ * py_; } } /** * \brief Returns the values of all Chebyshev basis functions of degree up to d * evaluated at the point coord. */ template inline void cheb_eval(int cheb_deg, T* coord, T* coeff0,T* buff){ int d=cheb_deg+1; std::vector coeff(d*d*d); T* p=&buff[0]; T* p_=&buff[3*d]; cheb_poly(d-1,&coord[0],3,&p[0]); for(int i=0;i v_p0 (1, d, & p_[0],false); Matrix v_p1 (d, 1, & p_[d],false); Matrix M_coeff_(d, d, &coeff_[0],false); M_coeff_ = v_p1 * v_p0; // */ //mat::gemm(CblasRowMajor,CblasNoTrans,CblasNoTrans,d,d,1,1.0,&p_[d],1,&p_[0],d,0.0,&coeff_[0],d); Matrix v_p2 (d, 1, & p_[2*d],false); Matrix v_coeff_(1, d*d, &coeff_[ 0],false); Matrix M_coeff (d, d*d, &coeff [ 0],false); M_coeff = v_p2 * v_coeff_; // */ //mat::gemm(CblasRowMajor,CblasNoTrans,CblasNoTrans,d,d*d,1,1.0,&p_[2*d],1,&coeff_[0],d*d,0.0,&coeff[0],d*d); {// Rearrange data int indx=0; for(int i=0;i void points2cheb(int deg, T* coord, T* val, int n, int dim, T* node_coord, T node_size, Vector& cheb_coeff){ if(n==0) return; int deg_=((int)(pow((T)n*6,1.0/3.0)+0.5))/2; deg_=(deg_>deg?deg:deg_); deg_=(deg_>0?deg_:1); int deg3=((deg_+1)*(deg_+2)*(deg_+3))/6; cheb_coeff.Resize(dim*((deg+1)*(deg+2)*(deg+3))/6); cheb_coeff.SetZero(); //Map coordinates to unit cube std::vector coord_(n*3); for(int i=0;i M(n,deg3); std::vector buff((deg_+1)*(deg_+1+3*2)); for(int i=0;i M_val(n,dim,&val[0]); T eps=machine_eps()*64; Matrix cheb_coeff_=(M.pinv(eps)*M_val).Transpose(); //Set the output int indx=0; int indx1=0; for(int l=0;l void quad_rule(int n, T* x, T* w){//* static std::vector > x_lst(10000); static std::vector > w_lst(10000); assert(n<10000); bool done=false; #pragma omp critical (QUAD_RULE) if(x_lst[n].Dim()>0){ Vector& x_=x_lst[n]; Vector& w_=w_lst[n]; for(int i=0;i x_(n); Vector w_(n); { //Gauss-Chebyshev quadrature nodes and weights for(int i=0;i()); w_[i]=0;//sqrt(1.0-x_[i]*x_[i])*const_pi()/n; } Matrix M(n,n); cheb_poly(n-1, &x_[0], n, &M[0][0]); for(size_t i=0;i w_sample(n,0); if(n>0) w_sample[0]=2.0; if(n>1) w_sample[1]=0.0; if(n>2) w_sample[2]=-((T)2.0)/3; if(n>3) w_sample[3]=0.0; if(n>4) w_sample[4]=-((T)2.0)/15; if(n>5) w_sample[5]=0.0; if(n>6) w_sample[5]=((T)64)/7-((T)96)/5+((T)36)/3-2; if(n>7) w_sample[5]=0; if(n>8){ T eps=machine_eps()*64; std::vector qx(n-1); std::vector qw(n-1); quad_rule(n-1, &qx[0], &qw[0]); T err=1.0; std::vector w_prev; for(size_t iter=1;err>eps*iter;iter*=2){ w_prev=w_sample; w_sample.assign(n,0); size_t N=(n-1)*iter; std::vector x_sample(N,0); Matrix M_sample(n,N); for(size_t i=0;i void quad_rule(int n, double* x, double* w){//* static std::vector > x_lst(10000); static std::vector > w_lst(10000); assert(n<10000); bool done=false; #pragma omp critical (QUAD_RULE) if(x_lst[n].Dim()>0){ Vector& x_=x_lst[n]; Vector& w_=w_lst[n]; for(int i=0;i x_(n); Vector w_(n); { //Gauss-Legendre quadrature nodes and weights double alpha=0.0; double beta=0.0; double a=-1.0; double b= 1.0; int kind = 1; cgqf ( n, kind, (double)alpha, (double)beta, (double)a, (double)b, &x_[0], &w_[0] ); } #pragma omp critical (QUAD_RULE) { // Set x_lst, w_lst x_lst[n]=x_; w_lst[n]=w_; } quad_rule(n, x, w); } template std::vector integ_pyramid(int m, T* s, T r, int nx, Kernel& kernel, int* perm){//* static mem::MemoryManager mem_mgr(16*1024*1024*sizeof(T)); int ny=nx; int nz=nx; T eps=machine_eps()*64; int k_dim=kernel.ker_dim[0]*kernel.ker_dim[1]; std::vector qp_x(nx), qw_x(nx); std::vector qp_y(ny), qw_y(ny); std::vector qp_z(nz), qw_z(nz); std::vector p_x(nx*m); std::vector p_y(ny*m); std::vector p_z(nz*m); std::vector x_; { // Build stack along X-axis. x_.push_back(s[0]); x_.push_back(fabs(1.0-s[0])+s[0]); x_.push_back(fabs(1.0-s[1])+s[0]); x_.push_back(fabs(1.0+s[1])+s[0]); x_.push_back(fabs(1.0-s[2])+s[0]); x_.push_back(fabs(1.0+s[2])+s[0]); std::sort(x_.begin(),x_.end()); for(int i=0;i 1.0) x_[i]= 1.0; } std::vector x_new; T x_jump=fabs(1.0-s[0]); if(fabs(1.0-s[1])>eps) x_jump=std::min(x_jump,(T)fabs(1.0-s[1])); if(fabs(1.0+s[1])>eps) x_jump=std::min(x_jump,(T)fabs(1.0+s[1])); if(fabs(1.0-s[2])>eps) x_jump=std::min(x_jump,(T)fabs(1.0-s[2])); if(fabs(1.0+s[2])>eps) x_jump=std::min(x_jump,(T)fabs(1.0+s[2])); for(int k=0; k 1.0) y0= 1.0; T y1=s[1]+(x0-s[0]); if(y1<-1.0) y1=-1.0; if(y1> 1.0) y1= 1.0; T z0=s[2]-(x0-s[0]); if(z0<-1.0) z0=-1.0; if(z0> 1.0) z0= 1.0; T z1=s[2]+(x0-s[0]); if(z1<-1.0) z1=-1.0; if(z1> 1.0) z1= 1.0; A0=(y1-y0)*(z1-z0); } { // A1 T y0=s[1]-(x1-s[0]); if(y0<-1.0) y0=-1.0; if(y0> 1.0) y0= 1.0; T y1=s[1]+(x1-s[0]); if(y1<-1.0) y1=-1.0; if(y1> 1.0) y1= 1.0; T z0=s[2]-(x1-s[0]); if(z0<-1.0) z0=-1.0; if(z0> 1.0) z0= 1.0; T z1=s[2]+(x1-s[0]); if(z1<-1.0) z1=-1.0; if(z1> 1.0) z1= 1.0; A1=(y1-y0)*(z1-z0); } T V=0.5*(A0+A1)*(x1-x0); if(V k_out( ny*nz*k_dim,(T*)mem_mgr.malloc( ny*nz*k_dim*sizeof(T)),false); //Output of kernel evaluation. Vector I0 ( ny*m *k_dim,(T*)mem_mgr.malloc( ny*m *k_dim*sizeof(T)),false); Vector I1 ( m *m *k_dim,(T*)mem_mgr.malloc( m *m *k_dim*sizeof(T)),false); Vector I2 (m *m *m *k_dim,(T*)mem_mgr.malloc(m *m *m *k_dim*sizeof(T)),false); I2.SetZero(); if(x_.size()>1) for(int k=0; k qp(nx); std::vector qw(nx); quad_rule(nx,&qp[0],&qw[0]); for(int i=0; i 1.0) y0= 1.0; T y1=s[1]+(qp_x[i]-s[0]); if(y1<-1.0) y1=-1.0; if(y1> 1.0) y1= 1.0; T z0=s[2]-(qp_x[i]-s[0]); if(z0<-1.0) z0=-1.0; if(z0> 1.0) z0= 1.0; T z1=s[2]+(qp_x[i]-s[0]); if(z1<-1.0) z1=-1.0; if(z1> 1.0) z1= 1.0; { // Set qp_y std::vector qp(ny); std::vector qw(ny); quad_rule(ny,&qp[0],&qw[0]); for(int j=0; j qp(nz); std::vector qw(nz); quad_rule(nz,&qp[0],&qw[0]); for(int j=0; j trg(ny*nz*3); for(int i0=0; i0 k_val(ny*nz*kernel.ker_dim[0],kernel.ker_dim[1]); kernel.BuildMatrix(&src[0],1,&trg[0],ny*nz,&k_val[0][0]); Matrix k_val_t(kernel.ker_dim[1],ny*nz*kernel.ker_dim[0],&k_out[0], false); k_val_t=k_val.Transpose(); } for(int kk=0; kk1) Profile::Add_FLOP(( 2*ny*nz*m*k_dim +ny*m*(m+1)*k_dim +2*m*(m+1)*k_dim +m*(m+1)*(m+2)/3*k_dim)*nx*(x_.size()-1)); std::vector I2_(&I2[0], &I2[0]+I2.Dim()); mem_mgr.free(&k_out[0]); mem_mgr.free(&I0 [0]); mem_mgr.free(&I1 [0]); mem_mgr.free(&I2 [0]); return I2_; } template std::vector integ(int m, T* s, T r, int n, Kernel& kernel){//* //Compute integrals over pyramids in all directions. int k_dim=kernel.ker_dim[0]*kernel.ker_dim[1]; T s_[3]; s_[0]=s[0]*2.0/r-1.0; s_[1]=s[1]*2.0/r-1.0; s_[2]=s[2]*2.0/r-1.0; T s1[3]; int perm[6]; std::vector U_[6]; s1[0]= s_[0];s1[1]=s_[1];s1[2]=s_[2]; perm[0]= 0; perm[2]= 1; perm[4]= 2; perm[1]= 1; perm[3]= 1; perm[5]= 1; U_[0]=integ_pyramid(m,s1,r,n,kernel,perm); s1[0]=-s_[0];s1[1]=s_[1];s1[2]=s_[2]; perm[0]= 0; perm[2]= 1; perm[4]= 2; perm[1]=-1; perm[3]= 1; perm[5]= 1; U_[1]=integ_pyramid(m,s1,r,n,kernel,perm); s1[0]= s_[1];s1[1]=s_[0];s1[2]=s_[2]; perm[0]= 1; perm[2]= 0; perm[4]= 2; perm[1]= 1; perm[3]= 1; perm[5]= 1; U_[2]=integ_pyramid(m,s1,r,n,kernel,perm); s1[0]=-s_[1];s1[1]=s_[0];s1[2]=s_[2]; perm[0]= 1; perm[2]= 0; perm[4]= 2; perm[1]=-1; perm[3]= 1; perm[5]= 1; U_[3]=integ_pyramid(m,s1,r,n,kernel,perm); s1[0]= s_[2];s1[1]=s_[0];s1[2]=s_[1]; perm[0]= 2; perm[2]= 0; perm[4]= 1; perm[1]= 1; perm[3]= 1; perm[5]= 1; U_[4]=integ_pyramid(m,s1,r,n,kernel,perm); s1[0]=-s_[2];s1[1]=s_[0];s1[2]=s_[1]; perm[0]= 2; perm[2]= 0; perm[4]= 1; perm[1]=-1; perm[3]= 1; perm[5]= 1; U_[5]=integ_pyramid(m,s1,r,n,kernel,perm); std::vector U; U.assign(m*m*m*k_dim,0); for(int kk=0; kk std::vector cheb_integ(int m, T* s_, T r_, Kernel& kernel){ T eps=machine_eps(); T r=r_; T s[3]={s_[0],s_[1],s_[2]}; int n=m+1; T err=1.0; int k_dim=kernel.ker_dim[0]*kernel.ker_dim[1]; std::vector U=integ(m+1,s,r,n,kernel); std::vector U_; while(err>eps*n*n){ n=(int)round(n*1.3); if(n>300){ using ::operator<<; std::cout<<"Cheb_Integ::Failed to converge.["<(m+1,s,r,n,kernel); err=0; for(int i=0;i<(m+1)*(m+1)*(m+1)*k_dim;i++) if(fabs(U[i]-U_[i])>err) err=fabs(U[i]-U_[i]); U=U_; } std::vector U0(((m+1)*(m+2)*(m+3)*k_dim)/6); {// Rearrange data int indx=0; int* ker_dim=kernel.ker_dim; for(int l0=0;l0 std::vector cheb_nodes(int deg, int dim){ int d=deg+1; std::vector x(d); for(int i=0;i()/d)*0.5+0.5; if(dim==1) return x; int n1=(int)(pow((T)d,dim)+0.5); std::vector y(n1*dim); for(int i=0;i void cheb_diff(T* A, int deg, T* B){ int d=deg+1; static Matrix M; #pragma omp critical (CHEB_DIFF) if(M.Dim(0)!=d){ M.Resize(d,d); for(int i=0;i MA(d,1,A,false); Matrix MB(d,1,B,false); MB=M*MA; } template void cheb_diff(T* A, int deg, int dim, int curr_dim, T* B){ int d=deg+1; static Matrix M; #pragma omp critical (CHEB_DIFF1) if(M.Dim(0)!=(size_t)d){ M.Resize(d,d); for(int i=0;i MA(d,n1,&A[i*n1*d],false); Matrix MB(d,n1,&B[i*n1*d],false); MB=M*MA; } } template void cheb_grad(T* A, int deg, T* B){ int dim=3; int d=deg+1; int n1 =(d*(d+1)*(d+2))/6; int n1_=(int)(pow((T)d,dim)+0.5); Vector A_(n1_); A_.SetZero(); Vector B_(n1_); B_.SetZero(); {// Rearrange data int indx=0; for(int i=0;i void cheb_div(T* A_, int deg, T* B_){ int dim=3; int d=deg+1; int n1 =(int)(pow((T)d,dim)+0.5); Vector A(n1*dim); A.SetZero(); Vector B(n1 ); B.SetZero(); {// Rearrange data int indx=0; for(int l=0;l MB(n1,1,&B[0],false); Matrix MC(n1,1); for(int i=0;i<3;i++){ cheb_diff(&A[n1*i],d-1,3,i,MC[0]); MB+=MC; } {// Rearrange data int indx=0; for(int i=0;i void cheb_curl(T* A_, int deg, T* B_){ int dim=3; int d=deg+1; int n1 =(int)(pow((T)d,dim)+0.5); Vector A(n1*dim); A.SetZero(); Vector B(n1*dim); B.SetZero(); {// Rearrange data int indx=0; for(int l=0;l MC1(n1,1); Matrix MC2(n1,1); for(int i=0;i<3;i++){ Matrix MB(n1,1,&B[n1*i],false); int j1=(i+1)%3; int j2=(i+2)%3; cheb_diff(&A[n1*j1],d-1,3,j2,MC1[0]); cheb_diff(&A[n1*j2],d-1,3,j1,MC2[0]); MB=MC2; MB-=MC1; } {// Rearrange data int indx=0; for(int l=0;l void cheb_laplacian(T* A, int deg, T* B){ int dim=3; int d=deg+1; int n1=(int)(pow((T)d,dim)+0.5); T* C1=new T[n1]; T* C2=new T[n1]; Matrix M_(1,n1,C2,false); for(int i=0;i<3;i++){ Matrix M (1,n1,&B[n1*i],false); for(int j=0;j void cheb_img(T* A, T* B, int deg, int dir, bool neg_){ int d=deg+1; int n1=(int)(pow((T)d,3-dir)+0.5); int n2=(int)(pow((T)d, dir)+0.5); int indx; T sgn,neg; neg=(T)(neg_?-1.0:1.0); for(int i=0;i