From 704d10d77cce4e0c2b291e532b59e5db8c247e7c Mon Sep 17 00:00:00 2001 From: Thomas Toulorge <thomas.toulorge@mines-paristech.fr> Date: Wed, 21 Nov 2012 14:32:44 +0000 Subject: [PATCH] Fixed and cleaned pyramidal basis functions. Added test script. --- Numeric/BergotBasis.cpp | 109 +++++++++------ benchmarks/misc/testPyramidalBasis.py | 194 ++++++++++++++++++++++++++ 2 files changed, 261 insertions(+), 42 deletions(-) create mode 100644 benchmarks/misc/testPyramidalBasis.py diff --git a/Numeric/BergotBasis.cpp b/Numeric/BergotBasis.cpp index ad9a2c0433..8611c1e0a7 100644 --- a/Numeric/BergotBasis.cpp +++ b/Numeric/BergotBasis.cpp @@ -12,6 +12,30 @@ +namespace { + +// Private function to compute value and gradients of Jacobi polynomial at 1 (the general +// implementation in class jacobiPolynomials does not handle the indefinite form) +inline void jacobiDiffOne(int alpha, int beta, int n, double *wf, double *wg) +{ + + const int fMax = std::max(n,1)+alpha; + std::vector<double> fact(fMax+1); + fact[0] = 1.; + for (int i=1;i<=fMax;i++) fact[i] = i*fact[i-1]; + wf[0] = 1.; wg[0] = 0.; + for (int k=1;k<=n;k++) { + wf[k] = fact[k+alpha]/(fact[alpha]*fact[k]); + wg[k] = 0.5*(k+alpha+beta+1)*fact[k+alpha]/(fact[alpha+1]*fact[k-1]); + } + +} + +} + + + + BergotBasis::BergotBasis(int p): order(p) { } @@ -24,19 +48,26 @@ BergotBasis::~BergotBasis() +// Values of Bergot basis functions for coordinates (u,v,w) in the unit pyramid: +// f = L_i(uhat)*L_j(vhat)*(1-w)^max(i,j)*P_k^{2*max(i,j)+2,0}(what) +// with i,j = 0...order and k = 0...order-max(i,j) +// and (uhat,vhat,what) = (u/(1-w),v/(1-w),2*w-1) reduced coordinates in the unit cube [-1,1]^3 void BergotBasis::f(double u, double v, double w, double* val) const { LegendrePolynomials legendre(order); - double uhat = (w == 1.) ? 1. : u/(1.-w); + // Compute Legendre polynomials at uhat + double uhat = (w == 1.) ? 0. : u/(1.-w); std::vector<double> uFcts(order+1); legendre.f(uhat,&(uFcts[0])); - double vhat = (w == 1.) ? 1. : v/(1.-w); + // Compute Legendre polynomials at vhat + double vhat = (w == 1.) ? 0. : v/(1.-w); std::vector<double> vFcts(order+1); legendre.f(vhat,&(vFcts[0])); + // Compute Jacobi polynomials at what double what = 2.*w - 1.; std::vector<std::vector<double> > wFcts(order+1), wGrads(order+1); for (int mIJ=0; mIJ<=order; mIJ++) { @@ -47,17 +78,14 @@ void BergotBasis::f(double u, double v, double w, double* val) const jacobi.f(what,&(wf[0])); } - // recombine to find shape function values - + // Recombine to find shape function values int index = 0; for (int i=0;i<=order;i++) { for (int j=0;j<=order;j++) { int mIJ = std::max(i,j); double fact = pow(1.-w, mIJ); std::vector<double> &wf = wFcts[mIJ]; - for (int k=0; k<=order-mIJ; k++,index++) { - val[index] = uFcts[i] * vFcts[j] * wf[k] * fact; - } + for (int k=0; k<=order-mIJ; k++,index++) val[index] = uFcts[i] * vFcts[j] * wf[k] * fact; } } @@ -65,23 +93,31 @@ void BergotBasis::f(double u, double v, double w, double* val) const +// Derivatives of Bergot basis functions for coordinates (u,v,w) in the unit pyramid: +// dfdu = L'_i(uhat)*L_j(vhat)*(1-w)^(max(i,j)-1)*P_k^{2*max(i,j)+2,0}(what) +// dfdv = L_i(uhat)*L'_j(vhat)*(1-w)^(max(i,j)-1)*P_k^{2*max(i,j)+2,0}(what) +// dfdw = (1-w)^(max(i,j)-2)*P_k^{2*max(i,j)+2,0}(what)*(u*L'_i(uhat)*L_j(vhat)+v*L_i(uhat)*L'_j(vhat)) +// + u*v*(1-w)^(max(i,j)-1)*(2*(1-w)*P'_k^{2*max(i,j)+2,0}(what)-max(i,j)*P_k^{2*max(i,j)+2,0}(what)) +// with i,j = 0...order and k = 0...order-max(i,j) +// and (uhat,vhat,what) = (u/(1-w),v/(1-w),2*w-1) reduced coordinates in the unit cube [-1,1]^3 void BergotBasis::df(double u, double v, double w, double grads[][3]) const { LegendrePolynomials legendre(order); -// std::cout << "DBGTT: u = " << u << ", v = " << v << ", w = " << w << "\n"; - - double uhat = (w == 1.) ? 1. : u/(1.-w); + // Compute Legendre polynomials and derivatives at uhat + double uhat = (w == 1.) ? 0. : u/(1.-w); std::vector<double> uFcts(order+1), uGrads(order+1); legendre.f(uhat,&(uFcts[0])); legendre.df(uhat,&(uGrads[0])); - double vhat = (w == 1.) ? 1. : v/(1.-w); + // Compute Legendre polynomials and derivatives at vhat + double vhat = (w == 1.) ? 0. : v/(1.-w); std::vector<double> vFcts(order+1), vGrads(order+1); legendre.f(vhat,&(vFcts[0])); legendre.df(vhat,&(vGrads[0])); + // Compute Jacobi polynomials and derivatives at what double what = 2.*w - 1.; std::vector<std::vector<double> > wFcts(order+1), wGrads(order+1); for (int mIJ=0; mIJ<=order; mIJ++) { @@ -89,71 +125,60 @@ void BergotBasis::df(double u, double v, double w, double grads[][3]) const std::vector<double> &wf = wFcts[mIJ], &wg = wGrads[mIJ]; wf.resize(kMax+1); wg.resize(kMax+1); - if (what == 1.) { - const int alpha = 2*mIJ+2, fMax = std::max(kMax,1)+alpha; - std::vector<double> fact(fMax); - fact[0] = 1.; - for (int i=1;i<=fMax;i++) fact[i] = i*fact[i-1]; - wf[0] = 1.; wg[0] = 0.; - for (int k=1;k<=kMax;k++) { -// std::cout << "DBGTT: calc. fMax = " << fMax << ", alpha = " << alpha << ", k = " << k << "\n"; - wf[k] = fact[k+alpha]/(fact[alpha]*fact[k]); - wg[k] = 0.5*(k+alpha+2)*fact[k+alpha]/(fact[alpha+1]*fact[k-1]); -// std::cout << "DBGTT: -> wf[k] = " << wf[k] << ", wg[k] = " << wg[k] << "\n"; - } - } + if (what == 1.) jacobiDiffOne(2*mIJ+2,0,kMax,&(wf[0]),&(wg[0])); else { JacobiPolynomials jacobi(2.*mIJ+2.,0.,kMax); jacobi.f(what,&(wf[0])); jacobi.df(what,&(wg[0])); } -// for (int k=0; k<=order-mIJ; k++) std::cout << " -> mIJ = " << mIJ << ", k = " << k -// << " -> wf[k] = " << wf[k] << ", wg[k] = " << wg[k] << "\n"; } - // now recombine to find the shape function gradients - + // Recombine to find the shape function gradients int index = 0; for (int i=0;i<=order;i++) { for (int j=0;j<=order;j++) { int mIJ = std::max(i,j); std::vector<double> &wf = wFcts[mIJ], &wg = wGrads[mIJ]; - double oMW = 1.-w; - double powM2 = ((w == 1.) && (mIJ < 2)) ? 0. : pow(oMW, mIJ-2); - double powM1 = powM2*oMW; - for (int k=0; k<=order-mIJ; k++,index++) { - if (mIJ == 0) { + if (mIJ == 0) { // Indeterminate form for mIJ = 0 + for (int k=0; k<=order-mIJ; k++,index++) { grads[index][0] = 0.; grads[index][1] = 0.; grads[index][2] = 2.*wg[k]; } - else if (mIJ == 1) { - if (i == 0) { + } + else if (mIJ == 1) { // Indeterminate form for mIJ = 1 + if (i == 0) { + for (int k=0; k<=order-mIJ; k++,index++) { grads[index][0] = 0.; grads[index][1] = wf[k]; grads[index][2] = 2.*v*wg[k]; } - else if (j == 0) { + } + else if (j == 0) { + for (int k=0; k<=order-mIJ; k++,index++) { grads[index][0] = wf[k]; grads[index][1] = 0.; grads[index][2] = 2.*u*wg[k]; } - else { + } + else { + for (int k=0; k<=order-mIJ; k++,index++) { grads[index][0] = vhat*wf[k]; grads[index][1] = uhat*wf[k]; grads[index][2] = uhat*vhat*wf[k]+2.*uhat*v*wg[k]; } } - else { + } + else { // General formula + double oMW = 1.-w; + double powM2 = pow(oMW, mIJ-2); + double powM1 = powM2*oMW; + for (int k=0; k<=order-mIJ; k++,index++) { grads[index][0] = uGrads[i] * vFcts[j] * wf[k] * powM1; grads[index][1] = uFcts[i] * vGrads[j] * wf[k] * powM1; grads[index][2] = wf[k]*powM2*(u*uGrads[i]*vFcts[j]+v*uFcts[i]*vGrads[j]) + uFcts[i]*vFcts[j]*powM1*(2.*oMW*wg[k]-mIJ*wf[k]); } -//// std::cout << " -> i = " << i << ", j = " << j << ", k = " << k << " -> grad = (" -//// << grads[index][0] << ", " << grads[index][1] << ", "<< grads[index][2] << ")\n"; -// std::cout << " -> i = " << i << ", j = " << j << ", k = " << k -// << " -> wf[k] = " << wf[k] << ", wg[k] = " << wg[k] << "\n"; } } } diff --git a/benchmarks/misc/testPyramidalBasis.py b/benchmarks/misc/testPyramidalBasis.py new file mode 100644 index 0000000000..b2c3b1c45e --- /dev/null +++ b/benchmarks/misc/testPyramidalBasis.py @@ -0,0 +1,194 @@ +from dgpy import * +import random +import math + + + +# Parameters +Nr = 100 +p = 8 + + + +# Evaluate polynomial +def polyEval(FCT, p, coeff, uvw): + for n in range(uvw.size1()): + u = uvw(n,0) + v = uvw(n,1) + w = uvw(n,2) + FCT.set(n,0) + ind = 0 + for i in range(p+1): + upi = math.pow(u,i) + for j in range(p+1-i): + vpj = math.pow(v,j) + for k in range(p+1-i-j): + FCT.set(n,FCT(n)+coeff(ind)*upi*vpj*math.pow(w,k)) + ind += 1 + + + +# Evaluate gradient of polynomial +def gradPolyEval(FCT, p, coeff, uvw): + for n in range(uvw.size1()): + u = uvw(n,0) + v = uvw(n,1) + w = uvw(n,2) + FCT.set(3*n,0) + FCT.set(3*n+1,0) + FCT.set(3*n+2,0) + ind = 0 + for i in range(p+1): + upi = math.pow(u,i) + if (i == 0): + dupi = 0 + else: + dupi = i*math.pow(u,i-1) + for j in range(p+1-i): + vpj = math.pow(v,j) + if (j == 0): + dvpj = 0 + else: + dvpj = j*math.pow(v,j-1) + for k in range(p+1-i-j): + wpk = math.pow(w,k) + if (k == 0): + dwpk = 0 + else: + dwpk = k*math.pow(w,k-1) + FCT.set(3*n,FCT(3*n)+coeff(ind)*dupi*vpj*wpk) + FCT.set(3*n+1,FCT(3*n+1)+coeff(ind)*upi*dvpj*wpk) + FCT.set(3*n+2,FCT(3*n+2)+coeff(ind)*upi*vpj*dwpk) + ind += 1 + + + +# Evaluate approx. on nodal basis +def interp(FCT, basis, coeff, uvw): + sf = fullMatrixDouble(uvw.size1(),coeff.size()) + basis.f(uvw,sf) + for i in range(uvw.size1()): + FCT.set(i,0) + for j in range(coeff.size()): + FCT.set(i,FCT(i)+coeff(j)*sf(i,j)) + + + +# Evaluate gradient of approx. on nodal basis +def gradInterp(FCT, basis, coeff, uvw): + gsf = fullMatrixDouble(coeff.size(),3*uvw.size1()) + basis.df(uvw,gsf) + for i in range(3*uvw.size1()): + FCT.set(i,0) + for j in range(coeff.size()): + FCT.set(i,FCT(i)+coeff(j)*gsf(j,i)) + + + +# Test if point is in unit pyramid +def isInPyr(u,v,w): + if ((w >= 0) and (w < 1)): + uh = u/(1-w) + vh = v/(1-w) + return ((uh >= -1) and (uh <= 1) and (vh >= -1) and (vh <= 1)) + elif (w == 1): + return ((u == 0) and (v == 0)) + else: + return False + + + +# Generate polynomial of order p with random coefficients +Np = (p+1)*(p+2)*(p+3)/6 +poly = fullVectorDouble(Np) +for i in range(Np): + poly.set(i,random.random()) + +# Get appropriate basis +if (p == 1): + basis = pyramidalBasis(MSH_PYR_5) +elif (p == 2): + basis = pyramidalBasis(MSH_PYR_14) +elif (p == 3): + basis = pyramidalBasis(MSH_PYR_30) +elif (p == 4): + basis = pyramidalBasis(MSH_PYR_55) +elif (p == 5): + basis = pyramidalBasis(MSH_PYR_91) +elif (p == 6): + basis = pyramidalBasis(MSH_PYR_140) +elif (p == 7): + basis = pyramidalBasis(MSH_PYR_204) +elif (p == 8): + basis = pyramidalBasis(MSH_PYR_285) +elif (p == 9): + basis = pyramidalBasis(MSH_PYR_385) + +print("Basis:\n -> type = %i\n -> parentType = %i\n -> order = %i\n -> points = %i\n -> dimension = %i\n -> numFaces = %i\n -> serendip = %s" % + (basis.type, basis.parentType, basis.order, basis.points.size1(), basis.dimension, basis.numFaces, basis.serendip)) + +#print("%i points:" % basis.points.size1()) +#for i in range(basis.points.size1()) : +# print(" -> (%g, %g, %g)" % (basis.points(i,0), basis.points(i,1), basis.points(i,2))) + +#sf = fullMatrixDouble(basis.points.size1(),5) +#basis.f(basis.points,sf) +#print("SF:") +#for i in range(5) : +# print(" -> %g %g %g %g %g" % (sf(i,0), sf(i,1), sf(i,2), sf(i,3), sf(i,4))) + +# nodal coefficients of function to be evaluated +nodalCoeff = fullVectorDouble(basis.points.size1()) +polyEval(nodalCoeff,p,poly,basis.points) +#print("%i nodalCoeffs:" % nodalCoeff.size()) +#for i in range(nodalCoeff.size()) : +# print(" -> %g" % nodalCoeff(i)) + +# Generate Nr random points in pyramid (well, Nr-1 random plus (0,0,1) to test indeterminate form) +uvwr = fullMatrixDouble(Nr,3) +uvwr.set(0,0,0) +uvwr.set(0,1,0) +uvwr.set(0,2,1) +for i in range(1,Nr): + uvwr.set(i,0,1000) + uvwr.set(i,1,1000) + uvwr.set(i,2,1000) + while (not isInPyr(uvwr(i,0),uvwr(i,1),uvwr(i,2))): + uvwr.set(i,0,random.random()) + uvwr.set(i,1,random.random()) + uvwr.set(i,2,random.random()) + +# Exact value and gradient of function at random points +valExact = fullVectorDouble(Nr) +polyEval(valExact,p,poly,uvwr) +gradExact = fullVectorDouble(3*Nr) +gradPolyEval(gradExact,p,poly,uvwr) + +# Approx. value and gradient of function at random points +valApp = fullVectorDouble(Nr) +interp(valApp,basis,nodalCoeff,uvwr) +gradApp = fullVectorDouble(3*Nr) +gradInterp(gradApp,basis,nodalCoeff,uvwr) + +# Compute error +avgErrVal = 0 +avgErrGradX = 0 +avgErrGradY = 0 +avgErrGradZ = 0 +for i in range(Nr): + avgErrVal += (valApp(i)-valExact(i))*(valApp(i)-valExact(i)) + avgErrGradX += (gradApp(3*i)-gradExact(3*i))*(gradApp(3*i)-gradExact(3*i)) + avgErrGradY += (gradApp(3*i+1)-gradExact(3*i+1))*(gradApp(3*i+1)-gradExact(3*i+1)) + avgErrGradZ += (gradApp(3*i+2)-gradExact(3*i+2))*(gradApp(3*i+2)-gradExact(3*i+2)) +avgErrVal = math.sqrt(avgErrVal)/Nr +avgErrGradX = math.sqrt(avgErrGradX)/Nr +avgErrGradY = math.sqrt(avgErrGradY)/Nr +avgErrGradZ = math.sqrt(avgErrGradZ)/Nr + +# Print error +print("Check on %i random points:" % Nr) +print(" -> average error value = %g" % avgErrVal) +print(" -> average error X gradient = %g" % avgErrGradX) +print(" -> average error Y gradient = %g" % avgErrGradY) +print(" -> average error Z gradient = %g" % avgErrGradZ) + -- GitLab