bool LagrangeFields2D::gradFE (const FiniteElement& fe, Matrix& grad) const { grad.resize(nf,2,true); Vector N; Matrix dNdu; if (!Lagrange::computeBasis(N,dNdu,p1,fe.xi,p2,fe.eta)) return false; const int nel1 = (n1-1)/p1; div_t divresult = div(fe.iel,nel1); int iel1 = divresult.rem; int iel2 = divresult.quot; const int node1 = p1*iel1-1; const int node2 = p2*iel2-1; const int nen = (p1+1)*(p2+1); Matrix Xnod(2,nen), Vnod(nf,nen); int locNode = 1; for (int j = node2; j <= node2+p2; j++) for (int i = node1; i <= node1+p1; i++, locNode++) { int node = (j-1)*n1 + i; Xnod.fillColumn(locNode,coord.getColumn(node)); Vnod.fillColumn(locNode,values.ptr()+nf*(node-1)); } Matrix Jac, dNdX; utl::Jacobian(Jac,dNdX,Xnod,dNdu); grad.multiply(Vnod,dNdX); return true; }
bool LagrangeField3D::gradFE (const FiniteElement& fe, Vector& grad) const { grad.resize(3,true); Vector Vnod; Matrix dNdu; if (!Lagrange::computeBasis(Vnod,dNdu,p1,fe.xi,p2,fe.eta,p3,fe.zeta)) return false; const int nel1 = (n1-1)/p1; const int nel2 = (n2-1)/p2; div_t divresult = div(fe.iel,nel1*nel2); int iel2 = divresult.rem; int iel3 = divresult.quot; divresult = div(iel2,nel1); int iel1 = divresult.rem; iel2 = divresult.quot; const int node1 = p1*iel1-1; const int node2 = p2*iel2-1; const int node3 = p3*iel3-1; const int nen = (p1+1)*(p2+1)*(p3+1); Matrix Xnod(3,nen); int locNode = 1; for (int k = node3; k <= node3+p3; k++) for (int j = node2; j <= node2+p2; j++) for (int i = node1; i <= node1+p1; i++, locNode++) { int node = (k-1)*n1*n2 + (j-1)*n1 + i; Xnod.fillColumn(locNode,coord.getColumn(node)); Vnod(locNode) = values(node); } Matrix Jac, dNdX; utl::Jacobian(Jac,dNdX,Xnod,dNdu); return dNdX.multiply(Vnod,grad); }
bool ASMs3DLag::integrate (Integrand& integrand, GlobalIntegral& glInt, const TimeDomain& time) { if (!svol) return true; // silently ignore empty patches // Get Gaussian quadrature points and weights const double* xg = GaussQuadrature::getCoord(nGauss); const double* wg = GaussQuadrature::getWeight(nGauss); if (!xg || !wg) return false; // Get the reduced integration quadrature points, if needed const double* xr = nullptr; const double* wr = nullptr; int nRed = integrand.getReducedIntegration(nGauss); if (nRed > 0) { xr = GaussQuadrature::getCoord(nRed); wr = GaussQuadrature::getWeight(nRed); if (!xr || !wr) return false; } else if (nRed < 0) nRed = nGauss; // The integrand needs to know nGauss // Get parametric coordinates of the elements RealArray upar, vpar, wpar; this->getGridParameters(upar,0,1); this->getGridParameters(vpar,1,1); this->getGridParameters(wpar,2,1); // Number of elements in each direction const int nel1 = upar.size() - 1; const int nel2 = vpar.size() - 1; // 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); // === Assembly loop over all elements in the patch ========================== bool ok = true; for (size_t g = 0; g < threadGroupsVol.size() && ok; g++) { #pragma omp parallel for schedule(static) for (size_t t = 0; t < threadGroupsVol[g].size(); t++) { FiniteElement fe(p1*p2*p3); Matrix dNdu, Xnod, Jac; Vec4 X; for (size_t l = 0; l < threadGroupsVol[g][t].size() && ok; l++) { int iel = threadGroupsVol[g][t][l]; int i1 = iel % nel1; int i2 = (iel / nel1) % nel2; int i3 = iel / (nel1*nel2); // Set up nodal point coordinates for current element if (!this->getElementCoordinates(Xnod,++iel)) { ok = false; break; } if (integrand.getIntegrandType() & Integrand::ELEMENT_CENTER) { // Compute the element "center" (average of element node coordinates) X = 0.0; for (size_t i = 1; i <= 3; i++) for (size_t j = 1; j <= Xnod.cols(); j++) X[i-1] += Xnod(i,j); X *= 1.0/(double)Xnod.cols(); } // Initialize element quantities fe.iel = MLGE[iel-1]; LocalIntegral* A = integrand.getLocalIntegral(fe.N.size(),fe.iel); if (!integrand.initElement(MNPC[iel-1],fe,X,nRed*nRed*nRed,*A)) { A->destruct(); ok = false; break; } if (xr) { // --- Selective reduced integration loop ---------------------------- for (int k = 0; k < nRed; k++) for (int j = 0; j < nRed; j++) for (int i = 0; i < nRed; i++) { // Local element coordinates of current integration point fe.xi = xr[i]; fe.eta = xr[j]; fe.zeta = xr[k]; // Parameter value of current integration point fe.u = 0.5*(upar[i1]*(1.0-xr[i]) + upar[i1+1]*(1.0+xr[i])); fe.v = 0.5*(vpar[i2]*(1.0-xr[j]) + vpar[i2+1]*(1.0+xr[j])); fe.w = 0.5*(wpar[i3]*(1.0-xr[k]) + wpar[i3+1]*(1.0+xr[k])); // Compute basis function derivatives at current point // using tensor product of one-dimensional Lagrange polynomials if (!Lagrange::computeBasis(fe.N,dNdu, p1,xr[i],p2,xr[j],p3,xr[k])) { ok = false; break; } // Compute Jacobian inverse and derivatives fe.detJxW = utl::Jacobian(Jac,fe.dNdX,Xnod,dNdu); // Cartesian coordinates of current integration point X = Xnod * fe.N; X.t = time.t; // Compute the reduced integration terms of the integrand fe.detJxW *= wr[i]*wr[j]*wr[k]; if (!integrand.reducedInt(*A,fe,X)) ok = false; } } // --- Integration loop over all Gauss points in each direction -------- int jp = ((i3*nel2 + i2)*nel1 + i1)*nGauss*nGauss*nGauss; fe.iGP = firstIp + jp; // Global integration point counter for (int k = 0; k < nGauss; k++) for (int j = 0; j < nGauss; j++) for (int i = 0; i < nGauss; i++, fe.iGP++) { // Local element coordinates of current integration point fe.xi = xg[i]; fe.eta = xg[j]; fe.zeta = xg[k]; // Parameter value of current integration point fe.u = 0.5*(upar[i1]*(1.0-xg[i]) + upar[i1+1]*(1.0+xg[i])); fe.v = 0.5*(vpar[i2]*(1.0-xg[j]) + vpar[i2+1]*(1.0+xg[j])); fe.w = 0.5*(wpar[i3]*(1.0-xg[k]) + wpar[i3+1]*(1.0+xg[k])); // Compute basis function derivatives at current integration point // using tensor product of one-dimensional Lagrange polynomials if (!Lagrange::computeBasis(fe.N,dNdu,p1,xg[i],p2,xg[j],p3,xg[k])) ok = false; // 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 = Xnod * fe.N; X.t = time.t; // Evaluate the integrand and accumulate element contributions fe.detJxW *= wg[i]*wg[j]*wg[k]; if (!integrand.evalInt(*A,fe,time,X)) ok = false; } // Finalize the element quantities if (ok && !integrand.finalizeElement(*A,time,firstIp+jp)) ok = false; // Assembly of global system integral if (ok && !glInt.assemble(A->ref(),fe.iel)) ok = false; A->destruct(); } } } return ok; }
bool ASMs2DLag::integrate (Integrand& integrand, GlobalIntegral& glInt, const TimeDomain& time) { if (this->empty()) return true; // silently ignore empty patches // Get Gaussian quadrature points and weights std::array<int,2> ng; std::array<const double*,2> xg, wg; for (int d = 0; d < 2; d++) { ng[d] = this->getNoGaussPt(d == 0 ? p1 : p2); xg[d] = GaussQuadrature::getCoord(ng[d]); wg[d] = GaussQuadrature::getWeight(ng[d]); if (!xg[d] || !wg[d]) return false; } // Get the reduced integration quadrature points, if needed const double* xr = nullptr; const double* wr = nullptr; int nRed = integrand.getReducedIntegration(ng[0]); if (nRed > 0) { xr = GaussQuadrature::getCoord(nRed); wr = GaussQuadrature::getWeight(nRed); if (!xr || !wr) return false; } else if (nRed < 0) nRed = ng[0]; // The integrand needs to know nGauss // Get parametric coordinates of the elements RealArray upar, vpar; this->getGridParameters(upar,0,1); this->getGridParameters(vpar,1,1); // Number of elements in each direction const int nelx = upar.empty() ? 0 : upar.size() - 1; // === 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, Xnod, Jac; Vec4 X; for (size_t i = 0; i < threadGroups[g][t].size() && ok; i++) { int iel = threadGroups[g][t][i]; int i1 = nelx > 0 ? iel % nelx : 0; int i2 = nelx > 0 ? iel / nelx : 0; // Set up nodal point coordinates for current element if (!this->getElementCoordinates(Xnod,1+iel)) { ok = false; break; } if (integrand.getIntegrandType() & Integrand::ELEMENT_CENTER) { // Compute the element "center" (average of element node coordinates) X = 0.0; for (size_t i = 1; i <= nsd; i++) for (size_t j = 1; j <= Xnod.cols(); j++) X[i-1] += Xnod(i,j); X *= 1.0/(double)Xnod.cols(); } // Initialize element quantities fe.iel = MLGE[iel]; LocalIntegral* A = integrand.getLocalIntegral(fe.N.size(),fe.iel); if (!integrand.initElement(MNPC[iel],fe,X,nRed*nRed,*A)) { A->destruct(); ok = false; break; } if (xr) { // --- Selective reduced integration loop ---------------------------- for (int j = 0; j < nRed; j++) for (int i = 0; i < nRed; i++) { // Local element coordinates of current integration point fe.xi = xr[i]; fe.eta = xr[j]; // Parameter value of current integration point if (!upar.empty()) fe.u = 0.5*(upar[i1]*(1.0-xr[i]) + upar[i1+1]*(1.0+xr[i])); if (!vpar.empty()) fe.v = 0.5*(vpar[i2]*(1.0-xr[j]) + vpar[i2+1]*(1.0+xr[j])); // Compute basis function derivatives at current point // using tensor product of one-dimensional Lagrange polynomials if (!Lagrange::computeBasis(fe.N,dNdu,p1,xr[i],p2,xr[j])) ok = false; // Compute Jacobian inverse and derivatives fe.detJxW = utl::Jacobian(Jac,fe.dNdX,Xnod,dNdu); // Cartesian coordinates of current integration point X = Xnod * fe.N; X.t = time.t; // Compute the reduced integration terms of the integrand fe.detJxW *= wr[i]*wr[j]; if (!integrand.reducedInt(*A,fe,X)) ok = false; } } // --- Integration loop over all Gauss points in each direction -------- int jp = iel*ng[0]*ng[1]; fe.iGP = firstIp + jp; // Global integration point counter for (int j = 0; j < ng[1]; j++) for (int i = 0; i < ng[0]; i++, fe.iGP++) { // Local element coordinates of current integration point fe.xi = xg[0][i]; fe.eta = xg[1][j]; // Parameter value of current integration point if (!upar.empty()) fe.u = 0.5*(upar[i1]*(1.0-xg[0][i]) + upar[i1+1]*(1.0+xg[0][i])); if (!vpar.empty()) fe.v = 0.5*(vpar[i2]*(1.0-xg[1][j]) + vpar[i2+1]*(1.0+xg[1][j])); // Compute basis function derivatives at current integration point // using tensor product of one-dimensional Lagrange polynomials if (!Lagrange::computeBasis(fe.N,dNdu,p1,xg[0][i],p2,xg[1][j])) ok = false; // 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 = Xnod * fe.N; X.t = time.t; // Evaluate the integrand and accumulate element contributions fe.detJxW *= wg[0][i]*wg[1][j]; if (!integrand.evalInt(*A,fe,time,X)) ok = false; } // Finalize the element quantities if (ok && !integrand.finalizeElement(*A,time,firstIp+jp)) 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, 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; }