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 ASMs2DmxLag::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 const double* xg = GaussQuadrature::getCoord(nGauss); const double* wg = GaussQuadrature::getWeight(nGauss); 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 // Extract the Neumann order flag (1 or higher) for the integrand integrand.setNeumannOrder(1 + lIndex/10); // Number of elements in each direction const int nelx = (nxx[geoBasis-1]-1)/(elem_sizes[geoBasis-1][0]-1); const int nely = (nyx[geoBasis-1]-1)/(elem_sizes[geoBasis-1][1]-1); std::map<char,size_t>::const_iterator iit = firstBp.find(lIndex%10); size_t firstp = iit == firstBp.end() ? 0 : iit->second; MxFiniteElement fe(elem_size); Matrices dNxdu(nxx.size()); Matrix 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 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(elem_size,fe.iel,true); bool ok = integrand.initElementBou(MNPC[iel-1],elem_size,nb,*A); // --- Integration loop over all Gauss points along the edge ------------- int jp = (t1 == 1 ? i2 : i1)*nGauss; fe.iGP = firstp + jp; // Global integration point counter for (int i = 0; i < nGauss && ok; i++, fe.iGP++) { // Gauss point coordinates along the edge xi[t1-1] = edgeDir < 0 ? -1.0 : 1.0; xi[t2-1] = xg[i]; // Compute the basis functions and their derivatives, using // tensor product of one-dimensional Lagrange polynomials for (size_t b = 0; b < nxx.size(); ++b) if (!Lagrange::computeBasis(fe.basis(b+1),dNxdu[b],elem_sizes[b][0],xi[0], elem_sizes[b][1],xi[1])) ok = false; // Compute basis function derivatives and the edge normal 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 < nxx.size(); ++b) if (b != (size_t)geoBasis-1) fe.grad(b+1).multiply(dNxdu[b],Jac); if (edgeDir < 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 *= wg[i]; if (ok && !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(); if (!ok) return false; } return true; }