bool ASMs3DLag::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 <<" *** ASMs3DLag::integrate: No thread groups for face "<< lIndex << std::endl; return false; } const ThreadGroups& threadGrp = tit->second; // Get Gaussian quadrature points and weights int nGP = integrand.getBouIntegrationPoints(nGauss); const double* xg = GaussQuadrature::getCoord(nGP); const double* wg = GaussQuadrature::getWeight(nGP); if (!xg || !wg) return false; // 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) const int p1 = svol->order(0); const int p2 = svol->order(1); const int p3 = svol->order(2); // Number of elements in each direction const int nel1 = (nx-1)/(p1-1); const int nel2 = (ny-1)/(p2-1); const int nel3 = (nz-1)/(p3-1); // Get parametric coordinates of the elements RealArray upar, vpar, wpar; if (t0 == 1) upar.resize(1,faceDir < 0 ? svol->startparam(0) : svol->endparam(0)); else if (t0 == 2) vpar.resize(1,faceDir < 0 ? svol->startparam(1) : svol->endparam(1)); else if (t0 == 3) wpar.resize(1,faceDir < 0 ? svol->startparam(2) : svol->endparam(2)); if (upar.empty()) this->getGridParameters(upar,0,1); if (vpar.empty()) this->getGridParameters(vpar,1,1); if (wpar.empty()) this->getGridParameters(wpar,2,1); // Integrate the extraordinary elements? size_t doXelms = 0; if (integrand.getIntegrandType() & Integrand::XO_ELEMENTS) if ((doXelms = nel1*nel2*nel3)*2 > MNPC.size()) { std::cerr <<" *** ASMs2DLag::integrate: Too few XO-elements " << MNPC.size() - doXelms << std::endl; return false; } std::map<char,size_t>::const_iterator iit = firstBp.find(lIndex); size_t firstp = iit == firstBp.end() ? 0 : iit->second; // === 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(p1*p2*p3); fe.u = upar.front(); fe.v = vpar.front(); fe.w = wpar.front(); Matrix dNdu, Xnod, Jac; Vec4 X; Vec3 normal; double xi[3]; for (size_t l = 0; l < threadGrp[g][t].size() && ok; l++) { int iel = threadGrp[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; } // Initialize element quantities fe.iel = abs(MLGE[doXelms+iel-1]); LocalIntegral* A = integrand.getLocalIntegral(fe.N.size(),fe.iel,true); if (!integrand.initElementBou(MNPC[doXelms+iel-1],*A)) { A->destruct(); ok = false; break; } // Define some loop control variables depending on which face we are on int nf1, j1, j2; 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; fe.iGP = firstp + jp; // Global integration point counter for (int j = 0; j < nGP; j++) for (int i = 0; i < nGP; 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]; 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])); // Compute the basis functions and their derivatives, using // tensor product of one-dimensional Lagrange polynomials if (!Lagrange::computeBasis(fe.N,dNdu,p1,xi[0],p2,xi[1],p3,xi[2])) ok = false; // 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[i]*wg[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; }
bool ASMs1D::integrate (Integrand& integrand, int lIndex, GlobalIntegral& glInt, const TimeDomain& time) { if (!curv) return true; // silently ignore empty patches // Integration of boundary point FiniteElement fe(curv->order()); size_t iel = 0; switch (lIndex) { case 1: fe.xi = -1.0; fe.u = curv->startparam(); break; case 2: fe.xi = 1.0; fe.u = curv->endparam(); iel = nel-1; break; default: return false; } std::map<char,size_t>::const_iterator iit = firstBp.find(lIndex); fe.iGP = iit == firstBp.end() ? 0 : iit->second; fe.iel = MLGE[iel]; if (fe.iel < 1) return true; // zero-length element // Set up control point coordinates for current element if (!this->getElementCoordinates(fe.Xn,1+iel)) return false; if (integrand.getIntegrandType() & Integrand::ELEMENT_CORNERS) this->getElementEnds(iel+curv->order(),fe.XC); if (integrand.getIntegrandType() & Integrand::NODAL_ROTATIONS) { this->getElementNodalRotations(fe.Tn,iel); if (!elmCS.empty()) fe.Te = elmCS[iel]; } // Initialize element matrices LocalIntegral* A = integrand.getLocalIntegral(fe.N.size(),fe.iel,true); bool ok = integrand.initElementBou(MNPC[iel],*A); Vec3 normal; // Evaluate basis functions and corresponding derivatives if (integrand.getIntegrandType() & Integrand::NO_DERIVATIVES) this->extractBasis(fe.u,fe.N); else { // Compute basis function derivatives Matrix dNdu, Jac; this->extractBasis(fe.u,fe.N,dNdu); utl::Jacobian(Jac,fe.dNdX,fe.Xn,dNdu); // Set up the normal vector if (lIndex == 1) normal.x = -copysign(1.0,Jac(1,1)); else normal.x = copysign(1.0,Jac(1,1)); } // Cartesian coordinates of current integration point Vec4 X(fe.Xn*fe.N,time.t); // Evaluate the integrand and accumulate element contributions if (ok && !integrand.evalBou(*A,fe,time,X,normal)) ok = false; // Assembly of global system integral if (ok && !glInt.assemble(A->ref(),fe.iel)) ok = false; A->destruct(); return ok; }
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 ASMs1D::integrate (Integrand& integrand, GlobalIntegral& glInt, const TimeDomain& time) { if (!curv) 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 if (integrand.getIntegrandType() & Integrand::SECOND_DERIVATIVES) if (curv->rational()) { std::cerr <<" *** ASMs1D::integrate: Second-derivatives of NURBS " <<" is not implemented yet, sorry..."<< std::endl; return false; } // Compute parameter values of the Gauss points over the whole patch Matrix gpar, redpar; this->getGaussPointParameters(gpar,nGauss,xg); if (xr) this->getGaussPointParameters(redpar,nRed,xr); const int p1 = curv->order(); FiniteElement fe(p1); Matrix dNdu, Jac; Matrix3D d2Ndu2, Hess; Vec4 X; if (nsd > 1 && (integrand.getIntegrandType() & Integrand::SECOND_DERIVATIVES)) fe.G.resize(nsd,2); // For storing d{X}/du and d2{X}/du2 // === Assembly loop over all elements in the patch ========================== for (size_t iel = 0; iel < nel; iel++) { fe.iel = MLGE[iel]; if (fe.iel < 1) continue; // zero-length element // Check that the current element has nonzero length double dL = this->getParametricLength(1+iel); if (dL < 0.0) return false; // topology error (probably logic error) // Set up control point coordinates for current element if (!this->getElementCoordinates(fe.Xn,1+iel)) return false; if (integrand.getIntegrandType() & Integrand::ELEMENT_CORNERS) this->getElementEnds(p1+iel,fe.XC); if (integrand.getIntegrandType() & Integrand::NODAL_ROTATIONS) { this->getElementNodalRotations(fe.Tn,iel); if (!elmCS.empty()) fe.Te = elmCS[iel]; } // Initialize element matrices LocalIntegral* A = integrand.getLocalIntegral(fe.N.size(),fe.iel); bool ok = integrand.initElement(MNPC[iel],fe,X,nRed,*A); if (xr) { // --- Selective reduced integration loop -------------------------------- for (int i = 0; i < nRed && ok; i++) { // Local element coordinates of current integration point fe.xi = xr[i]; // Parameter values of current integration point fe.u = redpar(1+i,1+iel); if (integrand.getIntegrandType() & Integrand::NO_DERIVATIVES) this->extractBasis(fe.u,fe.N); else { // Fetch basis function derivatives at current point this->extractBasis(fe.u,fe.N,dNdu); // Compute Jacobian inverse and derivatives dNdu.multiply(0.5*dL); // Derivatives w.r.t. xi=[-1,1] fe.detJxW = utl::Jacobian(Jac,fe.dNdX,fe.Xn,dNdu)*wr[i]; } // Cartesian coordinates of current integration point X = fe.Xn * fe.N; X.t = time.t; // Compute the reduced integration terms of the integrand ok = integrand.reducedInt(*A,fe,X); } } // --- Integration loop over all Gauss points in current element ----------- int jp = iel*nGauss; fe.iGP = firstIp + jp; // Global integration point counter for (int i = 0; i < nGauss && ok; i++, fe.iGP++) { // Local element coordinate of current integration point fe.xi = xg[i]; // Parameter value of current integration point fe.u = gpar(1+i,1+iel); // Compute basis functions and derivatives if (integrand.getIntegrandType() & Integrand::NO_DERIVATIVES) this->extractBasis(fe.u,fe.N); else if (integrand.getIntegrandType() & Integrand::SECOND_DERIVATIVES) this->extractBasis(fe.u,fe.N,dNdu,d2Ndu2); else this->extractBasis(fe.u,fe.N,dNdu); if (!dNdu.empty()) { // Compute derivatives in terms of physical coordinates dNdu.multiply(0.5*dL); // Derivatives w.r.t. xi=[-1,1] fe.detJxW = utl::Jacobian(Jac,fe.dNdX,fe.Xn,dNdu)*wg[i]; if (fe.detJxW == 0.0) continue; // skip singular points // Compute Hessian of coordinate mapping and 2nd order derivatives if (integrand.getIntegrandType() & Integrand::SECOND_DERIVATIVES) { d2Ndu2.multiply(0.25*dL*dL); // 2nd derivatives w.r.t. xi=[-1,1] if (!utl::Hessian(Hess,fe.d2NdX2,Jac,fe.Xn,d2Ndu2,fe.dNdX)) ok = false; else if (fe.G.cols() == 2) { // Store the first and second derivatives of {X} w.r.t. // the parametric coordinate (xi), in the G-matrix fe.G.fillColumn(1,Jac.ptr()); fe.G.fillColumn(2,Hess.ptr()); } } } // Cartesian coordinates of current integration point X = fe.Xn * fe.N; X.t = time.t; // Evaluate the integrand and accumulate element contributions if (ok && !integrand.evalInt(*A,fe,time,X)) ok = false; } // Finalize the element quantities if (ok && !integrand.finalizeElement(*A,fe,time,firstIp+jp)) ok = false; // Assembly of global system integral if (ok && !glInt.assemble(A->ref(),fe.iel)) ok = false; A->destruct(); if (!ok) return false; } return true; }
bool ASMs2DLag::integrate (Integrand& integrand, int lIndex, GlobalIntegral& glInt, const TimeDomain& time) { if (this->empty()) return true; // silently ignore empty patches // Get Gaussian quadrature points and weights int nG1 = this->getNoGaussPt(lIndex%10 < 3 ? p1 : p2, true); int nGP = integrand.getBouIntegrationPoints(nG1); const double* xg = GaussQuadrature::getCoord(nGP); const double* wg = GaussQuadrature::getWeight(nGP); if (!xg || !wg) return false; // Find the parametric direction of the edge normal {-2,-1, 1, 2} const int edgeDir = (lIndex%10+1)/((lIndex%2) ? -2 : 2); const int t1 = abs(edgeDir); // tangent direction normal to the patch edge const int t2 = 3-t1; // tangent direction along the patch edge // Number of elements in each direction const int nelx = (nx-1)/(p1-1); const int nely = (ny-1)/(p2-1); // Get parametric coordinates of the elements FiniteElement fe(p1*p2); RealArray upar, vpar; if (t1 == 1) { fe.u = edgeDir < 0 ? surf->startparam_u() : surf->endparam_u(); this->getGridParameters(vpar,1,1); } else if (t1 == 2) { this->getGridParameters(upar,0,1); fe.v = edgeDir < 0 ? surf->startparam_v() : surf->endparam_v(); } // Extract the Neumann order flag (1 or higher) for the integrand integrand.setNeumannOrder(1 + lIndex/10); // Integrate the extraordinary elements? size_t doXelms = 0; if (integrand.getIntegrandType() & Integrand::XO_ELEMENTS) if ((doXelms = nelx*nely)*2 > MNPC.size()) { std::cerr <<" *** ASMs2DLag::integrate: Too few XO-elements " << MNPC.size() - doXelms << std::endl; return false; } std::map<char,size_t>::const_iterator iit = firstBp.find(lIndex%10); size_t firstp = iit == firstBp.end() ? 0 : iit->second; Matrix dNdu, Xnod, Jac; Vec4 X; Vec3 normal; double 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 nodal point coordinates for current element if (!this->getElementCoordinates(Xnod,iel)) return false; // Initialize element quantities fe.iel = abs(MLGE[doXelms+iel-1]); LocalIntegral* A = integrand.getLocalIntegral(fe.N.size(),fe.iel,true); bool ok = integrand.initElementBou(MNPC[doXelms+iel-1],*A); // --- Integration loop over all Gauss points along the edge ------------- int jp = (t1 == 1 ? i2 : i1)*nGP; fe.iGP = firstp + jp; // Global integration point counter for (int i = 0; i < nGP && ok; i++, fe.iGP++) { // Local element coordinates of current integration point xi[t1-1] = edgeDir < 0 ? -1.0 : 1.0; xi[t2-1] = xg[i]; fe.xi = xi[0]; fe.eta = xi[1]; // Parameter values of current integration point if (upar.size() > 1) fe.u = 0.5*(upar[i1]*(1.0-xg[i]) + upar[i1+1]*(1.0+xg[i])); if (vpar.size() > 1) fe.v = 0.5*(vpar[i2]*(1.0-xg[i]) + vpar[i2+1]*(1.0+xg[i])); // Compute the basis functions and their derivatives, using // tensor product of one-dimensional Lagrange polynomials if (!Lagrange::computeBasis(fe.N,dNdu,p1,xi[0],p2,xi[1])) ok = false; // 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[i]; if (ok && !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(); if (!ok) return false; } return true; }
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 ASMs3Dmx::integrate (Integrand& integrand, int lIndex, GlobalIntegral& glInt, const TimeDomain& time) { if (!svol) return true; // silently ignore empty patches if (m_basis.empty()) return false; PROFILE2("ASMs3Dmx::integrate(B)"); bool useElmVtx = integrand.getIntegrandType() & Integrand::ELEMENT_CORNERS; std::map<char,ThreadGroups>::const_iterator tit; if ((tit = threadGroupsFace.find(lIndex)) == threadGroupsFace.end()) { std::cerr <<" *** ASMs3D::integrate: No thread groups for face "<< lIndex << std::endl; return false; } const ThreadGroups& threadGrp = tit->second; // Get Gaussian quadrature points and weights const double* xg = GaussQuadrature::getCoord(nGauss); const double* wg = GaussQuadrature::getWeight(nGauss); if (!xg || !wg) return false; // 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 t1 = 1 + abs(faceDir)%3; // first tangent direction const int t2 = 1 + t1%3; // second tangent direction // Compute parameter values of the Gauss points over the whole patch face std::array<Matrix,3> gpar; for (int d = 0; d < 3; d++) if (-1-d == faceDir) { gpar[d].resize(1,1); gpar[d].fill(svol->startparam(d)); } else if (1+d == faceDir) { gpar[d].resize(1,1); gpar[d].fill(svol->endparam(d)); } else this->getGaussPointParameters(gpar[d],d,nGauss,xg); // Evaluate basis function derivatives at all integration points std::vector<std::vector<Go::BasisDerivs>> splinex(m_basis.size()); #pragma omp parallel for schedule(static) for (size_t i = 0; i < m_basis.size(); ++i) m_basis[i]->computeBasisGrid(gpar[0],gpar[1],gpar[2],splinex[i]); const int n1 = svol->numCoefs(0); const int n2 = svol->numCoefs(1); const int p1 = svol->order(0); const int p2 = svol->order(1); const int p3 = svol->order(2); std::vector<size_t> elem_sizes; for (auto& it : m_basis) elem_sizes.push_back(it->order(0)*it->order(1)*it->order(2)); const int nel1 = n1 - p1 + 1; const int nel2 = n2 - p2 + 1; std::map<char,size_t>::const_iterator iit = firstBp.find(lIndex); size_t firstp = iit == firstBp.end() ? 0 : iit->second; // === 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) { MxFiniteElement fe(elem_sizes); fe.xi = fe.eta = fe.zeta = faceDir < 0 ? -1.0 : 1.0; fe.u = gpar[0](1,1); fe.v = gpar[1](1,1); fe.w = gpar[2](1,1); std::vector<Matrix> dNxdu(m_basis.size()); Matrix Xnod, Jac; Vec4 X; Vec3 normal; for (size_t l = 0; l < threadGrp[g][t].size() && ok; ++l) { int iel = threadGrp[g][t][l]; fe.iel = MLGE[iel]; if (fe.iel < 1) continue; // zero-volume element int i1 = p1 + iel % nel1; int i2 = p2 + (iel / nel1) % nel2; int i3 = p3 + iel / (nel1*nel2); // Get element face area in the parameter space double dA = this->getParametricArea(++iel,abs(faceDir)); if (dA < 0.0) // topology error (probably logic error) { ok = false; break; } // Set up control point coordinates for current element if (!this->getElementCoordinates(Xnod,iel)) { ok = false; break; } if (useElmVtx) this->getElementCorners(i1-1,i2-1,i3-1,fe.XC); // Initialize element quantities LocalIntegral* A = integrand.getLocalIntegral(elem_sizes,fe.iel,true); if (!integrand.initElementBou(MNPC[iel-1],elem_sizes,nb,*A)) { A->destruct(); ok = false; break; } // Define some loop control variables depending on which face we are on int nf1, j1, j2; 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*nGauss*nf1 + j1)*nGauss; int jp = (j2*nf1 + j1)*nGauss*nGauss; fe.iGP = firstp + jp; // Global integration point counter for (int j = 0; j < nGauss; j++, ip += nGauss*(nf1-1)) for (int i = 0; i < nGauss; i++, ip++, fe.iGP++) { // Local element coordinates and parameter values // of current integration point 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; } if (gpar[0].size() > 1) { fe.xi = xg[k1]; fe.u = gpar[0](k1+1,i1-p1+1); } if (gpar[1].size() > 1) { fe.eta = xg[k2]; fe.v = gpar[1](k2+1,i2-p2+1); } if (gpar[2].size() > 1) { fe.zeta = xg[k3]; fe.w = gpar[2](k3+1,i3-p3+1); } // Fetch basis function derivatives at current integration point for (size_t b = 0; b < m_basis.size(); ++b) SplineUtils::extractBasis(splinex[b][ip],fe.basis(b+1),dNxdu[b]); // Compute Jacobian inverse of the coordinate mapping and // basis function derivatives w.r.t. Cartesian coordinates fe.detJxW = utl::Jacobian(Jac,normal,fe.grad(geoBasis),Xnod,dNxdu[geoBasis-1],t1,t2); if (fe.detJxW == 0.0) continue; // skip singular points for (size_t b = 0; b < m_basis.size(); ++b) if (b != (size_t)geoBasis-1) fe.grad(b+1).multiply(dNxdu[b],Jac); if (faceDir < 0) normal *= -1.0; // Cartesian coordinates of current integration point X = Xnod * fe.basis(geoBasis); X.t = time.t; // Evaluate the integrand and accumulate element contributions fe.detJxW *= 0.25*dA*wg[i]*wg[j]; if (!integrand.evalBouMx(*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; }
bool ASMs3Dmx::integrate (Integrand& integrand, GlobalIntegral& glInt, const TimeDomain& time) { if (!svol) return true; // silently ignore empty patches if (m_basis.empty()) return false; PROFILE2("ASMs3Dmx::integrate(I)"); bool use2ndDer = integrand.getIntegrandType() & Integrand::SECOND_DERIVATIVES; bool useElmVtx = integrand.getIntegrandType() & Integrand::ELEMENT_CORNERS; // Get Gaussian quadrature points and weights const double* xg = GaussQuadrature::getCoord(nGauss); const double* wg = GaussQuadrature::getWeight(nGauss); if (!xg || !wg) return false; // Compute parameter values of the Gauss points over the whole patch std::array<Matrix,3> gpar; for (int d = 0; d < 3; d++) this->getGaussPointParameters(gpar[d],d,nGauss,xg); // Evaluate basis function derivatives at all integration points std::vector<std::vector<Go::BasisDerivs>> splinex(m_basis.size()); std::vector<std::vector<Go::BasisDerivs2>> splinex2(m_basis.size()); if (use2ndDer) { #pragma omp parallel for schedule(static) for (size_t i=0;i<m_basis.size();++i) m_basis[i]->computeBasisGrid(gpar[0],gpar[1],gpar[2],splinex2[i]); } else { #pragma omp parallel for schedule(static) for (size_t i=0;i<m_basis.size();++i) m_basis[i]->computeBasisGrid(gpar[0],gpar[1],gpar[2],splinex[i]); } std::vector<size_t> elem_sizes; for (auto& it : m_basis) elem_sizes.push_back(it->order(0)*it->order(1)*it->order(2)); const int p1 = svol->order(0); const int p2 = svol->order(1); const int p3 = svol->order(2); const int n1 = svol->numCoefs(0); const int n2 = svol->numCoefs(1); const int n3 = svol->numCoefs(2); const int nel1 = n1 - p1 + 1; const int nel2 = n2 - p2 + 1; const int nel3 = n3 - p3 + 1; // === 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) { MxFiniteElement fe(elem_sizes); std::vector<Matrix> dNxdu(m_basis.size()); std::vector<Matrix3D> d2Nxdu2(m_basis.size()); Matrix3D Hess; double dXidu[3]; Matrix Xnod, Jac; Vec4 X; for (size_t l = 0; l < threadGroupsVol[g][t].size() && ok; ++l) { int iel = threadGroupsVol[g][t][l]; fe.iel = MLGE[iel]; if (fe.iel < 1) continue; // zero-volume element int i1 = p1 + iel % nel1; int i2 = p2 + (iel / nel1) % nel3; int i3 = p3 + iel / (nel1*nel2); // Get element volume in the parameter space double dV = this->getParametricVolume(++iel); if (dV < 0.0) // topology error (probably logic error) { ok = false; break; } // Set up control point (nodal) coordinates for current element if (!this->getElementCoordinates(Xnod,iel)) { ok = false; break; } if (useElmVtx) this->getElementCorners(i1-1,i2-1,i3-1,fe.XC); if (integrand.getIntegrandType() & Integrand::G_MATRIX) { // Element size in parametric space dXidu[0] = svol->knotSpan(0,i1-1); dXidu[1] = svol->knotSpan(1,i2-1); dXidu[2] = svol->knotSpan(2,i3-1); } // Initialize element quantities LocalIntegral* A = integrand.getLocalIntegral(elem_sizes,fe.iel,false); if (!integrand.initElement(MNPC[iel-1],elem_sizes,nb,*A)) { A->destruct(); ok = false; break; } // --- Integration loop over all Gauss points in each direction -------- int ip = (((i3-p3)*nGauss*nel2 + i2-p2)*nGauss*nel1 + i1-p1)*nGauss; int jp = (((i3-p3)*nel2 + i2-p2*nel1 + i1-p1))*nGauss*nGauss*nGauss; fe.iGP = firstIp + jp; // Global integration point counter for (int k = 0; k < nGauss; k++, ip += nGauss*(nel2-1)*nGauss*nel1) for (int j = 0; j < nGauss; j++, ip += nGauss*(nel1-1)) for (int i = 0; i < nGauss; i++, ip++, fe.iGP++) { // Local element coordinates of current integration point fe.xi = xg[i]; fe.eta = xg[j]; fe.zeta = xg[k]; // Parameter values of current integration point fe.u = gpar[0](i+1,i1-p1+1); fe.v = gpar[1](j+1,i2-p2+1); fe.w = gpar[2](k+1,i3-p3+1); // Fetch basis function derivatives at current integration point if (use2ndDer) { for (size_t b = 0; b < m_basis.size(); ++b) SplineUtils::extractBasis(splinex2[b][ip],fe.basis(b+1),dNxdu[b], d2Nxdu2[b]); } else { for (size_t b = 0; b < m_basis.size(); ++b) SplineUtils::extractBasis(splinex[b][ip],fe.basis(b+1),dNxdu[b]); } // Compute Jacobian inverse of the coordinate mapping and // basis function derivatives w.r.t. Cartesian coordinates fe.detJxW = utl::Jacobian(Jac,fe.grad(geoBasis),Xnod, dNxdu[geoBasis-1]); if (fe.detJxW == 0.0) continue; // skip singular points for (size_t b = 0; b < m_basis.size(); ++b) if (b != (size_t)geoBasis-1) fe.grad(b+1).multiply(dNxdu[b],Jac); // Compute Hessian of coordinate mapping and 2nd order derivatives if (use2ndDer) { if (!utl::Hessian(Hess,fe.hess(geoBasis),Jac,Xnod, d2Nxdu2[geoBasis-1],fe.grad(geoBasis),true)) ok = false; for (size_t b = 0; b < m_basis.size() && ok; ++b) if ((int)b != geoBasis) if (!utl::Hessian(Hess,fe.hess(b+1),Jac,Xnod, d2Nxdu2[b],fe.grad(b+1),false)) ok = false; } // Compute G-matrix if (integrand.getIntegrandType() & Integrand::G_MATRIX) utl::getGmat(Jac,dXidu,fe.G); // Cartesian coordinates of current integration point X = Xnod * fe.basis(geoBasis); X.t = time.t; // Evaluate the integrand and accumulate element contributions fe.detJxW *= 0.125*dV*wg[i]*wg[j]*wg[k]; if (!integrand.evalIntMx(*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; }