From 3a6b1fea825a1c41dfd3d87fa4efef24a3d43184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carsten=20Gr=C3=A4ser?= <graeser@mi.fu-berlin.de> Date: Sun, 19 Mar 2023 18:31:58 +0100 Subject: [PATCH] Cleanup and improve * Split header * Detangle dependencies * Use better names in user interface * `FormOperator` -> `RangeOperator` * `transformedForm(op, form)` -> `transformRange(form, op)` * Remove `makeSumForm()`, using plain `SumForm(...)` does the trick due to CTAD * Add operator `D` for computing partial derivatives as `D(u, i)` * Further cleaup of form interface. Less magic, more explicit. * Add support for passing more arguemnts to `RangeOperator` --- doc/CMakeLists.txt | 1 + dune/fufem-forms/CMakeLists.txt | 11 +- dune/fufem-forms/baseclass.hh | 105 +++ dune/fufem-forms/forms.hh | 1347 +-------------------------- dune/fufem-forms/fufem-forms.hh | 6 - dune/fufem-forms/nullaryforms.hh | 74 ++ dune/fufem-forms/operators.hh | 213 +++++ dune/fufem-forms/productform.hh | 248 +++++ dune/fufem-forms/sumform.hh | 156 ++++ dune/fufem-forms/transformedform.hh | 158 ++++ dune/fufem-forms/unaryforms.hh | 400 ++++++++ src/fracture.cc | 2 +- 12 files changed, 1398 insertions(+), 1323 deletions(-) create mode 100644 dune/fufem-forms/baseclass.hh delete mode 100644 dune/fufem-forms/fufem-forms.hh create mode 100644 dune/fufem-forms/nullaryforms.hh create mode 100644 dune/fufem-forms/operators.hh create mode 100644 dune/fufem-forms/productform.hh create mode 100644 dune/fufem-forms/sumform.hh create mode 100644 dune/fufem-forms/transformedform.hh create mode 100644 dune/fufem-forms/unaryforms.hh diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index be52cfc..4828ea3 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory("doxygen") +add_subdirectory("manual") diff --git a/dune/fufem-forms/CMakeLists.txt b/dune/fufem-forms/CMakeLists.txt index bc3da27..343ec23 100644 --- a/dune/fufem-forms/CMakeLists.txt +++ b/dune/fufem-forms/CMakeLists.txt @@ -1,2 +1,11 @@ #install headers -install(FILES fufem-forms.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/fufem-forms) +install(FILES + baseclass.hh + forms.hh + nullaryforms.hh + operators.hh + productform.hh + sumform.hh + transformedform.hh + unaryforms.hh + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/fufem-forms) diff --git a/dune/fufem-forms/baseclass.hh b/dune/fufem-forms/baseclass.hh new file mode 100644 index 0000000..c51df77 --- /dev/null +++ b/dune/fufem-forms/baseclass.hh @@ -0,0 +1,105 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FUFEM_FORMS_BASECLASS_HH +#define DUNE_FUFEM_FORMS_BASECLASS_HH + +#include <cstddef> +#include <type_traits> +#include <tuple> +#include <utility> + +#include <dune/common/typetraits.hh> +#include <dune/common/indices.hh> + +#include <dune/typetree/childextraction.hh> +#include <dune/typetree/treepath.hh> + + + +namespace Dune::Fufem::Forms::Impl { + + template<class Tuple, class F> + void forEachTupleEntry(Tuple&& tuple, F&& f){ + std::apply([&](auto&&... entry){ + (f(std::forward<decltype(entry)>(entry)), ...); + }, tuple); + } + + // Helper function to derive the leafTreePath associated + // to a treePath. For treePaths refering to a leaf this is + // the identity. For treePaths referring to the a power + // node whose children are leaf, it's the first child. + // This is used to implement vector valued ansatz functions + // for power nodes. + template<class Tree, class TreePath> + static auto leafTreePath(const TreePath& tp) + { + using Node = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; + if constexpr(Node::isLeaf) + return tp; + else if constexpr(Node::isPower and Node::ChildType::isLeaf) + return Dune::TypeTree::push_back(tp, Dune::Indices::_0); + } + + template<class Tree, class TreePath> + using LeafTreePath = decltype(leafTreePath<Tree>(std::declval<TreePath>())); + + template<class F> + using LazyFormRange = decltype(std::declval<F>()(std::declval<typename F::Coordinate>(), 0)); + + // Helper template to determine the range after bind, evaluation, and index access. + template<class F> + using FormRange = decltype(std::declval<LazyFormRange<F>>()(std::tuple<int,int>{})); + +} // namespace Impl + + + +namespace Dune::Fufem::Forms { + + + + // Base class of form implementations. The term _form_ follows the + // terminalogy in UFL, altough it does stricly speaking not refer + // to a multilinear form here. Instead all forms are multilinear + // maps from finite element spaces into the space of functions + // from the domain into some finite dimensional vector spaces. + // Poinwise products (scalar-vector, matrix-vector, dot,...) of + // multilinear maps induces multilinear maps of higher order. + // The product of a k-linear map and an l-linear is a (k+l) linear + // map into a suitable function space. + // + // Example: + // Denote by {A->B} the set of all functions f:B->A. Now let + // D a domain in R^d, V \subset {D->R} and W \subset {D->R} + // two FE-spaces, and f \in {D->R^(d,d)} a fixed matrix valued + // function. + // + // Then f is a 0-linear map into {D->R^(d,d)}. + // The gradient operator \nabla:V -> {D->R^d} is a 1-linear map into {D->R^d}. + // The gradient operator \nabla:W -> {D->R^d} is a 1-linear map into {D->R^d}. + // The pointwise matrix-vector product f\nabla:V -> {D->R^d} is a 1-linear map into {D->R^d}. + // The pointwise dot product dot(f\nabla(.), \nabla(.)):V\times W -> {D->R} + // is a 2-linear map into {D->R}. + // + // Integrating a k-linear map F:V_1 \times ... \times V_k -> {D->R} over D + // results in a k-linear form \int_D F(...)dx : V_1 \times ... \times V_k -> D + class Form {}; + + + + // Check if a class is a form + + template<class F> + using isForm = std::is_base_of<Form, std::decay_t<F>>; + + template<class F> + inline constexpr bool isForm_v = isForm<F>::value; + + + +} // namespace Dune::Fufem::Forms + + + +#endif // DUNE_FUFEM_FORMS_BASECLASS_HH diff --git a/dune/fufem-forms/forms.hh b/dune/fufem-forms/forms.hh index 5cb108b..d00c496 100644 --- a/dune/fufem-forms/forms.hh +++ b/dune/fufem-forms/forms.hh @@ -3,1317 +3,36 @@ #ifndef DUNE_FUFEM_ASSEMBLERS_FORMS_HH #define DUNE_FUFEM_ASSEMBLERS_FORMS_HH +#include <cstddef> #include <type_traits> #include <tuple> #include <utility> -#include <dune/common/typetraits.hh> - -#include <dune/istl/matrix.hh> -#include <dune/istl/matrixindexset.hh> - -#include <dune/functions/functionspacebases/subspacebasis.hh> +#include <dune/typetree/childextraction.hh> #include <dune/fufem/quadraturerules/quadraturerulecache.hh> #include <dune/fufem/boundarypatch.hh> #include <dune/fufem-forms/shapefunctioncache.hh> +#include <dune/fufem-forms/baseclass.hh> +#include <dune/fufem-forms/nullaryforms.hh> +#include <dune/fufem-forms/transformedform.hh> +#include <dune/fufem-forms/unaryforms.hh> +#include <dune/fufem-forms/sumform.hh> +#include <dune/fufem-forms/productform.hh> +#include <dune/fufem-forms/operators.hh> -namespace Dune::Fufem::Forms { - - -namespace Impl { - - template<class Tuple, class F, std::size_t... i> - void forEachTupleEntryHelper(Tuple&& tuple, F&& f, std::index_sequence<i...>){ - (f(std::get<i>(tuple)),...); - } - - template<class Tuple, class F> - void forEachTupleEntry(Tuple&& tuple, F&& f){ - forEachTupleEntryHelper(tuple, f, std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{}); - } - - struct TransposeOp { - template<class K, int n, int m> - auto operator()(const Dune::FieldMatrix<K,n,m>& x) const - { - Dune::FieldMatrix<K,m,n> y; - for(std::size_t i=0; i<x.N(); ++i) - for(std::size_t j=0; j<x.M(); ++j) - y[j][i] = x[i][j]; - return y; - } - }; - - struct DotOp { - - template<class K1, class K2, int n> - auto operator()(const Dune::FieldVector<K1,n>& x, const Dune::FieldVector<K2,n>& y) const - { - using K = typename PromotionTraits<K1,K2>::PromotedType; - auto result = K(0); - for(std::size_t i=0; i<n; ++i) - result += x[i]*y[i]; - return result; - } - - template<class K1, class K2, int n, int m> - auto operator()(const Dune::FieldMatrix<K1,n,m>& x, const Dune::FieldMatrix<K2,n,m>& y) const - { - using K = typename PromotionTraits<K1,K2>::PromotedType; - auto result = K(0); - for(std::size_t i=0; i<n; ++i) - for(std::size_t j=0; j<m; ++j) - result += x[i][j]*y[i][j]; - return result; - } - - template<class K1, class K2, int n> - auto operator()(const Dune::FieldMatrix<K1,n,1>& x, const Dune::FieldVector<K2,n>& y) const - { - using K = typename PromotionTraits<K1,K2>::PromotedType; - auto result = K(0); - for(std::size_t i=0; i<n; ++i) - result += x[i][0]*y[i]; - return result; - } - - template<class K1, class K2, int n> - auto operator()(const Dune::FieldVector<K1,n>& x, const Dune::FieldMatrix<K2,n,1>& y) const - { - using K = typename PromotionTraits<K1,K2>::PromotedType; - auto result = K(0); - for(std::size_t i=0; i<n; ++i) - result += x[i]*y[i][0]; - return result; - } - - template<class K1, class K2, int n> - auto operator()(const Dune::ScaledIdentityMatrix<K1,n>& x, const Dune::FieldMatrix<K2,n,n>& y) const - { - using K = typename PromotionTraits<K1,K2>::PromotedType; - auto result = K(0); - for(std::size_t i=0; i<x.N(); ++i) - result += y[i][i]; - return x.scalar()*result; - } - - template<class K1, class K2, int n> - auto operator()(const Dune::FieldMatrix<K1,n,n>& x, const Dune::ScaledIdentityMatrix<K2,n>& y) const - { - using K = typename PromotionTraits<K1,K2>::PromotedType; - auto result = K(0); - for(std::size_t i=0; i<x.N(); ++i) - result += x[i][i]; - return result*y.scalar(); - } - - template<class K1, class K2, int n> - auto operator()(const Dune::ScaledIdentityMatrix<K1,n>& x, const Dune::ScaledIdentityMatrix<K2,n>& y) const - { - return n*x.scalar()*y.scalar(); - } - - }; - - struct MultOp { - - template<class X, class Y, - class = std::void_t<decltype(std::declval<X>()*std::declval<Y>())>> - auto operator()(const X& x, const Y& y) const - { - return x*y; - } - - template<class K1, class K2, int n, int m> - auto operator()(const Dune::FieldMatrix<K1,n,m>& x, const Dune::FieldVector<K2,m>& y) const - { - using K = typename PromotionTraits<K1,K2>::PromotedType; - Dune::FieldVector<K,n> result; - x.mv(y, result); - return result; - } - - template<class K, class Y> - auto operator()(const Dune::FieldMatrix<K,1,1>& x, const Y& y) const - { - return x[0][0]*y; - } - - template<class K, class Y> - auto operator()(const Dune::FieldVector<K,1>& x, const Y& y) const - { - return x[0]*y; - } - - }; - - - // Helper function to derive the leafTreePath associated - // to a treePath. For treePaths refering to a leaf this is - // the identity. For treePaths referring to the a power - // node whose children are leaf, it's the first child. - // This is used to implement vector valued ansatz functions - // for power nodes. - template<class Tree, class TreePath> - static auto leafTreePath(const TreePath& tp) - { - using Node = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; - if constexpr(Node::isLeaf) - return tp; - else if constexpr(Node::isPower and Node::ChildType::isLeaf) - return Dune::TypeTree::push_back(tp, Dune::Indices::_0); - } - - template<class Tree, class TreePath> - using LeafTreePath = decltype(leafTreePath<Tree>(std::declval<TreePath>())); - - template<class F> - using LazyFormRange = decltype(std::declval<F>()(std::declval<typename F::Coordinate>(), 0)); - - // Helper template to determine the range after bind, evaluation, and index access. - template<class F> - using FormRange = decltype(std::declval<LazyFormRange<F>>()(std::tuple<int,int>{})); - -} // namespace Impl - - - - // Base class of form implementations. The term _form_ follows the - // terminalogy in UFL, altough it does stricly speaking not refer - // to a multilinear form here. Instead all forms are multilinear - // maps from finite element spaces into the space of functions - // from the domain into some finite dimensional vector spaces. - // Poinwise products (scalar-vector, matrix-vector, dot,...) of - // multilinear maps induces multilinear maps of higher order. - // The product of a k-linear map and an l-linear is a (k+l) linear - // map into a suitable function space. - // - // Example: - // Denote by {A->B} the set of all functions f:B->A. Now let - // D a domain in R^d, V \subset {D->R} and W \subset {D->R} - // two FE-spaces, and f \in {D->R^(d,d)} a fixed matrix valued - // function. - // - // Then f is a 0-linear map into {D->R^(d,d)}. - // The gradient operator \nabla:V -> {D->R^d} is a 1-linear map into {D->R^d}. - // The gradient operator \nabla:W -> {D->R^d} is a 1-linear map into {D->R^d}. - // The pointwise matrix-vector product f\nabla:V -> {D->R^d} is a 1-linear map into {D->R^d}. - // The pointwise dot product dot(f\nabla(.), \nabla(.)):V\times W -> {D->R} - // is a 2-linear map into {D->R}. - // - // Integrating a k-linear map F:V_1 \times ... \times V_k -> {D->R} over D - // results in a k-linear form \int_D F(...)dx : V_1 \times ... \times V_k -> D - class Form { - public: - - template<class Element> - void bindToElement(const Element&) {} - - template<class LocalView> - void bindToTestLocalView(const LocalView&) {} - - template<class LocalView> - void bindToTrialLocalView(const LocalView&) {} - - template<class Cache> - void bindToTestCache(const Cache&) {} - - template<class Cache> - void bindToTrialCache(const Cache&) {} - - void testBasis() const {} - - void trialBasis() const {} - - void testTreePath() const {} - - void trialTreePath() const {} - }; - - - - // Check if a class is a form - - template<class F> - using isForm = std::is_base_of<Form, std::decay_t<F>>; - - template<class F> - inline constexpr bool isForm_v = isForm<F>::value; - - - template<class Impl, std::size_t argIndex> - struct UnaryForm; - - template<class Impl> - struct UnaryForm<Impl, 0> : public Form - { - static constexpr std::size_t arity = 1; - - template<class LocalView> - void bindToTestLocalView(const LocalView& localView) - { - static_cast<Impl*>(this)->bindToLocalView(localView); - } - - template<class Cache> - void bindToTestCache(Cache& cache) - { - static_cast<Impl*>(this)->bindToCache(cache); - } - - const auto& testBasis() const - { - return static_cast<const Impl*>(this)->basis(); - } - - const auto& testTreePath() const - { - return static_cast<const Impl*>(this)->treePath(); - } - - }; - - template<class Impl> - struct UnaryForm<Impl, 1> : public Form - { - static constexpr std::size_t arity = 1; - - template<class LocalView> - void bindToTrialLocalView(const LocalView& localView) - { - static_cast<Impl*>(this)->bindToLocalView(localView); - } - - template<class Cache> - void bindToTrialCache(Cache& cache) - { - static_cast<Impl*>(this)->bindToCache(cache); - } - - const auto& trialBasis() const - { - return static_cast<const Impl*>(this)->basis(); - } - - const auto& trialTreePath() const - { - return static_cast<const Impl*>(this)->treePath(); - } - - }; - - - - - // Forward declarations - - template<class F, - std::enable_if_t<isForm_v<F>, int> = 0> - auto transpose(const F& f); - - template<class B, class TP, std::size_t argIndex> - class FEFunctionJacobianForm; - - template<class B, class TP, std::size_t argIndex> - class FEFunctionDivergenceForm; - - template<class B, class TP, std::size_t argIndex> - class FEFunctionForm; - - - - - - // Zero-linear map induced by fixed coefficient function - template<class E, class C> - class ConstCoefficient : public Form - { - public: - static constexpr std::size_t arity = 0; - - using Element = E; - using Coordinate = typename Element::Geometry::LocalCoordinate; - - ConstCoefficient(const C& c) : - c_(c) - {} - - auto quadratureRuleKey() const - { - return QuadratureRuleKey(Element::dimension, 0); - } - - auto operator()(const Coordinate& x, std::size_t quadPointIndex) const - { - return [=](const auto& args) { - return c_; - }; - } - - private: - C c_; - }; - - - - // Zero-linear map induced by fixed coefficient function - template<class F> - class Coefficient : public Form - { - using LocalFunction = std::decay_t<decltype(localFunction(std::declval<F&>()))>; - - public: - static constexpr std::size_t arity = 0; - - using Element = typename F::EntitySet::Element; - using Coordinate = typename Element::Geometry::LocalCoordinate; - - Coefficient(const F& f) : - f_(f), - localF_(localFunction(f)) - {} - - Coefficient(const F& f, std::size_t order) : - f_(f), - localF_(localFunction(f)) - {} - - auto quadratureRuleKey() const - { - return QuadratureRuleKey(Element::dimension, 0); - } - - void bindToElement(const Element& element) - { - localF_.bind(element); - } - - auto operator()(const Coordinate& x, std::size_t quadPointIndex) const - { - auto value = localF_(x); - return [value](const auto& args) { - return value; - }; - } - - private: - F f_; - LocalFunction localF_; - }; - - - - // Linear map on FE-space that maps v onto itself - template<class B, class TP, std::size_t argIndex> - class FEFunctionForm : public UnaryForm<FEFunctionForm<B,TP,argIndex>, argIndex> - { - public: - using Basis = B; - using TreePath = TP; - using LocalView = typename Basis::LocalView; - using Tree = typename LocalView::Tree; - using Node = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; - - private: - using LeafNode = typename Dune::TypeTree::ChildForTreePath<Tree, Impl::LeafTreePath<Tree,TreePath>>; - - using Buffer = std::vector<typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::RangeType>; - using ValueCache = typename ShapeFunctionCache<Tree>::template NodalValueCache<LeafNode>; - - public: - - using Element = typename Basis::LocalView::Element; - using Coordinate = typename Element::Geometry::LocalCoordinate; - - FEFunctionForm(const Basis& basis, const TreePath& treePath) : - basis_(basis), - treePath_(treePath), - leafTreePath_(Impl::leafTreePath<Tree>(treePath)), - leafNode_(nullptr) - {} - - auto quadratureRuleKey() const - { - return QuadratureRuleKey(leafNode_->finiteElement()); - } - - void bindToLocalView(const LocalView& localView) - { - leafNode_ = & Dune::TypeTree::child(localView.tree(), leafTreePath_); - } - - template<class Cache> - void bindToCache(Cache& cache) - { - valueCache_ = &(cache.getValues(leafTreePath_)); - } - - const auto& treePath() const - { - return treePath_; - } - - const auto& basis() const - { - return basis_; - } - - auto operator()(const Coordinate& x, std::size_t quadPointIndex) const - { - const auto& values = (*valueCache_)[quadPointIndex]; - if constexpr(Node::isLeaf) - return [&](const auto& args) { - return values[std::get<argIndex>(args)]; - }; - else if constexpr(Node::isPower and Node::ChildType::isLeaf) - return [&](const auto& args) { - using Field = typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::RangeFieldType; - constexpr auto components = Node::degree(); - auto componentIndex = std::get<argIndex>(args) / leafNode_->size(); - auto shapeFunctionIndex = std::get<argIndex>(args) % leafNode_->size(); - auto result = Dune::FieldVector<Field,components>(0); - result[componentIndex] = values[shapeFunctionIndex]; - return result; - }; - } - - friend auto jacobian(const FEFunctionForm& f) - { - return FEFunctionJacobianForm<Basis, TreePath, argIndex>(f.basis_, f.treePath_); - } - - friend auto gradient(const FEFunctionForm& f) - { - return Fufem::Forms::transpose(jacobian(f)); - } - - friend auto grad(const FEFunctionForm& f) - { - return Fufem::Forms::transpose(jacobian(f)); - } - - friend auto divergence(const FEFunctionForm& f) - { - return FEFunctionDivergenceForm<Basis, TreePath, argIndex>(f.basis_, f.treePath_); - } - - friend auto div(const FEFunctionForm& f) - { - return divergence(f); - } - - template<std::size_t i> - auto childForm(Dune::index_constant<i> childIndex) const - { - auto childTP = Dune::TypeTree::push_back(treePath_, childIndex); - return FEFunctionForm<Basis, decltype(childTP), argIndex>(basis_, childTP); - } - - private: - const Basis& basis_; - const TreePath treePath_; - Impl::LeafTreePath<Tree,TreePath> leafTreePath_; - const LeafNode* leafNode_; - mutable Buffer evaluationBuffer_; - const ValueCache* valueCache_; - }; - - - - // Linear map on FE-space that maps v onto its Jacobian - template<class B, class TP, std::size_t argIndex> - class FEFunctionJacobianForm : public UnaryForm<FEFunctionJacobianForm<B,TP,argIndex>, argIndex> - { - public: - using Basis = B; - using TreePath = TP; - using LocalView = typename Basis::LocalView; - using Tree = typename LocalView::Tree; - using Node = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; - - private: - using LeafNode = typename Dune::TypeTree::ChildForTreePath<Tree, Impl::LeafTreePath<Tree,TreePath>>; - - using Buffer = std::vector<typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::JacobianType>; - using GlobalJacobianCache = typename ShapeFunctionCache<Tree>::template NodalGlobalJacobianCache<LeafNode>; - - public: - - using Element = typename Basis::LocalView::Element; - using Coordinate = typename Element::Geometry::LocalCoordinate; - - FEFunctionJacobianForm(const Basis& basis, const TreePath& treePath) : - basis_(basis), - treePath_(treePath), - leafTreePath_(Impl::leafTreePath<Tree>(treePath)), - leafNode_(nullptr) - {} - - auto quadratureRuleKey() const - { - return QuadratureRuleKey(leafNode_->finiteElement()).derivative(); - } - - void bindToLocalView(const LocalView& localView) - { - leafNode_ = & Dune::TypeTree::child(localView.tree(), leafTreePath_); - } - - template<class Cache> - void bindToCache(Cache& cache) - { - globalJacobianCache_ = &(cache.getGlobalJacobians(leafTreePath_)); - } - - const auto& treePath() const - { - return treePath_; - } - - const auto& basis() const - { - return basis_; - } - - auto operator()(const Coordinate& x, std::size_t quadPointIndex) const - { - const auto& globalJacobians = (*globalJacobianCache_)[quadPointIndex]; - if constexpr(Node::isLeaf) - return [&](const auto& args) { - return globalJacobians[std::get<argIndex>(args)]; - }; - else if constexpr(Node::isPower and Node::ChildType::isLeaf) - return [&](const auto& args) { - using F = typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::RangeFieldType; - static constexpr auto components = Node::degree(); - static constexpr auto dimension = LeafNode::FiniteElement::Traits::LocalBasisType::Traits::dimDomain; - auto componentIndex = std::get<argIndex>(args) / leafNode_->size(); - auto shapeFunctionIndex = std::get<argIndex>(args) % leafNode_->size(); - auto result = Dune::FieldMatrix<F,components,dimension>(0); - result[componentIndex] = globalJacobians[shapeFunctionIndex][0]; - return result; - }; - } - - template<std::size_t i> - auto childForm(Dune::index_constant<i> childIndex) const - { - auto childTP = Dune::TypeTree::push_back(treePath_, childIndex); - return FEFunctionJacobianForm<Basis, decltype(childTP), argIndex>(basis_, childTP); - } - - private: - const Basis& basis_; - const TreePath treePath_; - Impl::LeafTreePath<Tree,TreePath> leafTreePath_; - const LeafNode* leafNode_; - mutable Buffer evaluationBuffer_; - const GlobalJacobianCache* globalJacobianCache_; - }; - - - - // Linear map on FE-space that maps v onto its divergence - template<class B, class TP, std::size_t argIndex> - class FEFunctionDivergenceForm : public UnaryForm<FEFunctionDivergenceForm<B,TP,argIndex>, argIndex> - { - public: - using Basis = B; - using TreePath = TP; - using LocalView = typename Basis::LocalView; - using Tree = typename LocalView::Tree; - using Node = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; - - private: - using LeafNode = typename Dune::TypeTree::ChildForTreePath<Tree, Impl::LeafTreePath<Tree,TreePath>>; - - using Buffer = std::vector<typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::JacobianType>; - using GlobalJacobianCache = typename ShapeFunctionCache<Tree>::template NodalGlobalJacobianCache<LeafNode>; - - public: - using Element = typename Basis::LocalView::Element; - using Coordinate = typename Element::Geometry::LocalCoordinate; - - FEFunctionDivergenceForm(const Basis& basis, const TreePath& treePath) : - basis_(basis), - treePath_(treePath), - leafTreePath_(Impl::leafTreePath<Tree>(treePath)), - leafNode_(nullptr) - {} - - auto quadratureRuleKey() const - { - return QuadratureRuleKey(leafNode_->finiteElement()).derivative(); - } - - void bindToLocalView(const LocalView& localView) - { - leafNode_ = & Dune::TypeTree::child(localView.tree(), leafTreePath_); - } - - template<class Cache> - void bindToCache(Cache& cache) - { - globalJacobianCache_ = &(cache.getGlobalJacobians(leafTreePath_)); - } - - const auto& treePath() const - { - return treePath_; - } - - const auto& basis() const - { - return basis_; - } - - auto operator()(const Coordinate& x, std::size_t quadPointIndex) const - { - const auto& globalJacobians = (*globalJacobianCache_)[quadPointIndex]; - if constexpr(Node::isLeaf) - return [&](const auto& args) { - using Field = typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::RangeFieldType; - constexpr auto dimension = LeafNode::FiniteElement::Traits::LocalBasisType::Traits::dimDomain; - auto div = Field{0}; - for(std::size_t i=0; i<dimension; ++i) - div += globalJacobians[std::get<argIndex>(args)][i][i]; - return div; - }; - else if constexpr(Node::isPower and Node::ChildType::isLeaf) - return [&](const auto& args) { - // Here we should compute the trace of the global Jacobian J - // that we would need to build first. But since only the - // componentIndex'th row of J is nonzero, we can simply - // return the diagonal entry of this row which coincides - // with the componentIndex'th entry of the gradient of - // the respective scalar basis function. - auto componentIndex = std::get<argIndex>(args) / leafNode_->size(); - auto shapeFunctionIndex = std::get<argIndex>(args) % leafNode_->size(); - return globalJacobians[shapeFunctionIndex][0][componentIndex]; - }; - } - - private: - const Basis& basis_; - const TreePath treePath_; - Impl::LeafTreePath<Tree,TreePath> leafTreePath_; - const LeafNode* leafNode_; - mutable Buffer evaluationBuffer_; - const GlobalJacobianCache* globalJacobianCache_; - }; - - - - // Generic product of multilinear forms. This combines - // a k_1-linear map F_1 with a k_2-linear map F_2 - // into one (k_1+k_2)-linear map using the pointwise - // bilinear map P on the range spaces. - template<class P, class Form0, class Form1> - class ProductForm : public Form - { - using Product = P; - - public: - - static constexpr std::size_t arity = Form0::arity + Form1::arity; - - using Element = typename Form0::Element; - using Coordinate = typename Element::Geometry::LocalCoordinate; - - ProductForm(const Product& product, const Form0& form0, const Form1& form1) : - product_(product), - form0_(form0), - form1_(form1) - {} - - auto quadratureRuleKey() const - { - return form0_.quadratureRuleKey().product(form1_.quadratureRuleKey()); - } - - void bindToElement(const Element& element) - { - form0_.bindToElement(element); - form1_.bindToElement(element); - } - - template<class LocalView> - void bindToTestLocalView(const LocalView& localView) - { - form0_.bindToTestLocalView(localView); - form1_.bindToTestLocalView(localView); - } - - template<class LocalView> - void bindToTrialLocalView(const LocalView& localView) - { - form0_.bindToTrialLocalView(localView); - form1_.bindToTrialLocalView(localView); - } - - template<class Cache> - void bindToTestCache(Cache& cache) - { - form0_.bindToTestCache(cache); - form1_.bindToTestCache(cache); - } - - template<class Cache> - void bindToTrialCache(Cache& cache) - { - form0_.bindToTrialCache(cache); - form1_.bindToTrialCache(cache); - } - - decltype(auto) testBasis() const - { - if constexpr (std::is_same_v<decltype(form1_.testBasis()), void>) - return form0_.testBasis(); - else - return form1_.testBasis(); - } - - decltype(auto) trialBasis() const - { - if constexpr (std::is_same_v<decltype(form1_.trialBasis()), void>) - return form0_.trialBasis(); - else - return form1_.trialBasis(); - } - - decltype(auto) testTreePath() const - { - if constexpr (std::is_same_v<decltype(form1_.testTreePath()), void>) - return form0_.testTreePath(); - else - return form1_.testTreePath(); - } - - decltype(auto) trialTreePath() const - { - if constexpr (std::is_same_v<decltype(form1_.trialTreePath()), void>) - return form0_.trialTreePath(); - else - return form1_.trialTreePath(); - } - - auto operator()(const Coordinate& x, std::size_t quadPointIndex) const - { - auto result0 = form0_(x, quadPointIndex); - auto result1 = form1_(x, quadPointIndex); - return [=](const auto& args) { - return product_(result0(args), result1(args)); - }; - } - - private: - Product product_; - Form0 form0_; - Form1 form1_; - }; - - - - template<class T, class Form0> - class TransformedForm : public Form - { - using Transformation = T; - - public: - static constexpr std::size_t arity = Form0::arity; - - using Element = typename Form0::Element; - using Coordinate = typename Element::Geometry::LocalCoordinate; - - TransformedForm(const Transformation& transformation, const Form0& form0) : - transformation_(transformation), - form0_(form0) - {} - - auto quadratureRuleKey() const - { - return form0_.quadratureRuleKey(); - } - - void bindToElement(const Element& element) - { - form0_.bindToElement(element); - } - - template<class LocalView> - void bindToTestLocalView(const LocalView& localView) - { - form0_.bindToTestLocalView(localView); - } - - template<class LocalView> - void bindToTrialLocalView(const LocalView& localView) - { - form0_.bindToTrialLocalView(localView); - } - - template<class Cache> - void bindToTestCache(Cache& cache) - { - form0_.bindToTestCache(cache); - } - - template<class Cache> - void bindToTrialCache(Cache& cache) - { - form0_.bindToTrialCache(cache); - } - - decltype(auto) testBasis() const - { - return form0_.testBasis(); - } - - decltype(auto) trialBasis() const - { - return form0_.trialBasis(); - } - - decltype(auto) testTreePath() const - { - return form0_.testTreePath(); - } - - decltype(auto) trialTreePath() const - { - return form0_.trialTreePath(); - } - - auto operator()(const Coordinate& x, std::size_t quadPointIndex) const - { - auto result0 = form0_(x, quadPointIndex); - return [=](const auto& args) { - return transformation_(result0(args)); - }; - } - - auto subForm() const - { - return form0_; - } - - private: - Transformation transformation_; - Form0 form0_; - }; - - - - template<class F> - class TransposedForm - : public TransformedForm<Dune::Fufem::Forms::Impl::TransposeOp, F> - { - using Base = TransformedForm<Dune::Fufem::Forms::Impl::TransposeOp, F>; - public: - using Base::arity; - - TransposedForm(const F& form) : - Base(Dune::Fufem::Forms::Impl::TransposeOp(), form) - {} - }; - - - - // Sum of multilinear forms of the same arity k gives another multilinear - // of arity k. - template<class Form0, class... Forms> - class SumForm : public Form - { - public: - static constexpr std::size_t arity = Form0::arity; - - using Element = typename Form0::Element; - using Coordinate = typename Element::Geometry::LocalCoordinate; - - SumForm(const Form0& form0, const Forms&... forms) : - forms_(form0, forms...) - {} - - auto quadratureRuleKey() const - { - auto quadKey = QuadratureRuleKey(); - Impl::forEachTupleEntry(forms_, [&](const auto& form) { - quadKey = form.quadratureRuleKey().sum(quadKey); - }); - return quadKey; - } - - void bindToElement(const Element& element) - { - Impl::forEachTupleEntry(forms_, [&](auto& form) { - form.bindToElement(element); - }); - } - - template<class LocalView> - void bindToTestLocalView(const LocalView& localView) - { - Impl::forEachTupleEntry(forms_, [&](auto& form) { - form.bindToTestLocalView(localView); - }); - } - - template<class LocalView> - void bindToTrialLocalView(const LocalView& localView) - { - Impl::forEachTupleEntry(forms_, [&](auto& form) { - form.bindToTrialLocalView(localView); - }); - } - - template<class Cache> - void bindToTestCache(Cache& cache) - { - Impl::forEachTupleEntry(forms_, [&](auto& form) { - form.bindToTestCache(cache); - }); - } - - template<class Cache> - void bindToTrialCache(Cache& cache) - { - Impl::forEachTupleEntry(forms_, [&](auto& form) { - form.bindToTrialCache(cache); - }); - } - - decltype(auto) testBasis() const - { - return std::get<0>(forms_).testBasis(); - } - - decltype(auto) trialBasis() const - { - return std::get<0>(forms_).trialBasis(); - } - - auto& forms() - { - return forms_; - } - - const auto& forms() const - { - return forms_; - } - - private: - std::tuple<Form0, Forms...> forms_; - }; - - - - template<class Form> - auto makeSumForm(Form form) - { - return SumForm<Form>(form); - } - - template<class... Forms> - auto makeSumForm(SumForm<Forms...> sumForm) - { - return sumForm; - } - - - - template<class Basis> - auto testFunction(const Basis& basis) - { - using RootBasis = std::decay_t<decltype(basis.rootBasis())>; - return FEFunctionForm<RootBasis, typename Basis::PrefixPath, 0>(basis.rootBasis(), basis.prefixPath()); - } - - template<class Basis, class... Args, std::enable_if_t<(sizeof...(Args)>0), int> = 0> - auto testFunction(const Basis& basis, Args&&... args) - { - return testFunction(Functions::subspaceBasis(basis, args...)); - } - - - - - template<class Basis> - auto trialFunction(const Basis& basis) - { - using RootBasis = std::decay_t<decltype(basis.rootBasis())>; - return FEFunctionForm<RootBasis, typename Basis::PrefixPath, 1>(basis.rootBasis(), basis.prefixPath()); - } - - template<class Basis, class... Args, std::enable_if_t<(sizeof...(Args)>0), int> = 0> - auto trialFunction(const Basis& basis, Args&&... args) - { - return trialFunction(Functions::subspaceBasis(basis, args...)); - } - - - - template<class L, class R, - std::enable_if_t<isForm_v<L> and isForm_v<R>, int> = 0> - auto sum(const L& l, const R& r) - { - return SumForm<L, R>(l, r); - } - - template<class L, class... Ri, - std::enable_if_t<isForm_v<L>, int> = 0> - auto sum(const L& l, const SumForm<Ri...>& r) - { - return std::apply([&](const auto&... ri) { - return SumForm<L, Ri...>(l, ri...); - }, r.forms()); - } - - template<class... Li, class R, - std::enable_if_t<isForm_v<R>, int> = 0> - auto sum(const SumForm<Li...>& l, const R& r) - { - return std::apply([&](const auto&... li) { - return SumForm<Li..., R>(li..., r); - }, l.forms()); - } - - template<class... Li, class... Rj> - auto sum(const SumForm<Li...>& l, const SumForm<Rj...>& r) - { - return std::apply([&](const auto&...li) { - return std::apply([&](const auto&...rj) { - return SumForm<Li..., Rj...>(li..., rj...); - }, r.forms()); - }, l.forms()); - } - - - - template<class Op, class F> - auto transformedForm(const Op& op, const F& f) - { - return TransformedForm(op, f); - } - - template<class Op, class... Fi> - auto transformedForm(const Op& op, const SumForm<Fi...>& f) - { - return std::apply([&](const auto&...fi) { - return (transformedForm(op, fi) + ...); - }, f.forms()); - } - - - - template<class Op> - class FormOperator - { - public: - FormOperator(const Op& op) : op_(op) {} - - template<class Arg, std::enable_if_t<isForm_v<Arg>, int> = 0> - auto operator()(Arg&& arg) const - { - return transformedForm(op_, std::forward<Arg>(arg)); - } - - template<class Arg, std::enable_if_t<not isForm_v<Arg>, int> = 0> - auto operator()(Arg&& arg) const - { - return op_(std::forward<Arg>(arg)); - } - - private: - Op op_; - }; - - - - // The following are the generic functions for producing - // product forms from a binary operator and two other - // terms. At least one of those terms should be a form. - - // Overload for two plain forms - template<class Op, class L, class R, - std::enable_if_t<isForm_v<L> and isForm_v<R>, int> = 0, - std::enable_if_t<Dune::IsCallable<Op(Impl::FormRange<L>, Impl::FormRange<R>)>::value, int> = 0> - auto productForm(const Op& op, const L& l, const R& r) - { - return ProductForm(op, l, r); - } - - // Overload for one raw value and one form - // This will wrap the raw value into a form. - template<class Op, class L, class R, - std::enable_if_t<(not isForm_v<L>) and isForm_v<R>, int> = 0, - std::enable_if_t<Dune::IsCallable<Op(L, Impl::FormRange<R>)>::value, int> = 0> - auto productForm(const Op& op, const L& l, const R& r) - { - return ProductForm(op, ConstCoefficient<typename R::Element,L>(l), r); - } - - // Overload for one form and one raw value - // This will wrap the raw value into a form. - template<class Op, class L, class R, - std::enable_if_t<isForm_v<L> and (not isForm_v<R>), int> = 0, - std::enable_if_t<Dune::IsCallable<Op(Impl::FormRange<L>, R)>::value, int> = 0> - auto productForm(const Op& op, const L& l, const R& r) - { - return ProductForm(op, l, ConstCoefficient<typename L::Element,R>(r)); - } - - // Overload for one sum-form and one plain term - // This will multiply out the product for the sum-form. - template<class Op, class... Li, class R, - class = std::void_t<decltype((productForm(std::declval<Op>(), std::declval<Li>(), std::declval<R>()),...))>> - auto productForm(const Op& op, const SumForm<Li...>& l, const R& r) - { - return std::apply([&](const auto&... li) { - return (productForm(op, li, r) + ...); - }, l.forms()); - } - - // Overload for one plain term and one sum-form - // This will multiply out the product for the sum-form. - template<class Op, class L, class... Ri, - class = std::void_t<decltype((productForm(std::declval<Op>(), std::declval<L>(), std::declval<Ri>()),...))>> - auto productForm(const Op& op, const L& l, const SumForm<Ri...>& r) - { - return std::apply([&](const auto&...ri) { - return (productForm(op, l, ri) + ...); - }, r.forms()); - } - - // Overload for two sum-forms - // This will multiply out the product for both sum-forms. - template<class Op, class... Li, class... Rj, - class = std::void_t<decltype((productForm(std::declval<Op>(), std::declval<Li>(), std::declval<SumForm<Rj...>>()),...))>> - auto productForm(const Op& op, const SumForm<Li...>& l, const SumForm<Rj...>& r) - { - return std::apply([&](const auto&...li) { - return (productForm(op, li, r) + ...); - }, l.forms()); - } - - - - // This is the default implementation. The following specializations - // are optimized versions for certain special cases. - template<class L, class R, - std::enable_if_t<isForm_v<L> or isForm_v<R>, int> = 0, - class = std::void_t<decltype(productForm(std::declval<Impl::DotOp>(), std::declval<L>(), std::declval<R>()))>> - auto dot(const L& l, const R& r) - { - return productForm(Dune::Fufem::Forms::Impl::DotOp(), l, r); - } - - template<class B, class TP, std::size_t argIndex_L, std::size_t argIndex_R> - auto dot(const FEFunctionForm<B,TP,argIndex_L>& l, const FEFunctionForm<B,TP,argIndex_R>& r); - - template<class B, class TP, std::size_t argIndex_L, std::size_t argIndex_R> - auto dot(const FEFunctionJacobianForm<B,TP,argIndex_L>& l, const FEFunctionJacobianForm<B,TP,argIndex_R>& r); - - template<class L, class R> - auto dot(const TransposedForm<L>& l, const TransposedForm<R>& r) - { - using namespace Dune::Indices; - return Dune::Fufem::Forms::dot(l.subForm(), r.subForm()); - } - - template<class B, class TP, std::size_t argIndex_L, std::size_t argIndex_R> - auto dot(const FEFunctionForm<B,TP,argIndex_L>& l, const FEFunctionForm<B,TP,argIndex_R>& r) - { - using Node = typename FEFunctionForm<B,TP,argIndex_L>::Node; - if constexpr (Node::isPower) - { - return unpackIntegerSequence( - [&](auto... i) { - return (Dune::Fufem::Forms::dot(l.childForm(i), r.childForm(i)) + ...); - }, - std::make_index_sequence<Node::degree()>{}); - } - else - return productForm(Dune::Fufem::Forms::Impl::DotOp(), l, r); - } - - template<class B, class TP, std::size_t argIndex_L, std::size_t argIndex_R> - auto dot(const FEFunctionJacobianForm<B,TP,argIndex_L>& l, const FEFunctionJacobianForm<B,TP,argIndex_R>& r) - { - using Node = typename FEFunctionJacobianForm<B,TP,argIndex_L>::Node; - if constexpr (Node::isPower) - { - return unpackIntegerSequence( - [&](auto... i) { - return (Dune::Fufem::Forms::dot(l.childForm(i), r.childForm(i)) + ...); - }, - std::make_index_sequence<Node::degree()>{}); - } - else - return productForm(Dune::Fufem::Forms::Impl::DotOp(), l, r); - } - - - - // This overload is recursively constrained to cases where `operator*` makes sense. - // Otherwise Dune::dot(a,b) may be activated for forms because it relies on availability - // of a*b. This is a problem, because it leads to ambiguity with Forms::dot(a,b). - template<class L, class R, - std::enable_if_t<isForm_v<L> or isForm_v<R>, int> = 0, - class = std::void_t<decltype(productForm(std::declval<Impl::MultOp>(), std::declval<L>(), std::declval<R>()))>> - auto operator* (const L& l, const R& r) - { - return productForm(Impl::MultOp{}, l, r); - } - - - - template<class F, - std::enable_if_t<isForm_v<F>, int> = 0> - auto operator- (const F& f) - { - auto op = [](const auto& x) { - return -x; - }; - return transformedForm(op, f); - } - - template<class L, class R, - std::enable_if_t<isForm_v<L> and isForm_v<R>, int> = 0> - auto operator+ (const L& l, const R& r) - { - static_assert(L::arity==R::arity, "The forms passed to operator+ do not have the same arity"); - return sum(l, r); - } - - template<class L, class R, - std::enable_if_t<isForm_v<L> and isForm_v<R>, int> = 0> - auto operator- (const L& l, const R& r) - { - static_assert(L::arity==R::arity, "The forms passed to operator- do not have the same arity"); - return sum(l, -r); - } - - - - template<class F, - std::enable_if_t<isForm_v<F>, int>> - auto transpose(const F& f) - { - return TransposedForm(f); - } +namespace Dune::Fufem::Forms { template<class Form, bool isSemiAffine=true> class IntegratedLinearForm { - using TestRootBasis = std::decay_t<decltype(std::declval<Form>().testBasis())>; + using TestRootBasis = std::decay_t<decltype(std::declval<Form>().template basis<0>())>; using TestTree = typename TestRootBasis::LocalView::Tree; @@ -1322,7 +41,7 @@ namespace Impl { IntegratedLinearForm(const Form& sumForm) : sumForm_(sumForm), - testRootBasis_(sumForm_.testBasis()) + testRootBasis_(sumForm_.template basis<0>()) {} template<class LocalVector, class TestLocalView> @@ -1333,13 +52,13 @@ namespace Impl { const auto& geometry = element.geometry(); sumForm_.bindToElement(testLocalView.element()); - sumForm_.bindToTestLocalView(testLocalView); + sumForm_.bindToLocalView(testLocalView); auto& cacheForRule = cache_[sumForm_.quadratureRuleKey()]; cacheForRule.bind(testLocalView.tree()); cacheForRule.invalidate(isSemiAffine); - sumForm_.bindToTestCache(cacheForRule); + sumForm_.bindToCache(cacheForRule); const auto& quad = cacheForRule.rule(); @@ -1352,7 +71,7 @@ namespace Impl { Impl::forEachTupleEntry(sumForm_.forms(), [&](auto& form) { auto evaluatedForm = form(quadPoint.position(), k); - const auto& testPrefix = form.testTreePath(); + const auto& testPrefix = form.template treePath<0>(); const auto& testNode = Dune::TypeTree::child(testLocalView.tree(), testPrefix); for (std::size_t i=0; i<testNode.size(); ++i) { @@ -1379,8 +98,8 @@ namespace Impl { template<class Form, bool isSemiAffine=true> class IntegratedBilinearForm { - using TestRootBasis = std::decay_t<decltype(std::declval<Form>().testBasis())>; - using AnsatzRootBasis = std::decay_t<decltype(std::declval<Form>().trialBasis())>; + using TestRootBasis = std::decay_t<decltype(std::declval<Form>().template basis<0>())>; + using AnsatzRootBasis = std::decay_t<decltype(std::declval<Form>().template basis<1>())>; using TestTree = typename TestRootBasis::LocalView::Tree; @@ -1389,8 +108,8 @@ namespace Impl { IntegratedBilinearForm(const Form& sumForm) : sumForm_(sumForm), - testRootBasis_(sumForm_.testBasis()), - ansatzRootBasis_(sumForm_.trialBasis()) + testRootBasis_(sumForm_.template basis<0>()), + ansatzRootBasis_(sumForm_.template basis<1>()) {} template<class LocalMatrix, class TestLocalView, class AnsatzLocalView> @@ -1402,15 +121,13 @@ namespace Impl { const auto& geometry = element.geometry(); sumForm_.bindToElement(testLocalView.element()); - sumForm_.bindToTestLocalView(testLocalView); - sumForm_.bindToTrialLocalView(ansatzLocalView); + sumForm_.bindToLocalView(testLocalView, ansatzLocalView); auto& cacheForRule = cache_[sumForm_.quadratureRuleKey()]; cacheForRule.bind(testLocalView.tree()); cacheForRule.invalidate(isSemiAffine); - sumForm_.bindToTestCache(cacheForRule); - sumForm_.bindToTrialCache(cacheForRule); + sumForm_.bindToCache(cacheForRule, cacheForRule); const auto& quad = cacheForRule.rule(); @@ -1423,9 +140,9 @@ namespace Impl { Impl::forEachTupleEntry(sumForm_.forms(), [&](auto& form) { auto evaluatedForm = form(quadPoint.position(), k); - const auto& testPrefix = form.testTreePath(); + const auto& testPrefix = form.template treePath<0>(); const auto& testNode = Dune::TypeTree::child(testLocalView.tree(), testPrefix); - const auto& ansatzPrefix = form.trialTreePath(); + const auto& ansatzPrefix = form.template treePath<1>(); const auto& ansatzTree = Dune::TypeTree::child(ansatzLocalView.tree(), ansatzPrefix); for (std::size_t i=0; i<testNode.size(); ++i) { @@ -1457,7 +174,7 @@ namespace Impl { template<class Form, class Patch, bool isSemiAffine=true> class IntegratedBoundaryLinearForm { - using TestRootBasis = std::decay_t<decltype(std::declval<Form>().testBasis())>; + using TestRootBasis = std::decay_t<decltype(std::declval<Form>().template basis<0>())>; using TestTree = typename TestRootBasis::LocalView::Tree; @@ -1468,7 +185,7 @@ namespace Impl { IntegratedBoundaryLinearForm(const Form& sumForm, const Patch& patch) : sumForm_(sumForm), - testRootBasis_(sumForm_.testBasis()), + testRootBasis_(sumForm_.template basis<0>()), patch_(patch) {} @@ -1491,13 +208,13 @@ namespace Impl { const auto& geometry = intersection.geometry(); sumForm_.bindToElement(testLocalView.element()); - sumForm_.bindToTestLocalView(testLocalView); + sumForm_.bindToLocalView(testLocalView); auto& cacheForRule = cache_[std::pair(sumForm_.quadratureRuleKey(), facet)]; cacheForRule.bind(testLocalView.tree()); cacheForRule.invalidate(isSemiAffine); - sumForm_.bindToTestCache(cacheForRule); + sumForm_.bindToCache(cacheForRule); const auto& quad = cacheForRule.rule(); @@ -1511,7 +228,7 @@ namespace Impl { Impl::forEachTupleEntry(sumForm_.forms(), [&](auto& form) { auto evaluatedForm = form(quadPoint.position(), k); - const auto& testPrefix = form.testTreePath(); + const auto& testPrefix = form.template treePath<0>(); const auto& testNode = Dune::TypeTree::child(testLocalView.tree(), testPrefix); for (std::size_t i=0; i<testNode.size(); ++i) { @@ -1590,7 +307,7 @@ namespace Impl { std::enable_if_t<isForm_v<Form>, int> = 0> auto integrate(Form form) { - auto sumForm = makeSumForm(form); + auto sumForm = SumForm(form); if constexpr(Form::arity==1) return IntegratedLinearForm(sumForm); else if constexpr(Form::arity==2) @@ -1601,7 +318,7 @@ namespace Impl { std::enable_if_t<isForm_v<Form>, int> = 0> auto integrate(Form form, const BoundaryPatch<GridView>& patch) { - auto sumForm = makeSumForm(form); + auto sumForm = SumForm(form); if constexpr(Form::arity==1) return IntegratedBoundaryLinearForm(sumForm, patch); } @@ -1614,7 +331,7 @@ namespace Impl { std::enable_if_t<isForm_v<Form>, int> = 0> auto integrate(Form form, NonAffineFamiliyTag) { - auto sumForm = makeSumForm(form); + auto sumForm = SumForm(form); using SumForm = decltype(sumForm); if constexpr(Form::arity==1) return IntegratedLinearForm<SumForm, false>(sumForm); @@ -1626,7 +343,7 @@ namespace Impl { std::enable_if_t<isForm_v<Form>, int> = 0> auto integrate(Form form, const BoundaryPatch<GridView>& patch, NonAffineFamiliyTag) { - auto sumForm = makeSumForm(form); + auto sumForm = SumForm(form); using SumForm = decltype(sumForm); using Patch = BoundaryPatch<GridView>; if constexpr(Form::arity==1) diff --git a/dune/fufem-forms/fufem-forms.hh b/dune/fufem-forms/fufem-forms.hh deleted file mode 100644 index 6262192..0000000 --- a/dune/fufem-forms/fufem-forms.hh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef DUNE_FUFEM_FORMS_HH -#define DUNE_FUFEM_FORMS_HH - -// add your classes here - -#endif // DUNE_FUFEM_FORMS_HH diff --git a/dune/fufem-forms/nullaryforms.hh b/dune/fufem-forms/nullaryforms.hh new file mode 100644 index 0000000..d2ce26a --- /dev/null +++ b/dune/fufem-forms/nullaryforms.hh @@ -0,0 +1,74 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FUFEM_FORMS_NULLARYFORMS_HH +#define DUNE_FUFEM_FORMS_NULLARYFORMS_HH + +#include <type_traits> +#include <tuple> +#include <utility> + +#include <dune/common/typetraits.hh> + +#include <dune/istl/matrix.hh> +#include <dune/istl/matrixindexset.hh> + +#include <dune/fufem-forms/baseclass.hh> + + + +namespace Dune::Fufem::Forms { + + + + // Zero-linear map induced by fixed coefficient function + template<class F> + class Coefficient : public Form + { + using LocalFunction = std::decay_t<decltype(localFunction(std::declval<F&>()))>; + + public: + static constexpr std::size_t arity = 0; + + using Element = typename F::EntitySet::Element; + using Coordinate = typename Element::Geometry::LocalCoordinate; + + Coefficient(const F& f) : + f_(f), + localF_(localFunction(f)) + {} + + Coefficient(const F& f, std::size_t order) : + f_(f), + localF_(localFunction(f)) + {} + + auto quadratureRuleKey() const + { + return QuadratureRuleKey(Element::dimension, 0); + } + + void bindToElement(const Element& element) + { + localF_.bind(element); + } + + auto operator()(const Coordinate& x, std::size_t quadPointIndex) const + { + auto value = localF_(x); + return [value](const auto& args) { + return value; + }; + } + + private: + F f_; + LocalFunction localF_; + }; + + + +} // namespace Dune::Fufem::Forms + + + +#endif // DUNE_FUFEM_FORMS_NULLARYFORMS_HH diff --git a/dune/fufem-forms/operators.hh b/dune/fufem-forms/operators.hh new file mode 100644 index 0000000..92be0c5 --- /dev/null +++ b/dune/fufem-forms/operators.hh @@ -0,0 +1,213 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FUFEM_FORMS_OPERATORS_HH +#define DUNE_FUFEM_FORMS_OPERATORS_HH + +#include <cstddef> +#include <type_traits> +#include <utility> + +#include <dune/common/fvector.hh> +#include <dune/common/fmatrix.hh> + +#include <dune/istl/scaledidmatrix.hh> + +#include <dune/fufem-forms/baseclass.hh> +#include <dune/fufem-forms/transformedform.hh> +#include <dune/fufem-forms/unaryforms.hh> +#include <dune/fufem-forms/productform.hh> + + + +namespace Dune::Fufem::Forms::Impl { + + + + struct DotOp { + + template<class K1, class K2, int n> + auto operator()(const Dune::FieldVector<K1,n>& x, const Dune::FieldVector<K2,n>& y) const + { + using K = typename PromotionTraits<K1,K2>::PromotedType; + auto result = K(0); + for(std::size_t i=0; i<n; ++i) + result += x[i]*y[i]; + return result; + } + + template<class K1, class K2, int n, int m> + auto operator()(const Dune::FieldMatrix<K1,n,m>& x, const Dune::FieldMatrix<K2,n,m>& y) const + { + using K = typename PromotionTraits<K1,K2>::PromotedType; + auto result = K(0); + for(std::size_t i=0; i<n; ++i) + for(std::size_t j=0; j<m; ++j) + result += x[i][j]*y[i][j]; + return result; + } + + template<class K1, class K2, int n> + auto operator()(const Dune::FieldMatrix<K1,n,1>& x, const Dune::FieldVector<K2,n>& y) const + { + using K = typename PromotionTraits<K1,K2>::PromotedType; + auto result = K(0); + for(std::size_t i=0; i<n; ++i) + result += x[i][0]*y[i]; + return result; + } + + template<class K1, class K2, int n> + auto operator()(const Dune::FieldVector<K1,n>& x, const Dune::FieldMatrix<K2,n,1>& y) const + { + using K = typename PromotionTraits<K1,K2>::PromotedType; + auto result = K(0); + for(std::size_t i=0; i<n; ++i) + result += x[i]*y[i][0]; + return result; + } + + template<class K1, class K2, int n> + auto operator()(const Dune::ScaledIdentityMatrix<K1,n>& x, const Dune::FieldMatrix<K2,n,n>& y) const + { + using K = typename PromotionTraits<K1,K2>::PromotedType; + auto result = K(0); + for(std::size_t i=0; i<x.N(); ++i) + result += y[i][i]; + return x.scalar()*result; + } + + template<class K1, class K2, int n> + auto operator()(const Dune::FieldMatrix<K1,n,n>& x, const Dune::ScaledIdentityMatrix<K2,n>& y) const + { + using K = typename PromotionTraits<K1,K2>::PromotedType; + auto result = K(0); + for(std::size_t i=0; i<x.N(); ++i) + result += x[i][i]; + return result*y.scalar(); + } + + template<class K1, class K2, int n> + auto operator()(const Dune::ScaledIdentityMatrix<K1,n>& x, const Dune::ScaledIdentityMatrix<K2,n>& y) const + { + return n*x.scalar()*y.scalar(); + } + + }; + + struct MultOp { + + template<class X, class Y, + class = std::void_t<decltype(std::declval<X>()*std::declval<Y>())>> + auto operator()(const X& x, const Y& y) const + { + return x*y; + } + + template<class K1, class K2, int n, int m> + auto operator()(const Dune::FieldMatrix<K1,n,m>& x, const Dune::FieldVector<K2,m>& y) const + { + using K = typename PromotionTraits<K1,K2>::PromotedType; + Dune::FieldVector<K,n> result; + x.mv(y, result); + return result; + } + + template<class K, class Y> + auto operator()(const Dune::FieldMatrix<K,1,1>& x, const Y& y) const + { + return x[0][0]*y; + } + + template<class K, class Y> + auto operator()(const Dune::FieldVector<K,1>& x, const Y& y) const + { + return x[0]*y; + } + + }; + + + +} // namespace Impl + + + +namespace Dune::Fufem::Forms { + + + + // This is the default implementation. The following specializations + // are optimized versions for certain special cases. + template<class L, class R, + std::enable_if_t<isForm_v<L> or isForm_v<R>, int> = 0, + class = std::void_t<decltype(productForm(std::declval<Impl::DotOp>(), std::declval<L>(), std::declval<R>()))>> + auto dot(const L& l, const R& r) + { + return productForm(Dune::Fufem::Forms::Impl::DotOp(), l, r); + } + + template<class B, class TP, std::size_t argIndex_L, std::size_t argIndex_R> + auto dot(const FEFunctionForm<B,TP,argIndex_L>& l, const FEFunctionForm<B,TP,argIndex_R>& r); + + template<class B, class TP, std::size_t argIndex_L, std::size_t argIndex_R> + auto dot(const FEFunctionJacobianForm<B,TP,argIndex_L>& l, const FEFunctionJacobianForm<B,TP,argIndex_R>& r); + + template<class L, class R> + auto dot(const TransposedForm<L>& l, const TransposedForm<R>& r) + { + std::cout << "eliminating transpose" << std::endl; + using namespace Dune::Indices; + return Dune::Fufem::Forms::dot(l.baseForm(), r.baseForm()); + } + + template<class B, class TP, std::size_t argIndex_L, std::size_t argIndex_R> + auto dot(const FEFunctionForm<B,TP,argIndex_L>& l, const FEFunctionForm<B,TP,argIndex_R>& r) + { + using Node = typename FEFunctionForm<B,TP,argIndex_L>::Node; + if constexpr (Node::isPower) + { + return unpackIntegerSequence( + [&](auto... i) { + return (Dune::Fufem::Forms::dot(l.childForm(i), r.childForm(i)) + ...); + }, + std::make_index_sequence<Node::degree()>{}); + } + else + return productForm(Dune::Fufem::Forms::Impl::DotOp(), l, r); + } + + template<class B, class TP, std::size_t argIndex_L, std::size_t argIndex_R> + auto dot(const FEFunctionJacobianForm<B,TP,argIndex_L>& l, const FEFunctionJacobianForm<B,TP,argIndex_R>& r) + { + using Node = typename FEFunctionJacobianForm<B,TP,argIndex_L>::Node; + if constexpr (Node::isPower) + { + return unpackIntegerSequence( + [&](auto... i) { + return (Dune::Fufem::Forms::dot(l.childForm(i), r.childForm(i)) + ...); + }, + std::make_index_sequence<Node::degree()>{}); + } + else + return productForm(Dune::Fufem::Forms::Impl::DotOp(), l, r); + } + + + + // This overload is recursively constrained to cases where `operator*` makes sense. + // Otherwise Dune::dot(a,b) may be activated for forms because it relies on availability + // of a*b. This is a problem, because it leads to ambiguity with Forms::dot(a,b). + template<class L, class R, + std::enable_if_t<isForm_v<L> or isForm_v<R>, int> = 0, + class = std::void_t<decltype(productForm(std::declval<Impl::MultOp>(), std::declval<L>(), std::declval<R>()))>> + auto operator* (const L& l, const R& r) + { + return productForm(Impl::MultOp{}, l, r); + } + + + +} // namespace Dune::Fufem::Forms + + +#endif // DUNE_FUFEM_FORMS_OPERATORS_HH diff --git a/dune/fufem-forms/productform.hh b/dune/fufem-forms/productform.hh new file mode 100644 index 0000000..14ecebc --- /dev/null +++ b/dune/fufem-forms/productform.hh @@ -0,0 +1,248 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FUFEM_FORMS_PRODUCTFORM_HH +#define DUNE_FUFEM_FORMS_PRODUCTFORM_HH + +#include <cstddef> +#include <type_traits> +#include <tuple> +#include <utility> + +#include <dune/fufem-forms/baseclass.hh> +#include <dune/fufem-forms/transformedform.hh> +#include <dune/fufem-forms/sumform.hh> + + + +namespace Dune::Fufem::Forms::Impl { + + + template<class Op> + class TransposedBinaryOp + { + public: + TransposedBinaryOp(const Op& op) : op_(op) {} + + template<class X, class Y> + decltype(auto) operator()(X&& x, Y&& y) const + { + return op_(std::forward<X>(x), std::forward<Y>(y)); + } + + private: + Op op_; + }; + +} // namespace Dune::Fufem::Forms::Impl + +namespace Dune::Fufem::Forms { + + + template<class C, class BaseForm, class NullaryForm> + class ProductWithNullaryForm : public BaseForm + { + using Contraction = C; + + public: + + static constexpr std::size_t arity = NullaryForm::arity + BaseForm::arity; + + using Element = typename BaseForm::Element; + using Coordinate = typename BaseForm::Coordinate; + + ProductWithNullaryForm(const Contraction& product, const BaseForm& form0, const NullaryForm& form1) : + BaseForm(form0), + product_(product), + form1_(form1) + {} + + auto quadratureRuleKey() const + { + return BaseForm::quadratureRuleKey().product(form1_.quadratureRuleKey()); + } + + void bindToElement(const Element& element) + { + form1_.bindToElement(element); + if constexpr (BaseForm::arity==0) + BaseForm::bindToElement(element); + } + + auto operator()(const Coordinate& x, std::size_t quadPointIndex) const + { + auto result0 = BaseForm::operator()(x, quadPointIndex); + auto result1 = form1_(x, quadPointIndex); + return [=](const auto& args) { + return product_(result0(args), result1(args)); + }; + } + + private: + Contraction product_; + NullaryForm form1_; + }; + + + + // Generic product of multilinear forms. This combines + // a k_1-linear map F_1 with a k_2-linear map F_2 + // into one (k_1+k_2)-linear map using the pointwise + // bilinear map P on the range spaces. + template<class P, class Form0, class Form1> + class UnaryUnaryProductForm : public Dune::Fufem::Forms::Form + { + using Contraction = P; + + public: + + static constexpr std::size_t arity = Form0::arity + Form1::arity; + + using Element = typename Form0::Element; + using Coordinate = typename Element::Geometry::LocalCoordinate; + + UnaryUnaryProductForm(const Contraction& product, const Form0& form0, const Form1& form1) : + product_(product), + form0_(form0), + form1_(form1) + {} + + auto quadratureRuleKey() const + { + return form0_.quadratureRuleKey().product(form1_.quadratureRuleKey()); + } + + void bindToElement(const Element& element) + { + form0_.bindToElement(element); + form1_.bindToElement(element); + } + + template<class LocalView0, class LocalView1> + void bindToLocalView(const LocalView0& localView0, const LocalView1& localView1) + { + form0_.bindToLocalView(localView0); + form1_.bindToLocalView(localView1); + } + + template<class Cache0, class Cache1> + void bindToCache(Cache0& cache0, Cache1& cache1) + { + form0_.bindToCache(cache0); + form1_.bindToCache(cache1); + } + + template<int k> + decltype(auto) basis() const + { + if constexpr(k==0) + return form0_.template basis<0>(); + if constexpr(k==1) + return form1_.template basis<0>(); + } + + template<int k> + decltype(auto) treePath() const + { + if constexpr(k==0) + return form0_.template treePath<0>(); + if constexpr(k==1) + return form1_.template treePath<0>(); + } + + auto operator()(const Coordinate& x, std::size_t quadPointIndex) const + { + auto result0 = form0_(x, quadPointIndex); + auto result1 = form1_(x, quadPointIndex); + return [=](const auto& args) { + return product_(result0(args), result1(args)); + }; + } + + private: + Contraction product_; + Form0 form0_; + Form1 form1_; + }; + + + + // The following are the generic functions for producing + // product forms from a binary operator and two other + // terms. At least one of those terms should be a form. + + // Overload for two plain forms + template<class Op, class L, class R, + std::enable_if_t<isForm_v<L> and isForm_v<R>, int> = 0, + std::enable_if_t<Dune::IsCallable<Op(Impl::FormRange<L>, Impl::FormRange<R>)>::value, int> = 0> + auto productForm(const Op& op, const L& l, const R& r) + { + static_assert(L::arity+R::arity<=2, "Trying to construct multi-linear operator with rank>2"); + if constexpr(R::arity==0) + return ProductWithNullaryForm(op, l, r); + else if constexpr(L::arity==0) + return ProductWithNullaryForm(Impl::TransposedBinaryOp(op), r, l); + else if constexpr((L::argIndex==0) and (R::argIndex==1)) + return UnaryUnaryProductForm(op, l, r); + else if constexpr((L::argIndex==1) and (R::argIndex==0)) + return UnaryUnaryProductForm(Impl::TransposedBinaryOp(op), r, l); + } + + // Overload for one raw value and one form + // This will wrap the raw value into a form. + template<class Op, class L, class R, + std::enable_if_t<(not isForm_v<L>) and isForm_v<R>, int> = 0, + std::enable_if_t<Dune::IsCallable<Op(L, Impl::FormRange<R>)>::value, int> = 0> + auto productForm(const Op& op, const L& l, const R& r) + { + return transformRange(r, [op, l](auto&& r_value) { return op(l, r_value); }); + } + + // Overload for one form and one raw value + // This will wrap the raw value into a form. + template<class Op, class L, class R, + std::enable_if_t<isForm_v<L> and (not isForm_v<R>), int> = 0, + std::enable_if_t<Dune::IsCallable<Op(Impl::FormRange<L>, R)>::value, int> = 0> + auto productForm(const Op& op, const L& l, const R& r) + { + return transformRange(l, [op, r](auto&& l_value) { return op(l_value, r); }); + } + + // Overload for one sum-form and one plain term + // This will multiply out the product for the sum-form. + template<class Op, class... Li, class R, + class = std::void_t<decltype((productForm(std::declval<Op>(), std::declval<Li>(), std::declval<R>()),...))>> + auto productForm(const Op& op, const SumForm<Li...>& l, const R& r) + { + return std::apply([&](const auto&... li) { + return (productForm(op, li, r) + ...); + }, l.forms()); + } + + // Overload for one plain term and one sum-form + // This will multiply out the product for the sum-form. + template<class Op, class L, class... Ri, + class = std::void_t<decltype((productForm(std::declval<Op>(), std::declval<L>(), std::declval<Ri>()),...))>> + auto productForm(const Op& op, const L& l, const SumForm<Ri...>& r) + { + return std::apply([&](const auto&...ri) { + return (productForm(op, l, ri) + ...); + }, r.forms()); + } + + // Overload for two sum-forms + // This will multiply out the product for both sum-forms. + template<class Op, class... Li, class... Rj, + class = std::void_t<decltype((productForm(std::declval<Op>(), std::declval<Li>(), std::declval<SumForm<Rj...>>()),...))>> + auto productForm(const Op& op, const SumForm<Li...>& l, const SumForm<Rj...>& r) + { + return std::apply([&](const auto&...li) { + return (productForm(op, li, r) + ...); + }, l.forms()); + } + + + +} // namespace Dune::Fufem::Forms + + +#endif // DUNE_FUFEM_FORMS_PRODUCTFORM_HH diff --git a/dune/fufem-forms/sumform.hh b/dune/fufem-forms/sumform.hh new file mode 100644 index 0000000..089e564 --- /dev/null +++ b/dune/fufem-forms/sumform.hh @@ -0,0 +1,156 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FUFEM_FORMS_SUMFORM_HH +#define DUNE_FUFEM_FORMS_SUMFORM_HH + +#include <cstddef> +#include <type_traits> +#include <tuple> +#include <utility> + +#include <dune/fufem/quadraturerules/quadraturerulecache.hh> + +#include <dune/fufem-forms/baseclass.hh> + + + +namespace Dune::Fufem::Forms { + + + + // Sum of multilinear forms of the same arity k gives another multilinear + // of arity k. + template<class Form0, class... Forms> + class SumForm : public Dune::Fufem::Forms::Form + { + public: + static constexpr std::size_t arity = Form0::arity; + + using Element = typename Form0::Element; + using Coordinate = typename Element::Geometry::LocalCoordinate; + + SumForm(Form0 form0, Forms... forms) : + forms_(form0, forms...) + {} + + auto quadratureRuleKey() const + { + auto quadKey = QuadratureRuleKey(); + Impl::forEachTupleEntry(forms_, [&](const auto& form) { + quadKey = form.quadratureRuleKey().sum(quadKey); + }); + return quadKey; + } + + void bindToElement(const Element& element) + { + Impl::forEachTupleEntry(forms_, [&](auto& form) { + form.bindToElement(element); + }); + } + + template<class... LocalViews> + void bindToLocalView(const LocalViews&... localViews) + { + Impl::forEachTupleEntry(forms_, [&](auto& form) { + form.bindToLocalView(localViews...); + }); + } + + template<class... Caches> + void bindToCache(Caches&... caches) + { + Impl::forEachTupleEntry(forms_, [&](auto& form) { + form.bindToCache(caches...); + }); + } + + template<int k> + decltype(auto) basis() const + { + return std::get<0>(forms_).template basis<k>(); + } + + auto& forms() + { + return forms_; + } + + const auto& forms() const + { + return forms_; + } + + private: + std::tuple<Form0, Forms...> forms_; + }; + + + +// template<class... Forms> +// SumForm(Forms...) -> SumForm<Forms...>; + +// template<class... Forms> +// SumForm(SumForm<Forms...>) -> SumForm<Forms...>; + + + + template<class L, class R, + std::enable_if_t<isForm_v<L> and isForm_v<R>, int> = 0> + auto sum(const L& l, const R& r) + { + return SumForm<L, R>(l, r); + } + + template<class L, class... Ri, + std::enable_if_t<isForm_v<L>, int> = 0> + auto sum(const L& l, const SumForm<Ri...>& r) + { + return std::apply([&](const auto&... ri) { + return SumForm<L, Ri...>(l, ri...); + }, r.forms()); + } + + template<class... Li, class R, + std::enable_if_t<isForm_v<R>, int> = 0> + auto sum(const SumForm<Li...>& l, const R& r) + { + return std::apply([&](const auto&... li) { + return SumForm<Li..., R>(li..., r); + }, l.forms()); + } + + template<class... Li, class... Rj> + auto sum(const SumForm<Li...>& l, const SumForm<Rj...>& r) + { + return std::apply([&](const auto&...li) { + return std::apply([&](const auto&...rj) { + return SumForm<Li..., Rj...>(li..., rj...); + }, r.forms()); + }, l.forms()); + } + + + + template<class L, class R, + std::enable_if_t<isForm_v<L> and isForm_v<R>, int> = 0> + auto operator+ (const L& l, const R& r) + { + static_assert(L::arity==R::arity, "The forms passed to operator+ do not have the same arity"); + return sum(l, r); + } + + template<class L, class R, + std::enable_if_t<isForm_v<L> and isForm_v<R>, int> = 0> + auto operator- (const L& l, const R& r) + { + static_assert(L::arity==R::arity, "The forms passed to operator- do not have the same arity"); + return sum(l, -r); + } + + + +} // namespace Dune::Fufem::Forms + + +#endif // DUNE_FUFEM_FORMS_SUMFORM_HH diff --git a/dune/fufem-forms/transformedform.hh b/dune/fufem-forms/transformedform.hh new file mode 100644 index 0000000..e240492 --- /dev/null +++ b/dune/fufem-forms/transformedform.hh @@ -0,0 +1,158 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FUFEM_FORMS_TRANSFORMEDFORM_HH +#define DUNE_FUFEM_FORMS_TRANSFORMEDFORM_HH + +#include <cstddef> +#include <type_traits> +#include <tuple> +#include <utility> + + + +namespace Dune::Fufem::Forms { + + + + template<class BaseForm, class Op> + class TransformedForm : public BaseForm + { + using Transformation = Op; + + public: + + using Element = typename BaseForm::Element; + using Coordinate = typename BaseForm::Coordinate; + + TransformedForm(const BaseForm& form) : + BaseForm(form), + transformation_() + {} + + TransformedForm(const BaseForm& form, const Transformation& transformation) : + BaseForm(form), + transformation_(transformation) + {} + + auto operator()(const Coordinate& x, std::size_t quadPointIndex) const + { + auto y = BaseForm::operator()(x, quadPointIndex); + return [=](const auto& args) { + return transformation_(y(args)); + }; + } + + auto baseForm() const + { + return (const BaseForm&)(*this); + } + + private: + Transformation transformation_; + }; + + + + template<class F, class Op> + auto transformRange(const F& f, const Op& op) + { + return TransformedForm(f, op); + } + + + template<class Form0, class... Forms> + class SumForm; + + template<class Op, class... Fi> + auto transformRange(const Dune::Fufem::Forms::SumForm<Fi...>& f, const Op& op) + { + return std::apply([&](const auto&...fi) { + return Dune::Fufem::Forms::SumForm(transformRange(fi, op) ...); + }, f.forms()); + } + + + + template<class Op> + class RangeOperator + { + public: + RangeOperator(const Op& op) : op_(op) {} + + template<class F, class... Args, + std::enable_if_t<isForm_v<F>, int> = 0> + auto operator()(F&& form, Args... args) const + { + auto captureOp = [op=op_, argTuple = std::tuple(args...)](auto&& arg) { + return std::apply([&](auto&&... args) { + return op(std::forward<decltype(arg)>(arg), args...); + }, argTuple); + }; + return transformRange(std::forward<F>(form), captureOp); + } + + template<class Arg0, class... Args, + std::enable_if_t<not isForm_v<Arg0>, int> = 0> + auto operator()(Arg0&& arg0, Args&&... args) const + { + return op_(std::forward<Arg0>(arg0), std::forward<Args...>(args...)); + } + + private: + Op op_; + }; + + + + namespace Impl { + + struct TransposeOp { + template<class K, int n, int m> + auto operator()(const Dune::FieldMatrix<K,n,m>& x) const + { + Dune::FieldMatrix<K,m,n> y; + for(std::size_t i=0; i<x.N(); ++i) + for(std::size_t j=0; j<x.M(); ++j) + y[j][i] = x[i][j]; + return y; + } + }; + + } // namespace Impl + + + + template<class F> + using TransposedForm = TransformedForm<F, Dune::Fufem::Forms::Impl::TransposeOp>; + + template<class F, + std::enable_if_t<isForm_v<F>, int> = 0> + auto transpose(const F& f) + { + return TransposedForm<F>(f); + } + + + + template<class F, + std::enable_if_t<isForm_v<F>, int> = 0> + auto operator- (const F& f) + { + return transformRange(f, [](const auto& x) { return -x; }); + } + + + + template<class F, + std::enable_if_t<isForm_v<F>, int> = 0> + auto D(const F& f, std::size_t k) + { + return transformRange(grad(f), [k](auto&& g) { return g[k]; }); + } + + + +} // namespace Dune::Fufem::Forms + + +#endif // DUNE_FUFEM_FORMS_TRANSFORMEDFORM_HH diff --git a/dune/fufem-forms/unaryforms.hh b/dune/fufem-forms/unaryforms.hh new file mode 100644 index 0000000..ce2b992 --- /dev/null +++ b/dune/fufem-forms/unaryforms.hh @@ -0,0 +1,400 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FUFEM_FORMS_UNARYFORMS_HH +#define DUNE_FUFEM_FORMS_UNARYFORMS_HH + +#include <cstddef> +#include <type_traits> +#include <tuple> +#include <vector> + +#include <dune/common/typetraits.hh> +#include <dune/common/fvector.hh> +#include <dune/common/fmatrix.hh> +#include <dune/common/indices.hh> + +#include <dune/typetree/childextraction.hh> +#include <dune/typetree/treepath.hh> + +#include <dune/functions/functionspacebases/subspacebasis.hh> + +#include <dune/fufem/quadraturerules/quadraturerulecache.hh> + +#include <dune/fufem-forms/baseclass.hh> +#include <dune/fufem-forms/transformedform.hh> + + +namespace Dune::Fufem::Forms { + + + + template<std::size_t k> + struct UnaryForm : public Form + { + static constexpr std::size_t argIndex = k; + static constexpr std::size_t arity = 1; + + template<class Element> + void bindToElement(const Element&) {} + }; + + + + // Forward declarations + + template<class B, class TP, std::size_t argIndex> + class FEFunctionJacobianForm; + + template<class B, class TP, std::size_t argIndex> + class FEFunctionDivergenceForm; + + template<class B, class TP, std::size_t argIndex> + class FEFunctionForm; + + + + // Linear map on FE-space that maps v onto itself + template<class B, class TP, std::size_t argIndex> + class FEFunctionForm : public UnaryForm<argIndex> + { + public: + using Basis = B; + using TreePath = TP; + using LocalView = typename Basis::LocalView; + using Tree = typename LocalView::Tree; + using Node = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; + + private: + using LeafNode = typename Dune::TypeTree::ChildForTreePath<Tree, Impl::LeafTreePath<Tree,TreePath>>; + + using Buffer = std::vector<typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::RangeType>; + using ValueCache = typename ShapeFunctionCache<Tree>::template NodalValueCache<LeafNode>; + + public: + + using Element = typename Basis::LocalView::Element; + using Coordinate = typename Element::Geometry::LocalCoordinate; + + FEFunctionForm(const Basis& basis, const TreePath& treePath) : + basis_(basis), + treePath_(treePath), + leafTreePath_(Impl::leafTreePath<Tree>(treePath)), + leafNode_(nullptr) + {} + + auto quadratureRuleKey() const + { + return QuadratureRuleKey(leafNode_->finiteElement()); + } + + void bindToLocalView(const LocalView& localView) + { + leafNode_ = & Dune::TypeTree::child(localView.tree(), leafTreePath_); + } + + template<class Cache> + void bindToCache(Cache& cache) + { + valueCache_ = &(cache.getValues(leafTreePath_)); + } + + template<int k> + const auto& treePath() const + { + return treePath_; + } + + template<int k> + const auto& basis() const + { + return basis_; + } + + auto operator()(const Coordinate& x, std::size_t quadPointIndex) const + { + const auto& values = (*valueCache_)[quadPointIndex]; + if constexpr(Node::isLeaf) + return [&](const auto& args) { + return values[std::get<argIndex>(args)]; + }; + else if constexpr(Node::isPower and Node::ChildType::isLeaf) + return [&](const auto& args) { + using Field = typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::RangeFieldType; + constexpr auto components = Node::degree(); + auto componentIndex = std::get<argIndex>(args) / leafNode_->size(); + auto shapeFunctionIndex = std::get<argIndex>(args) % leafNode_->size(); + auto result = Dune::FieldVector<Field,components>(0); + result[componentIndex] = values[shapeFunctionIndex]; + return result; + }; + } + + friend auto jacobian(const FEFunctionForm& f) + { + return FEFunctionJacobianForm<Basis, TreePath, argIndex>(f.basis_, f.treePath_); + } + + friend auto gradient(const FEFunctionForm& f) + { + return Fufem::Forms::transpose(jacobian(f)); + } + + friend auto grad(const FEFunctionForm& f) + { + return Fufem::Forms::transpose(jacobian(f)); + } + + friend auto divergence(const FEFunctionForm& f) + { + return FEFunctionDivergenceForm<Basis, TreePath, argIndex>(f.basis_, f.treePath_); + } + + friend auto div(const FEFunctionForm& f) + { + return divergence(f); + } + + template<std::size_t i> + auto childForm(Dune::index_constant<i> childIndex) const + { + auto childTP = Dune::TypeTree::push_back(treePath_, childIndex); + return FEFunctionForm<Basis, decltype(childTP), argIndex>(basis_, childTP); + } + + private: + const Basis& basis_; + const TreePath treePath_; + Impl::LeafTreePath<Tree,TreePath> leafTreePath_; + const LeafNode* leafNode_; + mutable Buffer evaluationBuffer_; + const ValueCache* valueCache_; + }; + + + + // Linear map on FE-space that maps v onto its Jacobian + template<class B, class TP, std::size_t argIndex> + class FEFunctionJacobianForm : public UnaryForm<argIndex> + { + public: + using Basis = B; + using TreePath = TP; + using LocalView = typename Basis::LocalView; + using Tree = typename LocalView::Tree; + using Node = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; + + private: + using LeafNode = typename Dune::TypeTree::ChildForTreePath<Tree, Impl::LeafTreePath<Tree,TreePath>>; + + using Buffer = std::vector<typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::JacobianType>; + using GlobalJacobianCache = typename ShapeFunctionCache<Tree>::template NodalGlobalJacobianCache<LeafNode>; + + public: + + using Element = typename Basis::LocalView::Element; + using Coordinate = typename Element::Geometry::LocalCoordinate; + + FEFunctionJacobianForm(const Basis& basis, const TreePath& treePath) : + basis_(basis), + treePath_(treePath), + leafTreePath_(Impl::leafTreePath<Tree>(treePath)), + leafNode_(nullptr) + {} + + auto quadratureRuleKey() const + { + return QuadratureRuleKey(leafNode_->finiteElement()).derivative(); + } + + void bindToLocalView(const LocalView& localView) + { + leafNode_ = & Dune::TypeTree::child(localView.tree(), leafTreePath_); + } + + template<class Cache> + void bindToCache(Cache& cache) + { + globalJacobianCache_ = &(cache.getGlobalJacobians(leafTreePath_)); + } + + template<int k> + const auto& treePath() const + { + return treePath_; + } + + template<int k> + const auto& basis() const + { + return basis_; + } + + auto operator()(const Coordinate& x, std::size_t quadPointIndex) const + { + const auto& globalJacobians = (*globalJacobianCache_)[quadPointIndex]; + if constexpr(Node::isLeaf) + return [&](const auto& args) { + return globalJacobians[std::get<argIndex>(args)]; + }; + else if constexpr(Node::isPower and Node::ChildType::isLeaf) + return [&](const auto& args) { + using F = typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::RangeFieldType; + static constexpr auto components = Node::degree(); + static constexpr auto dimension = LeafNode::FiniteElement::Traits::LocalBasisType::Traits::dimDomain; + auto componentIndex = std::get<argIndex>(args) / leafNode_->size(); + auto shapeFunctionIndex = std::get<argIndex>(args) % leafNode_->size(); + auto result = Dune::FieldMatrix<F,components,dimension>(0); + result[componentIndex] = globalJacobians[shapeFunctionIndex][0]; + return result; + }; + } + + template<std::size_t i> + auto childForm(Dune::index_constant<i> childIndex) const + { + auto childTP = Dune::TypeTree::push_back(treePath_, childIndex); + return FEFunctionJacobianForm<Basis, decltype(childTP), argIndex>(basis_, childTP); + } + + private: + const Basis& basis_; + const TreePath treePath_; + Impl::LeafTreePath<Tree,TreePath> leafTreePath_; + const LeafNode* leafNode_; + mutable Buffer evaluationBuffer_; + const GlobalJacobianCache* globalJacobianCache_; + }; + + + + // Linear map on FE-space that maps v onto its divergence + template<class B, class TP, std::size_t argIndex> + class FEFunctionDivergenceForm : public UnaryForm<argIndex> + { + public: + using Basis = B; + using TreePath = TP; + using LocalView = typename Basis::LocalView; + using Tree = typename LocalView::Tree; + using Node = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; + + private: + using LeafNode = typename Dune::TypeTree::ChildForTreePath<Tree, Impl::LeafTreePath<Tree,TreePath>>; + + using Buffer = std::vector<typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::JacobianType>; + using GlobalJacobianCache = typename ShapeFunctionCache<Tree>::template NodalGlobalJacobianCache<LeafNode>; + + public: + + using Element = typename Basis::LocalView::Element; + using Coordinate = typename Element::Geometry::LocalCoordinate; + + FEFunctionDivergenceForm(const Basis& basis, const TreePath& treePath) : + basis_(basis), + treePath_(treePath), + leafTreePath_(Impl::leafTreePath<Tree>(treePath)), + leafNode_(nullptr) + {} + + auto quadratureRuleKey() const + { + return QuadratureRuleKey(leafNode_->finiteElement()).derivative(); + } + + void bindToLocalView(const LocalView& localView) + { + leafNode_ = & Dune::TypeTree::child(localView.tree(), leafTreePath_); + } + + template<class Cache> + void bindToCache(Cache& cache) + { + globalJacobianCache_ = &(cache.getGlobalJacobians(leafTreePath_)); + } + + template<int k> + const auto& treePath() const + { + return treePath_; + } + + template<int k> + const auto& basis() const + { + return basis_; + } + + auto operator()(const Coordinate& x, std::size_t quadPointIndex) const + { + const auto& globalJacobians = (*globalJacobianCache_)[quadPointIndex]; + if constexpr(Node::isLeaf) + return [&](const auto& args) { + using Field = typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::RangeFieldType; + constexpr auto dimension = LeafNode::FiniteElement::Traits::LocalBasisType::Traits::dimDomain; + auto div = Field{0}; + for(std::size_t i=0; i<dimension; ++i) + div += globalJacobians[std::get<argIndex>(args)][i][i]; + return div; + }; + else if constexpr(Node::isPower and Node::ChildType::isLeaf) + return [&](const auto& args) { + // Here we should compute the trace of the global Jacobian J + // that we would need to build first. But since only the + // componentIndex'th row of J is nonzero, we can simply + // return the diagonal entry of this row which coincides + // with the componentIndex'th entry of the gradient of + // the respective scalar basis function. + auto componentIndex = std::get<argIndex>(args) / leafNode_->size(); + auto shapeFunctionIndex = std::get<argIndex>(args) % leafNode_->size(); + return globalJacobians[shapeFunctionIndex][0][componentIndex]; + }; + } + + private: + const Basis& basis_; + const TreePath treePath_; + Impl::LeafTreePath<Tree,TreePath> leafTreePath_; + const LeafNode* leafNode_; + mutable Buffer evaluationBuffer_; + const GlobalJacobianCache* globalJacobianCache_; + }; + + + + + template<class Basis> + auto testFunction(const Basis& basis) + { + using RootBasis = std::decay_t<decltype(basis.rootBasis())>; + return FEFunctionForm<RootBasis, typename Basis::PrefixPath, 0>(basis.rootBasis(), basis.prefixPath()); + } + + template<class Basis, class... Args, std::enable_if_t<(sizeof...(Args)>0), int> = 0> + auto testFunction(const Basis& basis, Args&&... args) + { + return testFunction(Dune::Functions::subspaceBasis(basis, args...)); + } + + + + + template<class Basis> + auto trialFunction(const Basis& basis) + { + using RootBasis = std::decay_t<decltype(basis.rootBasis())>; + return FEFunctionForm<RootBasis, typename Basis::PrefixPath, 1>(basis.rootBasis(), basis.prefixPath()); + } + + template<class Basis, class... Args, std::enable_if_t<(sizeof...(Args)>0), int> = 0> + auto trialFunction(const Basis& basis, Args&&... args) + { + return trialFunction(Dune::Functions::subspaceBasis(basis, args...)); + } + + + +} // namespace Dune::Fufem::Forms + + + +#endif // DUNE_FUFEM_FORMS_UNARYFORMS_HH diff --git a/src/fracture.cc b/src/fracture.cc index ebcf7f7..2f9417e 100644 --- a/src/fracture.cc +++ b/src/fracture.cc @@ -167,7 +167,7 @@ int main (int argc, char *argv[]) try auto displacementBasis = Dune::Functions::subspaceBasis(basis, _0); auto damageBasis = Dune::Functions::subspaceBasis(basis, _1); - auto trace = FormOperator([](const auto& M) { + auto trace = RangeOperator([](const auto& M) { double tr = 0; for (int ii=0; ii<dim; ii++) tr += M[ii][ii]; -- GitLab