Fixed: Using direction-dependent Gauss quadrature on face integrals
This commit is contained in:
@@ -2478,28 +2478,43 @@ bool ASMs3D::integrate (Integrand& integrand, int lIndex,
|
||||
const int t2 = 1 + t1%3; // second tangent direction
|
||||
|
||||
// Get Gaussian quadrature points and weights
|
||||
// For now, use the largest polynomial order of the two tangent directions
|
||||
int nG1 = this->getNoGaussPt(std::max(svol->order(t1-1),svol->order(t2-1)),true);
|
||||
int nGP = integrand.getBouIntegrationPoints(nG1);
|
||||
const double* xg = GaussQuadrature::getCoord(nGP);
|
||||
const double* wg = GaussQuadrature::getWeight(nGP);
|
||||
if (!xg || !wg) return false;
|
||||
|
||||
// Compute parameter values of the Gauss points over the whole patch face
|
||||
// and compute parameter values of the Gauss points over the whole patch face
|
||||
std::array<int,3> ng;
|
||||
std::array<const double*,3> xg, wg;
|
||||
std::array<Matrix,3> gpar;
|
||||
for (int d = 0; d < 3; d++)
|
||||
if (-1-d == faceDir)
|
||||
{
|
||||
ng[d] = 1;
|
||||
xg[d] = nullptr;
|
||||
wg[d] = nullptr;
|
||||
gpar[d].resize(1,1);
|
||||
gpar[d].fill(svol->startparam(d));
|
||||
}
|
||||
else if (1+d == faceDir)
|
||||
{
|
||||
ng[d] = 1;
|
||||
xg[d] = nullptr;
|
||||
wg[d] = nullptr;
|
||||
gpar[d].resize(1,1);
|
||||
gpar[d].fill(svol->endparam(d));
|
||||
}
|
||||
else
|
||||
this->getGaussPointParameters(gpar[d],d,nGP,xg);
|
||||
{
|
||||
int n = this->getNoGaussPt(svol->order(d),true);
|
||||
ng[d] = integrand.getBouIntegrationPoints(n);
|
||||
xg[d] = GaussQuadrature::getCoord(ng[d]);
|
||||
wg[d] = GaussQuadrature::getWeight(ng[d]);
|
||||
if (xg[d] && wg[d])
|
||||
this->getGaussPointParameters(gpar[d],d,ng[d],xg[d]);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const int tt1 = t1 > t2 ? t2-1 : t1-1;
|
||||
const int tt2 = t1 > t2 ? t1-1 : t2-1;
|
||||
const int nG1 = ng[tt1];
|
||||
const int nG2 = ng[tt2];
|
||||
|
||||
// Extract the Neumann order flag (1 or higher) for the integrand
|
||||
integrand.setNeumannOrder(1 + lIndex/10);
|
||||
@@ -2607,48 +2622,54 @@ bool ASMs3D::integrate (Integrand& integrand, int lIndex,
|
||||
}
|
||||
|
||||
// Define some loop control variables depending on which face we are on
|
||||
int nf1, j1, j2;
|
||||
int nf1 = 0, j1 = 0, j2 = 0;
|
||||
switch (abs(faceDir))
|
||||
{
|
||||
case 1: nf1 = nel2; j2 = i3-p3; j1 = i2-p2; break;
|
||||
case 2: nf1 = nel1; j2 = i3-p3; j1 = i1-p1; break;
|
||||
case 3: nf1 = nel1; j2 = i2-p2; j1 = i1-p1; break;
|
||||
default: nf1 = j1 = j2 = 0;
|
||||
}
|
||||
|
||||
|
||||
// --- Integration loop over all Gauss points in each direction --------
|
||||
|
||||
int k1, k2, k3;
|
||||
int ip = (j2*nGP*nf1 + j1)*nGP;
|
||||
int jp = (j2*nf1 + j1)*nGP*nGP;
|
||||
int ip = (j2*nf1*nG2 + j1)*nG1;
|
||||
int jp = (j2*nf1 + j1)*nG1*nG2;
|
||||
fe.iGP = firstp + jp; // Global integration point counter
|
||||
|
||||
for (int j = 0; j < nGP; j++, ip += nGP*(nf1-1))
|
||||
for (int i = 0; i < nGP; i++, ip++, fe.iGP++)
|
||||
for (int j = 0; j < nG2; j++, ip += nG1*(nf1-1))
|
||||
for (int i = 0; i < nG1; i++, ip++, fe.iGP++)
|
||||
{
|
||||
#if SP_DEBUG > 4
|
||||
std::cout <<"Elem "<< iel <<": "<< i1-p1 <<" "<< i2-p2 <<" "<< i3-p3
|
||||
<<", Face "<< faceDir <<": "<< j1 <<" "<< j2
|
||||
<<", Point "<< ip <<": "<< i <<" "<< j <<" "
|
||||
<< spline.size() << std::endl;
|
||||
#endif
|
||||
|
||||
// Local element coordinates and parameter values
|
||||
// of current integration point
|
||||
int k1 = 0, k2 = 0, k3 = 0;
|
||||
switch (abs(faceDir))
|
||||
{
|
||||
case 1: k2 = i; k3 = j; k1 = 0; break;
|
||||
case 2: k1 = i; k3 = j; k2 = 0; break;
|
||||
case 3: k1 = i; k2 = j; k3 = 0; break;
|
||||
default: k1 = k2 = k3 = 0;
|
||||
case 1: k2 = i; k3 = j; break;
|
||||
case 2: k1 = i; k3 = j; break;
|
||||
case 3: k1 = i; k2 = j; break;
|
||||
}
|
||||
if (gpar[0].size() > 1)
|
||||
|
||||
if (xg[0])
|
||||
{
|
||||
fe.xi = xg[k1];
|
||||
fe.xi = xg[0][k1];
|
||||
fe.u = param[0] = gpar[0](k1+1,i1-p1+1);
|
||||
}
|
||||
if (gpar[1].size() > 1)
|
||||
if (xg[1])
|
||||
{
|
||||
fe.eta = xg[k2];
|
||||
fe.eta = xg[1][k2];
|
||||
fe.v = param[1] = gpar[1](k2+1,i2-p2+1);
|
||||
}
|
||||
if (gpar[2].size() > 1)
|
||||
if (xg[2])
|
||||
{
|
||||
fe.zeta = xg[k3];
|
||||
fe.zeta = xg[2][k3];
|
||||
fe.w = param[2] = gpar[2](k3+1,i3-p3+1);
|
||||
}
|
||||
|
||||
@@ -2675,7 +2696,7 @@ bool ASMs3D::integrate (Integrand& integrand, int lIndex,
|
||||
X.t = time.t;
|
||||
|
||||
// Evaluate the integrand and accumulate element contributions
|
||||
fe.detJxW *= dA*wg[i]*wg[j];
|
||||
fe.detJxW *= dA*wg[tt1][i]*wg[tt2][j];
|
||||
if (!integrand.evalBou(*A,fe,time,X,normal))
|
||||
ok = false;
|
||||
}
|
||||
|
||||
@@ -671,13 +671,6 @@ protected:
|
||||
void getCornerPoints(int i1, int i2, int i3,
|
||||
std::vector<utl::Point>& XC) const;
|
||||
|
||||
//! \brief Generates element groups for multi-threading of interior integrals.
|
||||
//! \param[in] integrand Object with problem-specific data and methods
|
||||
//! \param[in] silence If \e true, suppress threading group outprint
|
||||
//! \param[in] ignoreGlobalLM Sanity check option
|
||||
virtual void generateThreadGroups(const Integrand& integrand, bool silence,
|
||||
bool ignoreGlobalLM);
|
||||
|
||||
//! \brief Generates element groups for multi-threading of interior integrals.
|
||||
//! \param[in] strip1 Strip width in first direction
|
||||
//! \param[in] strip2 Strip width in second direction
|
||||
@@ -687,6 +680,14 @@ protected:
|
||||
void generateThreadGroups(size_t strip1, size_t strip2, size_t strip3,
|
||||
bool silence, bool ignoreGlobalLM);
|
||||
|
||||
public:
|
||||
//! \brief Generates element groups for multi-threading of interior integrals.
|
||||
//! \param[in] integrand Object with problem-specific data and methods
|
||||
//! \param[in] silence If \e true, suppress threading group outprint
|
||||
//! \param[in] ignoreGlobalLM Sanity check option
|
||||
virtual void generateThreadGroups(const Integrand& integrand, bool silence,
|
||||
bool ignoreGlobalLM);
|
||||
|
||||
//! \brief Generates element groups for multi-threading of boundary integrals.
|
||||
//! \param[in] lIndex Local index [1,6] of the boundary face
|
||||
//! \param[in] silence If \e true, suppress threading group outprint
|
||||
@@ -695,7 +696,6 @@ protected:
|
||||
//! \brief Generates element groups from a partition.
|
||||
virtual void generateThreadGroupsFromElms(const IntVec& elms);
|
||||
|
||||
public:
|
||||
//! \brief Auxilliary function for computation of basis function indices.
|
||||
static void scatterInd(int n1, int n2, int n3, int p1, int p2, int p3,
|
||||
const int* start, IntVec& index);
|
||||
|
||||
@@ -553,12 +553,44 @@ bool ASMs3DLag::integrate (Integrand& integrand, int lIndex,
|
||||
const int t2 = 1 + t1%3; // second tangent direction of the face
|
||||
|
||||
// Get Gaussian quadrature points and weights
|
||||
// For now, use the largest polynomial order of the two tangent directions
|
||||
int nG1 = this->getNoGaussPt(std::max(svol->order(t1-1),svol->order(t2-1)),true);
|
||||
int nGP = integrand.getBouIntegrationPoints(nG1);
|
||||
const double* xg = GaussQuadrature::getCoord(nGP);
|
||||
const double* wg = GaussQuadrature::getWeight(nGP);
|
||||
if (!xg || !wg) return false;
|
||||
// and compute parameter values of the Gauss points over the whole patch face
|
||||
std::array<int,3> ng;
|
||||
std::array<const double*,3> xg, wg;
|
||||
std::array<Matrix,3> gpar;
|
||||
for (int d = 0; d < 3; d++)
|
||||
if (-1-d == faceDir)
|
||||
{
|
||||
ng[d] = 1;
|
||||
xg[d] = nullptr;
|
||||
wg[d] = nullptr;
|
||||
gpar[d].resize(1,1);
|
||||
gpar[d].fill(svol->startparam(d));
|
||||
}
|
||||
else if (1+d == faceDir)
|
||||
{
|
||||
ng[d] = 1;
|
||||
xg[d] = nullptr;
|
||||
wg[d] = nullptr;
|
||||
gpar[d].resize(1,1);
|
||||
gpar[d].fill(svol->endparam(d));
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = this->getNoGaussPt(svol->order(d),true);
|
||||
ng[d] = integrand.getBouIntegrationPoints(n);
|
||||
xg[d] = GaussQuadrature::getCoord(ng[d]);
|
||||
wg[d] = GaussQuadrature::getWeight(ng[d]);
|
||||
if (xg[d] && wg[d])
|
||||
this->getGaussPointParameters(gpar[d],d,ng[d],xg[d]);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const int tt0 = t0-1;
|
||||
const int tt1 = t1 > t2 ? t2-1 : t1-1;
|
||||
const int tt2 = t1 > t2 ? t1-1 : t2-1;
|
||||
const int nG1 = ng[tt1];
|
||||
const int nG2 = ng[tt2];
|
||||
|
||||
// Number of elements in each direction
|
||||
const int nel1 = (nx-1)/(p1-1);
|
||||
@@ -594,6 +626,12 @@ bool ASMs3DLag::integrate (Integrand& integrand, int lIndex,
|
||||
std::map<char,size_t>::const_iterator iit = firstBp.find(lIndex%10);
|
||||
size_t firstp = iit == firstBp.end() ? 0 : iit->second;
|
||||
|
||||
// Lambda function for linear interpolation between two values
|
||||
auto&& linearInt = [](double xi, double v1, double v2)
|
||||
{
|
||||
return 0.5*(v1*(1.0-xi) + v2*(1.0+xi));
|
||||
};
|
||||
|
||||
|
||||
// === Assembly loop over all elements on the patch face =====================
|
||||
|
||||
@@ -638,48 +676,43 @@ bool ASMs3DLag::integrate (Integrand& integrand, int lIndex,
|
||||
}
|
||||
|
||||
// Define some loop control variables depending on which face we are on
|
||||
int nf1, j1, j2;
|
||||
int nf1 = 0, j1 = 0, j2 = 0;
|
||||
switch (abs(faceDir))
|
||||
{
|
||||
case 1: nf1 = nel2; j2 = i3; j1 = i2; break;
|
||||
case 2: nf1 = nel1; j2 = i3; j1 = i1; break;
|
||||
case 3: nf1 = nel1; j2 = i2; j1 = i1; break;
|
||||
default: nf1 = j1 = j2 = 0;
|
||||
}
|
||||
|
||||
|
||||
// --- Integration loop over all Gauss points in each direction --------
|
||||
|
||||
int k1, k2, k3;
|
||||
int jp = (j2*nf1 + j1)*nGP*nGP;
|
||||
int jp = (j2*nf1 + j1)*nG1*nG2;
|
||||
fe.iGP = firstp + jp; // Global integration point counter
|
||||
|
||||
for (int j = 0; j < nGP; j++)
|
||||
for (int i = 0; i < nGP; i++, fe.iGP++)
|
||||
for (int j = 0; j < nG2; j++)
|
||||
for (int i = 0; i < nG1; i++, fe.iGP++)
|
||||
{
|
||||
// Local element coordinates of current integration point
|
||||
xi[t0-1] = faceDir < 0 ? -1.0 : 1.0;
|
||||
xi[t1-1] = xg[i];
|
||||
xi[t2-1] = xg[j];
|
||||
xi[tt0] = faceDir < 0 ? -1.0 : 1.0;
|
||||
xi[tt1] = xg[tt1][i];
|
||||
xi[tt2] = xg[tt2][j];
|
||||
fe.xi = xi[0];
|
||||
fe.eta = xi[1];
|
||||
fe.zeta = xi[2];
|
||||
|
||||
// Local element coordinates and parameter values
|
||||
// of current integration point
|
||||
switch (abs(faceDir))
|
||||
{
|
||||
case 1: k2 = i; k3 = j; k1 = -1; break;
|
||||
case 2: k1 = i; k3 = j; k2 = -1; break;
|
||||
case 3: k1 = i; k2 = j; k3 = -1; break;
|
||||
default: k1 = k2 = k3 = -1;
|
||||
}
|
||||
if (upar.size() > 1)
|
||||
fe.u = 0.5*(upar[i1]*(1.0-xg[k1]) + upar[i1+1]*(1.0+xg[k1]));
|
||||
if (vpar.size() > 1)
|
||||
fe.v = 0.5*(vpar[i2]*(1.0-xg[k2]) + vpar[i2+1]*(1.0+xg[k2]));
|
||||
if (wpar.size() > 1)
|
||||
fe.w = 0.5*(wpar[i3]*(1.0-xg[k3]) + wpar[i3+1]*(1.0+xg[k3]));
|
||||
int k1 = -1, k2 = -1, k3 = -1;
|
||||
switch (abs(faceDir))
|
||||
{
|
||||
case 1: k2 = i; k3 = j; break;
|
||||
case 2: k1 = i; k3 = j; break;
|
||||
case 3: k1 = i; k2 = j; break;
|
||||
}
|
||||
if (xg[0]) fe.u = linearInt(xg[0][k1],upar[i1],upar[i1+1]);
|
||||
if (xg[1]) fe.v = linearInt(xg[1][k2],vpar[i2],vpar[i2+1]);
|
||||
if (xg[2]) fe.w = linearInt(xg[2][k3],wpar[i3],wpar[i3+1]);
|
||||
|
||||
// Compute the basis functions and their derivatives, using
|
||||
// tensor product of one-dimensional Lagrange polynomials
|
||||
@@ -697,7 +730,7 @@ bool ASMs3DLag::integrate (Integrand& integrand, int lIndex,
|
||||
X.t = time.t;
|
||||
|
||||
// Evaluate the integrand and accumulate element contributions
|
||||
fe.detJxW *= wg[i]*wg[j];
|
||||
fe.detJxW *= wg[tt1][i]*wg[tt2][j];
|
||||
if (!integrand.evalBou(*A,fe,time,X,normal))
|
||||
ok = false;
|
||||
}
|
||||
@@ -967,6 +1000,7 @@ bool ASMs3DLag::evalSolution (Matrix& sField, const IntegrandBase& integrand,
|
||||
const int nel = this->getNoElms(true);
|
||||
for (int iel = 1; iel <= nel; iel++)
|
||||
{
|
||||
fe.iel = MLGE[iel-1];
|
||||
const IntVec& mnpc = MNPC[iel-1];
|
||||
this->getElementCoordinates(Xnod,iel);
|
||||
|
||||
@@ -981,8 +1015,6 @@ bool ASMs3DLag::evalSolution (Matrix& sField, const IntegrandBase& integrand,
|
||||
if (!Lagrange::computeBasis(fe.N,dNdu,p1,fe.xi,p2,fe.eta,p3,fe.zeta))
|
||||
return false;
|
||||
|
||||
fe.iel = MLGE[iel-1];
|
||||
|
||||
// Compute the Jacobian inverse
|
||||
fe.detJxW = utl::Jacobian(Jac,fe.dNdX,Xnod,dNdu);
|
||||
|
||||
@@ -1094,7 +1126,7 @@ bool ASMs3DLag::getGridParameters (RealArray& prm, int dir, int nSegPerSpan) con
|
||||
}
|
||||
|
||||
|
||||
bool ASMs3DLag::write(std::ostream& os, int) const
|
||||
bool ASMs3DLag::write (std::ostream& os, int) const
|
||||
{
|
||||
return this->writeLagBasis(os, "hexahedron");
|
||||
return this->writeLagBasis(os,"hexahedron");
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
//==============================================================================
|
||||
|
||||
#include "ASMCube.h"
|
||||
#include "IntegrandBase.h"
|
||||
#include "GlobalIntegral.h"
|
||||
#include "SIM3D.h"
|
||||
#include <array>
|
||||
|
||||
@@ -326,3 +328,43 @@ TEST(TestASMs3D, Collapse)
|
||||
EXPECT_FALSE(pch.collapseFace(iface,iedge));
|
||||
}
|
||||
}
|
||||
|
||||
class NoProblem : public IntegrandBase
|
||||
{
|
||||
public:
|
||||
NoProblem() : IntegrandBase(3) {}
|
||||
virtual ~NoProblem() {}
|
||||
protected:
|
||||
virtual bool evalBou(LocalIntegral&, const FiniteElement&,
|
||||
const Vec3&, const Vec3&) const { return true; }
|
||||
};
|
||||
|
||||
|
||||
TEST(TestASMs3D, FaceIntegrate)
|
||||
{
|
||||
GlobalIntegral dummy;
|
||||
NoProblem prb;
|
||||
ASMCube patch;
|
||||
ASMbase::resetNumbering();
|
||||
|
||||
ASSERT_TRUE(patch.raiseOrder(2,1,0));
|
||||
ASSERT_TRUE(patch.generateFEMTopology());
|
||||
for (int lIndex = 1; lIndex <= 6; lIndex++)
|
||||
{
|
||||
patch.generateThreadGroups(lIndex,false,false);
|
||||
ASSERT_TRUE(patch.integrate(prb,lIndex,dummy,TimeDomain()));
|
||||
}
|
||||
|
||||
patch.clear(true);
|
||||
ASMbase::resetNumbering();
|
||||
|
||||
ASSERT_TRUE(patch.uniformRefine(0,3));
|
||||
ASSERT_TRUE(patch.uniformRefine(1,2));
|
||||
ASSERT_TRUE(patch.uniformRefine(2,1));
|
||||
ASSERT_TRUE(patch.generateFEMTopology());
|
||||
for (int lIndex = 1; lIndex <= 6; lIndex++)
|
||||
{
|
||||
patch.generateThreadGroups(lIndex,false,false);
|
||||
ASSERT_TRUE(patch.integrate(prb,lIndex,dummy,TimeDomain()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user