#pragma once #include #include #include #include #include namespace AMDiS { /** * \addtogroup operators * @{ **/ namespace tag { struct divtestvec_divtrialvec {}; } /// second-order operator \f$ \langle\nabla\cdot\Psi, c\,\nabla\cdot\Phi\rangle \f$ template class GridFunctionOperator : public GridFunctionOperatorBase, LC, GridFct> { using Self = GridFunctionOperator; using Super = GridFunctionOperatorBase; static_assert( static_size_v == 1, "Expression must be of scalar type." ); public: GridFunctionOperator(tag::divtestvec_divtrialvec, GridFct const& expr) : Super(expr, 2) {} template void getElementMatrix(CG const& contextGeo, RN const& rowNode, CN const& colNode, Mat& elementMatrix) { static_assert(RN::isPower && CN::isPower, "Operator can be applied to Power-Nodes only."); const bool sameFE = std::is_same_v, FiniteElementType_t>; const bool sameNode = rowNode.treeIndex() == colNode.treeIndex(); auto const& quad = this->getQuadratureRule(contextGeo.type(), rowNode, colNode); if (sameFE && sameNode) getElementMatrixOptimized(contextGeo, quad, rowNode, colNode, elementMatrix); else getElementMatrixStandard(contextGeo, quad, rowNode, colNode, elementMatrix); } protected: template void getElementMatrixStandard(CG const& contextGeo, QR const& quad, RN const& rowNode, CN const& colNode, Mat& elementMatrix) { static const std::size_t CHILDREN = RN::CHILDREN; static_assert( RN::CHILDREN == CN::CHILDREN, "" ); auto const& rowLocalFE = rowNode.child(0).finiteElement(); auto const& colLocalFE = colNode.child(0).finiteElement(); std::size_t rowSize = rowLocalFE.size(); std::size_t colSize = colLocalFE.size(); using RowFieldType = typename NodeQuadCache::Traits::RangeFieldType; using ColFieldType = typename NodeQuadCache::Traits::RangeFieldType; NodeQuadCache rowCache(rowLocalFE.localBasis()); NodeQuadCache colCache(colLocalFE.localBasis()); auto const& rowGradientsCache = rowCache.evaluateJacobianAtQP(contextGeo, quad); auto const& colGradientsCache = colCache.evaluateJacobianAtQP(contextGeo, quad); for (std::size_t iq = 0; iq < quad.size(); ++iq) { // Position of the current quadrature point in the reference element decltype(auto) local = contextGeo.local(quad[iq].position()); // The transposed inverse Jacobian of the map from the reference element to the element const auto jacobian = contextGeo.geometry().jacobianInverseTransposed(local); // The multiplicative factor in the integral transformation formula const auto factor = Super::coefficient(local) * contextGeo.integrationElement(quad[iq].position()) * quad[iq].weight(); // The gradients of the shape functions on the reference element auto const& rowShapeGradients = rowGradientsCache[iq]; auto const& colShapeGradients = colGradientsCache[iq]; // Compute the shape function gradients on the real element using RowWorldVector = FieldVector; thread_local std::vector rowGradients; rowGradients.resize(rowShapeGradients.size()); for (std::size_t i = 0; i < rowGradients.size(); ++i) jacobian.mv(rowShapeGradients[i][0], rowGradients[i]); using ColWorldVector = FieldVector; thread_local std::vector colGradients; colGradients.resize(colShapeGradients.size()); for (std::size_t i = 0; i < colGradients.size(); ++i) jacobian.mv(colShapeGradients[i][0], colGradients[i]); for (std::size_t i = 0; i < rowSize; ++i) { for (std::size_t j = 0; j < colSize; ++j) { for (std::size_t k = 0; k < CHILDREN; ++k) { const auto local_ki = rowNode.child(k).localIndex(i); const auto local_kj = colNode.child(k).localIndex(j); elementMatrix[local_ki][local_kj] += factor * rowGradients[i][k] * colGradients[j][k]; } } } } } template void getElementMatrixOptimized(CG const& contextGeo, QR const& quad, Node const& node, Node const& /*colNode*/, Mat& elementMatrix) { static const std::size_t CHILDREN = Node::CHILDREN; auto const& localFE = node.child(0).finiteElement(); std::size_t size = localFE.size(); using LeafNode = typename Node::ChildType; using RangeFieldType = typename NodeQuadCache::Traits::RangeFieldType; NodeQuadCache cache(localFE.localBasis()); auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(contextGeo, quad); for (std::size_t iq = 0; iq < quad.size(); ++iq) { // Position of the current quadrature point in the reference element decltype(auto) local = contextGeo.local(quad[iq].position()); // The transposed inverse Jacobian of the map from the reference element to the element const auto jacobian = contextGeo.geometry().jacobianInverseTransposed(local); // The multiplicative factor in the integral transformation formula const auto factor = Super::coefficient(local) * contextGeo.integrationElement(quad[iq].position()) * quad[iq].weight(); // The gradients of the shape functions on the reference element auto const& shapeGradients = shapeGradientsCache[iq]; // Compute the shape function gradients on the real element using WorldVector = FieldVector; thread_local std::vector gradients; gradients.resize(shapeGradients.size()); for (std::size_t i = 0; i < gradients.size(); ++i) jacobian.mv(shapeGradients[i][0], gradients[i]); for (std::size_t k = 0; k < CHILDREN; ++k) { for (std::size_t i = 0; i < size; ++i) { const auto local_ki = node.child(k).localIndex(i); const auto value = factor * gradients[i][k]; elementMatrix[local_ki][local_ki] += value * gradients[i][k]; for (std::size_t j = i+1; j < size; ++j) { const auto local_kj = node.child(k).localIndex(j); elementMatrix[local_ki][local_kj] += value * gradients[j][k]; elementMatrix[local_kj][local_ki] += value * gradients[j][k]; } } } } } }; /** @} **/ } // end namespace AMDiS