From 3d0fdf358226640600b233b45c88a3ee39a433e5 Mon Sep 17 00:00:00 2001 From: Simon Praetorius <simon.praetorius@tu-dresden.de> Date: Tue, 4 Dec 2018 16:52:03 -0500 Subject: [PATCH] added some missing operators and a test for all operators --- src/amdis/Operators.hpp | 4 + .../assembler/ConvectionDiffusionOperator.hpp | 4 +- src/amdis/assembler/FirstOrderDivTestvec.hpp | 93 ++++++++++++++ src/amdis/assembler/FirstOrderGradTest.hpp | 90 ++++++++++++++ src/amdis/assembler/FirstOrderPartialTest.hpp | 95 ++++++++++++++ .../SecondOrderPartialTestPartialTrial.hpp | 4 +- test/CMakeLists.txt | 3 + test/OperatorsTest.cpp | 116 ++++++++++++++++++ 8 files changed, 405 insertions(+), 4 deletions(-) create mode 100644 src/amdis/assembler/FirstOrderDivTestvec.hpp create mode 100644 src/amdis/assembler/FirstOrderGradTest.hpp create mode 100644 src/amdis/assembler/FirstOrderPartialTest.hpp create mode 100644 test/OperatorsTest.cpp diff --git a/src/amdis/Operators.hpp b/src/amdis/Operators.hpp index 9bfde78d..5fba1925 100644 --- a/src/amdis/Operators.hpp +++ b/src/amdis/Operators.hpp @@ -46,6 +46,10 @@ #include <amdis/assembler/ZeroOrderTestvecTrialvec.hpp> // <Psi, A * Phi>, <Psi, c * Phi> // first-order operators +#include <amdis/assembler/FirstOrderPartialTest.hpp> // <d_i(psi), c> +#include <amdis/assembler/FirstOrderGradTest.hpp> // <grad(psi), b> +#include <amdis/assembler/FirstOrderDivTestvec.hpp> // <div(Psi), c> + #include <amdis/assembler/FirstOrderDivTestvecTrial.hpp> // <div(Psi), c * phi> #include <amdis/assembler/FirstOrderGradTestTrial.hpp> // <grad(psi), b * phi> #include <amdis/assembler/FirstOrderGradTestTrialvec.hpp> // <grad(psi), c * Phi> diff --git a/src/amdis/assembler/ConvectionDiffusionOperator.hpp b/src/amdis/assembler/ConvectionDiffusionOperator.hpp index ca49b35f..57d465d3 100644 --- a/src/amdis/assembler/ConvectionDiffusionOperator.hpp +++ b/src/amdis/assembler/ConvectionDiffusionOperator.hpp @@ -108,11 +108,11 @@ namespace AMDiS const auto c = localFctC(local); IF_CONSTEXPR(conserving) { - WorldVector gradAi, gradBi; + WorldVector gradAi; for (std::size_t i = 0; i < numLocalFe; ++i) { const int local_i = rowNode.localIndex(i); gradAi = A * gradients[i]; - gradBi = b * gradients[i]; + auto gradBi = b * gradients[i]; for (std::size_t j = 0; j < numLocalFe; ++j) { const int local_j = colNode.localIndex(j); elementMatrix[local_i][local_j] += (dot(gradAi, gradients[j]) + (c * shapeValues[i] - gradBi) * shapeValues[j]) * factor; diff --git a/src/amdis/assembler/FirstOrderDivTestvec.hpp b/src/amdis/assembler/FirstOrderDivTestvec.hpp new file mode 100644 index 00000000..b92ab4e5 --- /dev/null +++ b/src/amdis/assembler/FirstOrderDivTestvec.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include <type_traits> + +#include <amdis/GridFunctionOperator.hpp> +#include <amdis/LocalBasisCache.hpp> +#include <amdis/Output.hpp> +#include <amdis/common/Size.hpp> + +namespace AMDiS +{ + /** + * \addtogroup operators + * @{ + **/ + + namespace tag + { + struct divtestvec {}; + } + + + /// first-order operator \f$ \langle\nabla\cdot\Psi, c\rangle \f$ + template <class LocalContext, class GridFct> + class GridFunctionOperator<tag::divtestvec, LocalContext, GridFct> + : public GridFunctionOperatorBase<GridFunctionOperator<tag::divtestvec, LocalContext, GridFct>, + LocalContext, GridFct> + { + using Self = GridFunctionOperator; + using Super = GridFunctionOperatorBase<Self, LocalContext, GridFct>; + + static_assert( Size_v<typename GridFct::Range> == 1, "Expression must be of scalar type." ); + + public: + GridFunctionOperator(tag::divtestvec, GridFct const& expr) + : Super(expr, 1) + {} + + template <class Context, class Node, class ElementVector> + void getElementVector(Context const& context, + Node const& node, + ElementVector& elementVector) + { + static_assert(Node::isPower, "Node must be a Power-Node."); + + static const std::size_t CHILDREN = Node::CHILDREN; + static_assert( CHILDREN == Context::dow, "" ); + + auto const& quad = this->getQuadratureRule(context.type(), node); + auto const& localFE = node.child(0).finiteElement(); + std::size_t feSize = localFE.size(); + + using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType; + using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType; + + LocalBasisCache<LocalBasisType> cache(localFE.localBasis()); + + auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad); + for (std::size_t iq = 0; iq < quad.size(); ++iq) { + // Position of the current quadrature point in the reference element + decltype(auto) local = context.local(quad[iq].position()); + + // The transposed inverse Jacobian of the map from the reference element to the element + const auto jacobian = context.geometry().jacobianInverseTransposed(local); + + // The multiplicative factor in the integral transformation formula + const auto factor = Super::coefficient(local) * context.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<RangeFieldType,Context::dow>; + thread_local std::vector<WorldVector> 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 j = 0; j < feSize; ++j) { + for (std::size_t k = 0; k < CHILDREN; ++k) { + const auto local_kj = node.child(k).localIndex(j); + elementVector[local_kj] += factor * gradients[j][k]; + } + } + } + + } + }; + + /** @} **/ + +} // end namespace AMDiS diff --git a/src/amdis/assembler/FirstOrderGradTest.hpp b/src/amdis/assembler/FirstOrderGradTest.hpp new file mode 100644 index 00000000..cfb1e7ad --- /dev/null +++ b/src/amdis/assembler/FirstOrderGradTest.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include <type_traits> + +#include <amdis/GridFunctionOperator.hpp> +#include <amdis/LocalBasisCache.hpp> +#include <amdis/Output.hpp> +#include <amdis/common/Size.hpp> + +namespace AMDiS +{ + /** + * \addtogroup operators + * @{ + **/ + + namespace tag + { + struct gradtest {}; + } + + + /// first-order operator \f$ \langle\Psi, c\,\nabla\phi\rangle \f$ + template <class LocalContext, class GridFct> + class GridFunctionOperator<tag::gradtest, LocalContext, GridFct> + : public GridFunctionOperatorBase<GridFunctionOperator<tag::gradtest, LocalContext, GridFct>, + LocalContext, GridFct> + { + static const int dow = ContextGeometry<LocalContext>::dow; + using Self = GridFunctionOperator; + using Super = GridFunctionOperatorBase<Self, LocalContext, GridFct>; + + static_assert( Size_v<typename GridFct::Range> == dow, "Expression must be of vector type." ); + + public: + GridFunctionOperator(tag::gradtest, GridFct const& expr) + : Super(expr, 1) + {} + + template <class Context, class Node, class ElementVector> + void getElementVector(Context const& context, + Node const& node, + ElementVector& elementVector) + { + static_assert(Node::isLeaf, "Node must be Leaf-Node."); + + auto const& quad = this->getQuadratureRule(context.type(), node); + auto const& localFE = node.finiteElement(); + std::size_t feSize = localFE.size(); + + using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType; + using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType; + + LocalBasisCache<LocalBasisType> cache(localFE.localBasis()); + + auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad); + for (std::size_t iq = 0; iq < quad.size(); ++iq) { + // Position of the current quadrature point in the reference element + decltype(auto) local = context.local(quad[iq].position()); + + // The transposed inverse Jacobian of the map from the reference element to the element + const auto jacobian = context.geometry().jacobianInverseTransposed(local); + + // The multiplicative factor in the integral transformation formula + const auto factor = Super::coefficient(local); + const auto dx = context.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<RangeFieldType,Context::dow>; + thread_local std::vector<WorldVector> 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 i = 0; i < feSize; ++i) { + const auto local_i = node.localIndex(i); + elementVector[local_i] += dx * (factor * gradients[i]); + } + } + + } + }; + + /** @} **/ + +} // end namespace AMDiS diff --git a/src/amdis/assembler/FirstOrderPartialTest.hpp b/src/amdis/assembler/FirstOrderPartialTest.hpp new file mode 100644 index 00000000..5d7e02bf --- /dev/null +++ b/src/amdis/assembler/FirstOrderPartialTest.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include <type_traits> + +#include <amdis/assembler/FirstOrderTestPartialTrial.hpp> + +namespace AMDiS +{ + /** + * \addtogroup operators + * @{ + **/ + + namespace tag + { + struct partialtest + { + std::size_t comp; + }; + } + + + /// first-order operator \f$ \langle\partial_i\psi, c\rangle \f$ + + template <class LocalContext, class GridFct> + class GridFunctionOperator<tag::partialtest, LocalContext, GridFct> + : public GridFunctionOperatorBase<GridFunctionOperator<tag::partialtest, LocalContext, GridFct>, + LocalContext, GridFct> + { + using Self = GridFunctionOperator; + using Super = GridFunctionOperatorBase<Self, LocalContext, GridFct>; + + static_assert( Size_v<typename GridFct::Range> == 1, "Expression must be of scalar type." ); + + public: + GridFunctionOperator(tag::partialtest tag, GridFct const& expr) + : Super(expr, 1) + , comp_(tag.comp) + {} + + template <class Context, class Node, class ElementVector> + void getElementVector(Context const& context, + Node const& node, + ElementVector& elementVector) + { + static_assert(Node::isLeaf, "Operator can be applied to Leaf-Nodes only."); + + auto const& quad = this->getQuadratureRule(context.type(), node); + auto const& localFE = node.finiteElement(); + std::size_t feSize = localFE.size(); + + using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType; + using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType; + + LocalBasisCache<LocalBasisType> cache(localFE.localBasis()); + + auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad); + for (std::size_t iq = 0; iq < quad.size(); ++iq) { + // Position of the current quadrature point in the reference element + decltype(auto) local = context.local(quad[iq].position()); + + // The transposed inverse Jacobian of the map from the reference element to the element + const auto jacobian = context.geometry().jacobianInverseTransposed(local); + assert(jacobian.M() == Context::dim); + + // The multiplicative factor in the integral transformation formula + const auto dx = context.integrationElement(quad[iq].position()) * quad[iq].weight(); + const auto c = Super::coefficient(local); + + // The gradients of the shape functions on the reference element + auto const& shapeGradients = shapeGradientsCache[iq]; + + // Compute the shape function gradients on the real element + thread_local std::vector<RangeFieldType> partial; + partial.resize(shapeGradients.size()); + for (std::size_t i = 0; i < partial.size(); ++i) { + partial[i] = jacobian[comp_][0] * shapeGradients[i][0][0]; + for (std::size_t j = 1; j < jacobian.M(); ++j) + partial[i] += jacobian[comp_][j] * shapeGradients[i][0][j]; + } + + for (std::size_t j = 0; j < feSize; ++j) { + const auto local_j = node.localIndex(j); + elementVector[local_j] += dx * (c * partial[j]); + } + } + } + + private: + std::size_t comp_; + }; + + /** @} **/ + +} // end namespace AMDiS diff --git a/src/amdis/assembler/SecondOrderPartialTestPartialTrial.hpp b/src/amdis/assembler/SecondOrderPartialTestPartialTrial.hpp index 434a9b53..bb558164 100644 --- a/src/amdis/assembler/SecondOrderPartialTestPartialTrial.hpp +++ b/src/amdis/assembler/SecondOrderPartialTestPartialTrial.hpp @@ -88,7 +88,7 @@ namespace AMDiS rowPartial.resize(rowShapeGradients.size()); for (std::size_t i = 0; i < rowPartial.size(); ++i) { rowPartial[i] = jacobian[compTest_][0] * rowShapeGradients[i][0][0]; - for (std::size_t j = 1; j < jacobian.cols(); ++j) + for (std::size_t j = 1; j < jacobian.cols; ++j) rowPartial[i] += jacobian[compTest_][j] * rowShapeGradients[i][0][j]; } @@ -96,7 +96,7 @@ namespace AMDiS colPartial.resize(colShapeGradients.size()); for (std::size_t i = 0; i < colPartial.size(); ++i) { colPartial[i] = jacobian[compTrial_][0] * colShapeGradients[i][0][0]; - for (std::size_t j = 1; j < jacobian.cols(); ++j) + for (std::size_t j = 1; j < jacobian.cols; ++j) colPartial[i] += jacobian[compTrial_][j] * colShapeGradients[i][0][j]; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 57754d5d..f7819bc7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -40,6 +40,9 @@ dune_add_test(SOURCES MultiTypeVectorTest.cpp dune_add_test(SOURCES MultiTypeMatrixTest.cpp LINK_LIBRARIES amdis) +dune_add_test(SOURCES OperatorsTest.cpp + LINK_LIBRARIES amdis) + dune_add_test(SOURCES RangeTypeTest.cpp LINK_LIBRARIES amdis) diff --git a/test/OperatorsTest.cpp b/test/OperatorsTest.cpp new file mode 100644 index 00000000..259eaddd --- /dev/null +++ b/test/OperatorsTest.cpp @@ -0,0 +1,116 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include "config.h" +#include <amdis/AMDiS.hpp> +#include <amdis/ProblemStat.hpp> +#include <amdis/Operators.hpp> +#include <amdis/assembler/ConvectionDiffusionOperator.hpp> +#include <amdis/assembler/StokesOperator.hpp> + + +using namespace AMDiS; + + +using Grid = Dune::YaspGrid<2>; +using Param = TaylorHoodBasis<typename Grid::LeafGridView>; +using Problem = ProblemStat<Param>; + +int main(int argc, char** argv) +{ + AMDiS::init(argc, argv); + + using namespace Dune::Indices; + + Problem prob("ellipt"); + prob.initialize(INIT_ALL); + + auto _v = _0; + auto _p = _1; + + FieldVector<double,1> scalar = 1.0; + FieldVector<double,2> vec; vec = 1.0; + FieldMatrix<double,2,2> mat; mat = 1.0; + + auto op1 = makeOperator(tag::divtestvec{}, scalar); + prob.addVectorOperator(op1, _v); + + auto op2 = makeOperator(tag::divtestvec_trial{}, scalar); + prob.addMatrixOperator(op2, _v, _p); + + auto op3 = makeOperator(tag::gradtest{}, vec); + prob.addVectorOperator(op3, _p); + + auto op4 = makeOperator(tag::gradtest_trial{}, vec); + prob.addMatrixOperator(op4, _p, _p); + + auto op5a = makeOperator(tag::gradtest_trialvec{}, scalar); + // auto op5b = makeOperator(tag::gradtest_trialvec{}, mat); // TODO: needs to be implemented + prob.addMatrixOperator(op5a, _p, _v); + + auto op6 = makeOperator(tag::partialtest{0}, scalar); + prob.addVectorOperator(op6, _p); + + auto op7 = makeOperator(tag::partialtest_trial{0}, scalar); + prob.addMatrixOperator(op7, _p, _p); + + auto op8 = makeOperator(tag::test_divtrialvec{}, scalar); + prob.addMatrixOperator(op8, _p, _v); + + auto op9 = makeOperator(tag::test_gradtrial{}, vec); + prob.addMatrixOperator(op9, _p, _p); + + auto op10 = makeOperator(tag::test_partialtrial{0}, scalar); + prob.addMatrixOperator(op10, _p, _p); + + auto op11a = makeOperator(tag::testvec_gradtrial{}, scalar); + // auto op11b = makeOperator(tag::testvec_gradtrial{}, mat); // TODO: needs to be implemented + prob.addMatrixOperator(op11a, _v, _p); + + auto op12 = makeOperator(tag::divtestvec_divtrialvec{}, scalar); + prob.addMatrixOperator(op12, _v, _v); + + auto op13a = makeOperator(tag::gradtest_gradtrial{}, scalar); + auto op13b = makeOperator(tag::gradtest_gradtrial{}, mat); + prob.addMatrixOperator(op13a, _p, _p); + prob.addMatrixOperator(op13b, _p, _p); + + auto op14 = makeOperator(tag::partialtest_partialtrial{0,0}, scalar); + prob.addMatrixOperator(op14, _p, _p); + + auto op15 = makeOperator(tag::test{}, scalar); + prob.addVectorOperator(op15, _p); + + auto op16 = makeOperator(tag::test_trial{}, scalar); + prob.addMatrixOperator(op16, _p, _p); + + auto op17 = makeOperator(tag::test_trialvec{}, vec); + prob.addMatrixOperator(op17, _p, _v); + + auto op18 = makeOperator(tag::testvec{}, vec); + prob.addVectorOperator(op18, _v); + + auto op19 = makeOperator(tag::testvec_trial{}, vec); + prob.addMatrixOperator(op19, _v, _p); + + auto op20a = makeOperator(tag::testvec_trialvec{}, scalar); + auto op20b = makeOperator(tag::testvec_trialvec{}, mat); + prob.addMatrixOperator(op20a, _v, _v); + prob.addMatrixOperator(op20b, _v, _v); + + // some special operators + + auto opStokes = makeOperator(tag::stokes{}, scalar); + prob.addMatrixOperator(opStokes); + prob.addMatrixOperator(opStokes, {}, {}); + + auto opCDa = convectionDiffusion(scalar, scalar, scalar, scalar); + prob.addMatrixOperator(opCDa, _p, _p); + prob.addVectorOperator(opCDa, _p); + + auto opCDb = convectionDiffusion(mat, vec, scalar, scalar, std::false_type{}); + prob.addMatrixOperator(opCDb, _p, _p); + prob.addVectorOperator(opCDb, _p); + + AMDiS::finalize(); + return 0; +} -- GitLab