bool SplineField2D::gradFE (const FiniteElement& fe, Vector& grad) const { if (!basis) return false; if (!surf) return false; // Evaluate the basis functions at the given point Go::BasisDerivsSf spline; #pragma omp critical surf->computeBasis(fe.u,fe.v,spline); const int uorder = surf->order_u(); const int vorder = surf->order_v(); const size_t nen = uorder*vorder; Matrix dNdu(nen,2), dNdX; for (size_t n = 1; n <= nen; n++) { dNdu(n,1) = spline.basisDerivs_u[n-1]; dNdu(n,2) = spline.basisDerivs_v[n-1]; } IntVec ip; ASMs2D::scatterInd(surf->numCoefs_u(),surf->numCoefs_v(), uorder,vorder,spline.left_idx,ip); // Evaluate the Jacobian inverse Matrix Xnod, Jac; Vector Xctrl(&(*surf->coefs_begin()),surf->coefs_end()-surf->coefs_begin()); utl::gather(ip,surf->dimension(),Xctrl,Xnod); utl::Jacobian(Jac,dNdX,Xnod,dNdu); // Evaluate the gradient of the solution field at the given point if (basis != surf) { // Mixed formulation, the solution uses a different basis than the geometry #pragma omp critical basis->computeBasis(fe.u,fe.v,spline); const size_t nbf = basis->order_u()*basis->order_v(); dNdu.resize(nbf,2); for (size_t n = 1; n <= nbf; n++) { dNdu(n,1) = spline.basisDerivs_u[n-1]; dNdu(n,2) = spline.basisDerivs_v[n-1]; } dNdX.multiply(dNdu,Jac); // dNdX = dNdu * Jac ip.clear(); ASMs2D::scatterInd(basis->numCoefs_u(),basis->numCoefs_v(), basis->order_u(),basis->order_v(), spline.left_idx,ip); } Vector Vnod; utl::gather(ip,1,values,Vnod); return dNdX.multiply(Vnod,grad,true); // grad = dNdX * Vnod^t }
bool ASMs2DSpec::evalSolution (Matrix& sField, const IntegrandBase& integrand, const RealArray*, bool) const { sField.resize(0,0); Vector wg1,xg1,wg2,xg2; if (!Legendre::GLL(wg1,xg1,p1)) return false; if (!Legendre::GLL(wg2,xg2,p2)) return false; Matrix D1, D2; if (!Legendre::basisDerivatives(p1,D1)) return false; if (!Legendre::basisDerivatives(p2,D2)) return false; size_t nPoints = this->getNoNodes(); IntVec check(nPoints,0); FiniteElement fe(p1*p2); Vector solPt; Vectors globSolPt(nPoints); Matrix dNdu(p1*p2,2), Xnod, Jac; // Evaluate the secondary solution field at each point const int nel = this->getNoElms(); for (int iel = 1; iel <= nel; iel++) { const IntVec& mnpc = MNPC[iel-1]; this->getElementCoordinates(Xnod,iel); int i, j, loc = 0; for (j = 0; j < p2; j++) for (i = 0; i < p1; i++, loc++) { evalBasis(i+1,j+1,p1,p2,D1,D2,fe.N,dNdu); // Compute the Jacobian inverse fe.detJxW = utl::Jacobian(Jac,fe.dNdX,Xnod,dNdu); // Now evaluate the solution field if (!integrand.evalSol(solPt,fe,Xnod.getColumn(loc+1),mnpc)) return false; else if (sField.empty()) sField.resize(solPt.size(),nPoints,true); if (++check[mnpc[loc]] == 1) globSolPt[mnpc[loc]] = solPt; else globSolPt[mnpc[loc]] += solPt; } } for (size_t i = 0; i < nPoints; i++) sField.fillColumn(1+i,globSolPt[i] /= check[i]); return true; }
bool ASMs1DSpec::evalSolution (Matrix& sField, const IntegrandBase& integrand, const RealArray*, bool) const { sField.resize(0,0); if (!curv) return false; const int p1 = curv->order(); Matrix D1; if (!Legendre::basisDerivatives(p1,D1)) return false; size_t nPoints = this->getNoNodes(); IntVec check(nPoints,0); FiniteElement fe(p1); Vector solPt; Vectors globSolPt(nPoints); Matrix dNdu(p1,1), Xnod, Jac; // Evaluate the secondary solution field at each point const int nel = this->getNoElms(); for (int iel = 1; iel <= nel; iel++) { const IntVec& mnpc = MNPC[iel-1]; this->getElementCoordinates(Xnod,iel); for (int i = 0; i < p1; i++) { fe.N.fill(0.0); fe.N(i+1) = 1.0; dNdu.fillColumn(1,D1.getRow(i+1)); // Compute the Jacobian inverse fe.detJxW = utl::Jacobian(Jac,fe.dNdX,Xnod,dNdu); // Now evaluate the solution field if (!integrand.evalSol(solPt,fe,Xnod.getColumn(i+1),mnpc)) return false; else if (sField.empty()) sField.resize(solPt.size(),nPoints,true); if (++check[mnpc[i]] == 1) globSolPt[mnpc[i]] = solPt; else globSolPt[mnpc[i]] += solPt; } } for (size_t i = 0; i < nPoints; i++) sField.fillColumn(1+i,globSolPt[i] /= check[i]); return true; }
bool ASMu3Dmx::assembleL2matrices (SparseMatrix& A, StdVector& B, const IntegrandBase& integrand, bool continuous) const { const int p1 = projBasis->order(0); const int p2 = projBasis->order(1); const int p3 = projBasis->order(2); // Get Gaussian quadrature points const int ng1 = continuous ? nGauss : p1 - 1; const int ng2 = continuous ? nGauss : p2 - 1; const int ng3 = continuous ? nGauss : p3 - 1; const double* xg = GaussQuadrature::getCoord(ng1); const double* yg = GaussQuadrature::getCoord(ng2); const double* zg = GaussQuadrature::getCoord(ng3); const double* wg = continuous ? GaussQuadrature::getWeight(nGauss) : nullptr; if (!xg || !yg || !zg) return false; if (continuous && !wg) return false; size_t nnod = this->getNoProjectionNodes(); double dV = 0.0; Vectors phi(2); Matrices dNdu(2); Matrix sField, Xnod, Jac; std::vector<Go::BasisDerivs> spl1(2); std::vector<Go::BasisPts> spl0(2); // === Assembly loop over all elements in the patch ========================== LR::LRSplineVolume* geoVol; if (m_basis[geoBasis-1]->nBasisFunctions() == projBasis->nBasisFunctions()) geoVol = m_basis[geoBasis-1].get(); else geoVol = projBasis.get(); for (const LR::Element* el1 : geoVol->getAllElements()) { double uh = (el1->umin()+el1->umax())/2.0; double vh = (el1->vmin()+el1->vmax())/2.0; double wh = (el1->wmin()+el1->wmax())/2.0; std::vector<size_t> els; els.push_back(projBasis->getElementContaining(uh, vh, wh) + 1); els.push_back(m_basis[geoBasis-1]->getElementContaining(uh, vh, wh) + 1); if (continuous) { // Set up control point (nodal) coordinates for current element if (!this->getElementCoordinates(Xnod,els[1])) return false; else if ((dV = 0.25*this->getParametricVolume(els[1])) < 0.0) return false; // topology error (probably logic error) } // Compute parameter values of the Gauss points over this element RealArray gpar[3], unstrGpar[3]; this->getGaussPointParameters(gpar[0],0,ng1,els[1],xg); this->getGaussPointParameters(gpar[1],1,ng2,els[1],yg); this->getGaussPointParameters(gpar[2],2,ng3,els[1],zg); // convert to unstructred mesh representation expandTensorGrid(gpar, unstrGpar); // Evaluate the secondary solution at all integration points if (!this->evalSolution(sField,integrand,unstrGpar)) return false; // set up basis function size (for extractBasis subroutine) const LR::Element* elm = projBasis->getElement(els[0]-1); phi[0].resize(elm->nBasisFunctions()); phi[1].resize(el1->nBasisFunctions()); IntVec lmnpc; if (projBasis != m_basis[0]) { lmnpc.reserve(phi[0].size()); for (const LR::Basisfunction* f : elm->support()) lmnpc.push_back(f->getId()); } const IntVec& mnpc = projBasis == m_basis[0] ? MNPC[els[1]-1] : lmnpc; // --- Integration loop over all Gauss points in each direction ---------- Matrix eA(phi[0].size(), phi[0].size()); Vectors eB(sField.rows(), Vector(phi[0].size())); int ip = 0; for (int k = 0; k < ng3; k++) for (int j = 0; j < ng2; j++) for (int i = 0; i < ng1; i++, ip++) { if (continuous) { projBasis->computeBasis(gpar[0][i], gpar[1][j], gpar[2][k], spl1[0], els[0]-1); SplineUtils::extractBasis(spl1[0],phi[0],dNdu[0]); m_basis[geoBasis-1]->computeBasis(gpar[0][i], gpar[1][j], gpar[2][k], spl1[1], els[1]-1); SplineUtils::extractBasis(spl1[1], phi[1], dNdu[1]); } else { projBasis->computeBasis(gpar[0][i], gpar[1][j], gpar[2][k], spl0[0], els[0]-1); phi[0] = spl0[0].basisValues; } // Compute the Jacobian inverse and derivatives double dJw = 1.0; if (continuous) { dJw = dV*wg[i]*wg[j]*wg[k]*utl::Jacobian(Jac,dNdu[1],Xnod,dNdu[1],false); if (dJw == 0.0) continue; // skip singular points } // Integrate the mass matrix eA.outer_product(phi[0], phi[0], true, dJw); // Integrate the rhs vector B for (size_t r = 1; r <= sField.rows(); r++) eB[r-1].add(phi[0],sField(r,ip+1)*dJw); } for (size_t i = 0; i < eA.rows(); ++i) { for (size_t j = 0; j < eA.cols(); ++j) A(mnpc[i]+1, mnpc[j]+1) += eA(i+1,j+1); int jp = mnpc[i]+1; for (size_t r = 0; r < sField.rows(); r++, jp += nnod) B(jp) += eB[r](1+i); } } return true; }
bool SplineField2D::hessianFE(const FiniteElement& fe, Matrix& H) const { if (!basis) return false; if (!surf) return false; // Order of basis const int uorder = surf->order_u(); const int vorder = surf->order_v(); const size_t nen = uorder*vorder; // Evaluate the basis functions at the given point Go::BasisDerivsSf spline; Go::BasisDerivsSf2 spline2; Matrix3D d2Ndu2; Matrix dNdu, dNdX; IntVec ip; #pragma omp critical if (surf == basis) { surf->computeBasis(fe.u,fe.v,spline2); dNdu.resize(nen,2); d2Ndu2.resize(nen,2,2); for (size_t n = 1; n <= nen; n++) { dNdu(n,1) = spline2.basisDerivs_u[n-1]; dNdu(n,2) = spline2.basisDerivs_v[n-1]; d2Ndu2(n,1,1) = spline2.basisDerivs_uu[n-1]; d2Ndu2(n,1,2) = d2Ndu2(n,2,1) = spline2.basisDerivs_uv[n-1]; d2Ndu2(n,2,2) = spline2.basisDerivs_vv[n-1]; } ASMs2D::scatterInd(surf->numCoefs_u(),surf->numCoefs_v(), uorder,vorder,spline2.left_idx,ip); } else { surf->computeBasis(fe.u,fe.v,spline); dNdu.resize(nen,2); for (size_t n = 1; n <= nen; n++) { dNdu(n,1) = spline.basisDerivs_u[n-1]; dNdu(n,2) = spline.basisDerivs_v[n-1]; } ASMs2D::scatterInd(surf->numCoefs_u(),surf->numCoefs_v(), uorder,vorder,spline.left_idx,ip); } // Evaluate the Jacobian inverse Matrix Xnod, Jac; Vector Xctrl(&(*surf->coefs_begin()),surf->coefs_end()-surf->coefs_begin()); utl::gather(ip,surf->dimension(),Xctrl,Xnod); utl::Jacobian(Jac,dNdX,Xnod,dNdu); // Evaluate the gradient of the solution field at the given point if (basis != surf) { // Mixed formulation, the solution uses a different basis than the geometry #pragma omp critical basis->computeBasis(fe.u,fe.v,spline2); const size_t nbf = basis->order_u()*basis->order_v(); dNdu.resize(nbf,2); d2Ndu2.resize(nbf,2,2); for (size_t n = 1; n <= nbf; n++) { dNdu(n,1) = spline2.basisDerivs_u[n-1]; dNdu(n,2) = spline2.basisDerivs_v[n-1]; d2Ndu2(n,1,1) = spline2.basisDerivs_uu[n-1]; d2Ndu2(n,1,2) = d2Ndu2(n,2,1) = spline2.basisDerivs_uv[n-1]; d2Ndu2(n,2,2) = spline2.basisDerivs_vv[n-1]; } ip.clear(); ASMs2D::scatterInd(basis->numCoefs_u(),basis->numCoefs_v(), basis->order_u(),basis->order_v(), spline2.left_idx,ip); } Vector Vnod; utl::gather(ip,1,values,Vnod); return H.multiply(d2Ndu2,Vnod); }
bool ASMs2DSpec::integrate (Integrand& integrand, GlobalIntegral& glInt, const TimeDomain& time) { if (this->empty()) return true; // silently ignore empty patches // Evaluate integration points (= nodal points) and weights Vector wg1,xg1,wg2,xg2; if (!Legendre::GLL(wg1,xg1,p1)) return false; if (!Legendre::GLL(wg2,xg2,p2)) return false; Matrix D1, D2; if (!Legendre::basisDerivatives(p1,D1)) return false; if (!Legendre::basisDerivatives(p2,D2)) return false; // === Assembly loop over all elements in the patch ========================== bool ok = true; for (size_t g = 0; g < threadGroups.size() && ok; g++) { #pragma omp parallel for schedule(static) for (size_t t = 0; t < threadGroups[g].size(); t++) { FiniteElement fe(p1*p2); Matrix dNdu(p1*p2,2), Xnod, Jac; Vec4 X; for (size_t e = 0; e < threadGroups[g][t].size(); e++) { int iel = threadGroups[g][t][e]+1; // Set up control point coordinates for current element if (!this->getElementCoordinates(Xnod,iel)) { ok = false; break; } // Initialize element quantities fe.iel = MLGE[iel-1]; LocalIntegral* A = integrand.getLocalIntegral(fe.N.size(),fe.iel); if (!integrand.initElement(MNPC[iel-1],*A)) { A->destruct(); ok = false; break; } // --- Integration loop over all Gauss points in each direction -------- int count = 1; for (int j = 1; j <= p2; j++) for (int i = 1; i <= p1; i++, count++) { // Evaluate the basis functions and gradients using // tensor product of one-dimensional Lagrange polynomials evalBasis(i,j,p1,p2,D1,D2,fe.N,dNdu); // Compute Jacobian inverse of coordinate mapping and derivatives fe.detJxW = utl::Jacobian(Jac,fe.dNdX,Xnod,dNdu); if (fe.detJxW == 0.0) continue; // skip singular points // Cartesian coordinates of current integration point X.x = Xnod(1,count); X.y = Xnod(2,count); X.t = time.t; // Evaluate the integrand and accumulate element contributions fe.detJxW *= wg1(i)*wg2(j); if (!integrand.evalInt(*A,fe,time,X)) ok = false; } // Assembly of global system integral if (ok && !glInt.assemble(A->ref(),fe.iel)) ok = false; A->destruct(); } } } return ok; }
bool ASMs2DSpec::integrate (Integrand& integrand, int lIndex, GlobalIntegral& glInt, const TimeDomain& time) { if (this->empty()) return true; // silently ignore empty patches // Find the parametric direction of the edge normal {-2,-1, 1, 2} int edgeDir = lIndex = (lIndex+1)/((lIndex%2) ? -2 : 2); const int t1 = abs(edgeDir); // Tangent direction normal to the patch edge const int t2 = 3-abs(edgeDir); // Tangent direction along the patch edge // Number of elements in each direction int n1, n2; this->getSize(n1,n2); const int nelx = (n1-1)/(p1-1); const int nely = (n2-1)/(p2-1); // Evaluate integration points and weights std::array<Vector,2> wg, xg; std::array<Matrix,2> D; std::array<int,2> p({{p1,p2}}); for (int d = 0; d < 2; d++) { if (!Legendre::GLL(wg[d],xg[d],p[d])) return false; if (!Legendre::basisDerivatives(p[d],D[d])) return false; } int nen = p1*p2; FiniteElement fe(nen); Matrix dNdu(nen,2), Xnod, Jac; Vec4 X; Vec3 normal; int xi[2]; // === Assembly loop over all elements on the patch edge ===================== int iel = 1; for (int i2 = 0; i2 < nely; i2++) for (int i1 = 0; i1 < nelx; i1++, iel++) { // Skip elements that are not on current boundary edge bool skipMe = false; switch (edgeDir) { case -1: if (i1 > 0) skipMe = true; break; case 1: if (i1 < nelx-1) skipMe = true; break; case -2: if (i2 > 0) skipMe = true; break; case 2: if (i2 < nely-1) skipMe = true; break; } if (skipMe) continue; // Set up control point coordinates for current element if (!this->getElementCoordinates(Xnod,iel)) return false; // Initialize element quantities fe.iel = MLGE[iel-1]; LocalIntegral* A = integrand.getLocalIntegral(nen,fe.iel,true); if (!integrand.initElementBou(MNPC[iel-1],*A)) return false; // --- Integration loop over all Gauss points along the edge ------------- for (int i = 0; i < p[t2-1]; i++) { // "Coordinates" along the edge xi[t1-1] = edgeDir < 0 ? 1 : p[t1-1]; xi[t2-1] = i+1; // Evaluate the basis functions and gradients using // tensor product of one-dimensional Lagrange polynomials evalBasis(xi[0],xi[1],p1,p2,D[0],D[1],fe.N,dNdu); // Compute basis function derivatives and the edge normal fe.detJxW = utl::Jacobian(Jac,normal,fe.dNdX,Xnod,dNdu,t1,t2); if (fe.detJxW == 0.0) continue; // skip singular points if (edgeDir < 0) normal *= -1.0; // Cartesian coordinates of current integration point X = Xnod * fe.N; X.t = time.t; // Evaluate the integrand and accumulate element contributions fe.detJxW *= wg[t2-1][i]; if (!integrand.evalBou(*A,fe,time,X,normal)) return false; } // Finalize the element quantities if (!integrand.finalizeElementBou(*A,fe,time)) return false; // Assembly of global system integral if (!glInt.assemble(A->ref(),fe.iel)) return false; A->destruct(); } return true; }
bool ASMs3DSpec::integrateEdge (Integrand& integrand, int lEdge, GlobalIntegral& glInt, const TimeDomain& time) { if (!svol) return true; // silently ignore empty patches // Parametric direction of the edge {0, 1, 2} const int lDir = (lEdge-1)/4; // Order of basis in the three parametric directions (order = degree + 1) const int p1 = svol->order(0); const int p2 = svol->order(1); const int p3 = svol->order(2); const int pe = svol->order(lDir); // Number of elements in each direction int n1, n2, n3; this->getSize(n1,n2,n3); const int nelx = (n1-1)/(p1-1); const int nely = (n2-1)/(p2-1); const int nelz = (n3-1)/(p3-1); // Evaluate integration points (=nodal points) and weights std::array<Vector,3> wg, xg; if (!Legendre::GLL(wg[0],xg[0],p1)) return false; if (!Legendre::GLL(wg[1],xg[1],p2)) return false; if (!Legendre::GLL(wg[2],xg[2],p3)) return false; Matrix D1, D2, D3; if (!Legendre::basisDerivatives(p1,D1)) return false; if (!Legendre::basisDerivatives(p2,D2)) return false; if (!Legendre::basisDerivatives(p3,D3)) return false; const int nen = p1*p2*p3; FiniteElement fe(nen); Matrix dNdu(nen,3), Xnod, Jac; Vec4 X; Vec3 tangent; int xi[3]; switch (lEdge) { case 1: xi[1] = 1; xi[2] = 1; break; case 2: xi[1] = p2; xi[2] = 1; break; case 3: xi[1] = 1; xi[2] = p3; break; case 4: xi[1] = p2; xi[2] = p3; break; case 5: xi[0] = 1; xi[2] = 1; break; case 6: xi[0] = p1; xi[2] = 1; break; case 7: xi[0] = 1; xi[2] = p3; break; case 8: xi[0] = p1; xi[2] = p3; break; case 9: xi[0] = 1; xi[1] = 1; break; case 10: xi[0] = p1; xi[1] = 1; break; case 11: xi[0] = 1; xi[1] = p2; break; case 12: xi[0] = p1; xi[1] = p2; break; } // === Assembly loop over all elements on the patch edge ===================== int iel = 1; for (int i3 = 0; i3 < nelz; i3++) for (int i2 = 0; i2 < nely; i2++) for (int i1 = 0; i1 < nelx; i1++, iel++) { // Skip elements that are not on current boundary edge bool skipMe = false; switch (lEdge) { case 1: if (i2 > 0 || i3 > 0) skipMe = true; break; case 2: if (i2 < nely-1 || i3 > 0) skipMe = true; break; case 3: if (i2 > 0 || i3 < nelz-1) skipMe = true; break; case 4: if (i2 < nely-1 || i3 < nelz-1) skipMe = true; break; case 5: if (i1 > 0 || i3 > 0) skipMe = true; break; case 6: if (i1 < nelx-1 || i3 > 0) skipMe = true; break; case 7: if (i1 > 0 || i3 < nelz-1) skipMe = true; break; case 8: if (i1 < nelx-1 || i3 < nelz-1) skipMe = true; break; case 9: if (i1 > 0 || i2 > 0) skipMe = true; break; case 10: if (i1 < nelx-1 || i2 > 0) skipMe = true; break; case 11: if (i1 > 0 || i2 < nely-1) skipMe = true; break; case 12: if (i1 < nelx-1 || i2 < nely-1) skipMe = true; break; } if (skipMe) continue; // Set up nodal point coordinates for current element if (!this->getElementCoordinates(Xnod,iel)) return false; // Initialize element quantities fe.iel = MLGE[iel-1]; LocalIntegral* A = integrand.getLocalIntegral(nen,fe.iel,true); if (!integrand.initElementBou(MNPC[iel-1],*A)) return false; // --- Integration loop over all Gauss points along the edge ----------- for (int i = 0; i < pe; i++) { // "Coordinate" on the edge xi[lDir] = i+1; // Compute the basis functions and their derivatives, using // tensor product of one-dimensional Lagrange polynomials evalBasis(xi[0],xi[1],xi[2],p1,p2,p3,D1,D2,D3,fe.N,dNdu); // Compute basis function derivatives and the edge tangent fe.detJxW = utl::Jacobian(Jac,tangent,fe.dNdX,Xnod,dNdu,1+lDir); if (fe.detJxW == 0.0) continue; // skip singular points // Cartesian coordinates of current integration point X = Xnod * fe.N; X.t = time.t; // Evaluate the integrand and accumulate element contributions fe.detJxW *= wg[lDir][i]; if (!integrand.evalBou(*A,fe,time,X,tangent)) return false; } // Finalize the element quantities if (!integrand.finalizeElementBou(*A,fe,time)) return false; // Assembly of global system integral if (!glInt.assemble(A->ref(),fe.iel)) return false; } return true; }
bool ASMs3DSpec::integrate (Integrand& integrand, int lIndex, GlobalIntegral& glInt, const TimeDomain& time) { if (!svol) return true; // silently ignore empty patches std::map<char,ThreadGroups>::const_iterator tit; if ((tit = threadGroupsFace.find(lIndex)) == threadGroupsFace.end()) { std::cerr <<" *** ASMs3DSpec::integrate: No thread groups for face "<<lIndex << std::endl; return false; } const ThreadGroups& threadGrp = tit->second; // Find the parametric direction of the face normal {-3,-2,-1, 1, 2, 3} const int faceDir = (lIndex+1)/(lIndex%2 ? -2 : 2); const int t0 = abs(faceDir); // unsigned normal direction of the face const int t1 = 1 + t0%3; // first tangent direction of the face const int t2 = 1 + t1%3; // second tangent direction of the face // Order of basis in the three parametric directions (order = degree + 1) int p[3]; p[0] = svol->order(0); p[1] = svol->order(1); p[2] = svol->order(2); // Evaluate integration points (=nodal points) and weights std::array<Vector,3> xg, wg; std::array<Matrix,3> D; for (int d = 0; d < 3; d++) { if (!Legendre::GLL(wg[d],xg[d],p[d])) return false; if (!Legendre::basisDerivatives(p[d],D[d])) return false; } int nen = p[0]*p[1]*p[2]; // === Assembly loop over all elements on the patch face ===================== bool ok = true; for (size_t g = 0; g < threadGrp.size() && ok; g++) { #pragma omp parallel for schedule(static) for (size_t t = 0; t < threadGrp[g].size(); t++) { FiniteElement fe(nen); Matrix dNdu(nen,3), Xnod, Jac; Vec4 X; Vec3 normal; int xi[3]; for (size_t l = 0; l < threadGrp[g][t].size(); l++) { int iel = threadGrp[g][t][l]; // Set up nodal point coordinates for current element if (!this->getElementCoordinates(Xnod,++iel)) { ok = false; break; } // Initialize element quantities fe.iel = MLGE[iel-1]; LocalIntegral* A = integrand.getLocalIntegral(nen,fe.iel,true); if (!integrand.initElementBou(MNPC[iel-1],*A)) { A->destruct(); ok = false; break; } // --- Integration loop over all Gauss points in each direction -------- for (int j = 0; j < p[t2-1]; j++) for (int i = 0; i < p[t1-1]; i++) { // "Coordinates" on the face xi[t0-1] = faceDir < 0 ? 1 : p[t0-1]; xi[t1-1] = i+1; xi[t2-1] = j+1; // Compute the basis functions and their derivatives, using // tensor product of one-dimensional Lagrange polynomials evalBasis(xi[0],xi[1],xi[2],p[0],p[1],p[2],D[0],D[1],D[2],fe.N,dNdu); // Compute basis function derivatives and the face normal fe.detJxW = utl::Jacobian(Jac,normal,fe.dNdX,Xnod,dNdu,t1,t2); if (fe.detJxW == 0.0) continue; // skip singular points if (faceDir < 0) normal *= -1.0; // Cartesian coordinates of current integration point X = Xnod * fe.N; X.t = time.t; // Evaluate the integrand and accumulate element contributions fe.detJxW *= wg[t1-1][i]*wg[t2-1][j]; if (!integrand.evalBou(*A,fe,time,X,normal)) ok = false; } // Finalize the element quantities if (ok && !integrand.finalizeElementBou(*A,fe,time)) ok = false; // Assembly of global system integral if (ok && !glInt.assemble(A->ref(),fe.iel)) ok = false; A->destruct(); } } } return ok; }