diff --git a/src/amdis/FileWriter.hpp b/src/amdis/FileWriter.hpp index ff734622af2e36a5947b59f972ca8f3b2bdeb12f..6f6488af74291444aa7693d4a0935db3961f05ea 100644 --- a/src/amdis/FileWriter.hpp +++ b/src/amdis/FileWriter.hpp @@ -12,7 +12,7 @@ #include <amdis/Initfile.hpp> #include <amdis/common/Size.hpp> #include <amdis/common/ValueCategory.hpp> -#include <amdis/gridfunctions/DOFVectorView.hpp> +#include <amdis/gridfunctions/DiscreteFunction.hpp> #include <amdis/io/FileWriterInterface.hpp> #include <amdis/utility/Filesystem.hpp> @@ -43,14 +43,17 @@ namespace AMDiS constexpr std::size_t VTKFieldSize = Size<Range>; - template <class GlobalBasis, class RangeType, class TreePath> + template <class GB, class VT, class TP> class FileWriter : public FileWriterInterface { - private: // typedefs and static constants + using GlobalBasis = GB; + using ValueType = VT; + using TreePath = TP; + + private: using GridView = typename GlobalBasis::GridView; - using Vector = DOFVectorConstView<GlobalBasis,RangeType,TreePath>; - using Range = typename Vector::Range; + using Range = typename DiscreteFunction<GB,VT,TP>::Range; /// Dimension of the mesh static constexpr int dim = GridView::dimension; @@ -60,33 +63,32 @@ namespace AMDiS public: /// Constructor. - FileWriter(std::string const& baseName, - Vector const& dofvector) - : FileWriterInterface(baseName) - , dofvector_(dofvector) + FileWriter(std::string const& name, DiscreteFunction<GB,VT,TP> const& discreteFct) + : FileWriterInterface(name) + , discreteFct_(discreteFct) , animation_(false) { - Parameters::get(baseName + "->ParaView animation", animation_); + Parameters::get(name + "->ParaView animation", animation_); - int m = Parameters::get<int>(baseName + "->ParaView mode").value_or(0); + int m = Parameters::get<int>(name + "->ParaView mode").value_or(0); mode_ = (m == 0 ? Dune::VTK::ascii : Dune::VTK::appendedraw); - init(baseName, ValueCategory_t<Range>{}); + init(name, ValueCategory_t<Range>{}); } template <class ValueCat> void init(std::string const& baseName, ValueCat) { - int subSampling = Parameters::get<int>(baseName + "->subsampling").value_or(0); - if (subSampling > 0) - vtkWriter_ = std::make_shared<Dune::SubsamplingVTKWriter<GridView>>(gridView(), subSampling); + auto subSampling = Parameters::get<int>(baseName + "->subsampling"); + if (subSampling) + vtkWriter_ = std::make_shared<Dune::SubsamplingVTKWriter<GridView>>(gridView(), subSampling.value()); else vtkWriter_ = std::make_shared<Dune::VTKWriter<GridView>>(gridView()); if (animation_) vtkSeqWriter_ = std::make_shared<Dune::VTKSequenceWriter<GridView>>(vtkWriter_, filename_, dir_, ""); - vtkWriter_->addVertexData(dofvector_, Dune::VTK::FieldInfo(name_, VTKFieldType<Range>, VTKFieldSize<Range>)); + vtkWriter_->addVertexData(discreteFct_, Dune::VTK::FieldInfo(name_, VTKFieldType<Range>, VTKFieldSize<Range>)); } void init(std::string const&, tag::unknown) {} @@ -105,11 +107,11 @@ namespace AMDiS protected: GridView const& gridView() const { - return dofvector_.basis().gridView(); + return discreteFct_.basis().gridView(); } private: - Vector dofvector_; + DiscreteFunction<GB,VT,TP> discreteFct_; std::shared_ptr<Dune::VTKWriter<GridView>> vtkWriter_; std::shared_ptr<Dune::VTKSequenceWriter<GridView>> vtkSeqWriter_; @@ -122,12 +124,12 @@ namespace AMDiS }; - template <class GlobalBasis, class Range, class TreePath> - std::shared_ptr<FileWriter<GlobalBasis,Range,TreePath>> - makeFileWriterPtr(std::string baseName, - DOFVectorConstView<GlobalBasis,Range,TreePath> const& dofvector) + template <class GlobalBasis, class ValueType, class TreePath> + std::shared_ptr<FileWriter<GlobalBasis,ValueType,TreePath>> + makeFileWriterPtr(std::string const& name, + DiscreteFunction<GlobalBasis,ValueType,TreePath> const& discreteFct) { - return std::make_shared<FileWriter<GlobalBasis,Range,TreePath>>(baseName, dofvector); + return std::make_shared<FileWriter<GlobalBasis,ValueType,TreePath>>(name, discreteFct); } } // end namespace AMDiS diff --git a/src/amdis/ProblemStat.hpp b/src/amdis/ProblemStat.hpp index 20c212d681e35ff0fa074066167796d893fed1ae..5a737a44d2aa2bc6c66bc6cfb8398455417b0aa2 100644 --- a/src/amdis/ProblemStat.hpp +++ b/src/amdis/ProblemStat.hpp @@ -32,6 +32,7 @@ #include <amdis/common/Utility.hpp> #include <amdis/GridFunctions.hpp> +#include <amdis/gridfunctions/DiscreteFunction.hpp> #include <amdis/gridfunctions/DOFVectorView.hpp> #include <amdis/io/FileWriterInterface.hpp> @@ -198,7 +199,7 @@ namespace AMDiS auto getSolution(TreePath const& path = {}) const { auto&& tp = makeTreePath(path); - return makeDOFVectorView(*solution_, tp); + return makeDiscreteFunction(*solution_, tp); } diff --git a/src/amdis/gridfunctions/CMakeLists.txt b/src/amdis/gridfunctions/CMakeLists.txt index a2b00db043f10c3680ffcfbb29aeb8a69fc6e4c7..328773dbb331542870e5923ecb608e05839620d7 100644 --- a/src/amdis/gridfunctions/CMakeLists.txt +++ b/src/amdis/gridfunctions/CMakeLists.txt @@ -5,8 +5,9 @@ install(FILES ConstantGridFunction.hpp CoordsGridFunction.hpp DerivativeGridFunction.hpp + DiscreteFunction.hpp + DiscreteFunction.inc.hpp DOFVectorView.hpp - DOFVectorView.inc.hpp FunctorGridFunction.hpp GridFunctionConcepts.hpp Integrate.hpp diff --git a/src/amdis/gridfunctions/DOFVectorView.hpp b/src/amdis/gridfunctions/DOFVectorView.hpp index 86b52e7e7771030973eeae3b3a83932e9672cd47..734c5e1ebc35037d2a13f252b35f9dae9b972e04 100644 --- a/src/amdis/gridfunctions/DOFVectorView.hpp +++ b/src/amdis/gridfunctions/DOFVectorView.hpp @@ -1,348 +1,132 @@ #pragma once -#include <vector> - -#include <dune/common/std/optional.hh> -#include <dune/functions/common/defaultderivativetraits.hh> -#include <dune/functions/common/treedata.hh> -#include <dune/functions/functionspacebases/defaultnodetorangemap.hh> -#include <dune/functions/functionspacebases/flatvectorview.hh> -#include <dune/functions/gridfunctions/gridviewentityset.hh> -#include <dune/typetree/childextraction.hh> - #include <amdis/GridFunctions.hpp> -#include <amdis/utility/FiniteElementType.hpp> +#include <amdis/gridfunctions/DiscreteFunction.hpp> namespace AMDiS { - /** - * \addtogroup GridFunctions - * @{ - **/ - - template <class GlobalBasisType, class RangeType, class TreePathType> - class DOFVectorConstView + /// A mutable view on the subspace of a DOFVector, \relates DiscreteFunction + template <class GB, class VT, class TP> + class DOFVectorView + : public DiscreteFunction<GB, VT, TP> { - public: - using GlobalBasis = GlobalBasisType; - using TreePath = TreePathType; - - using Tree = typename GlobalBasis::LocalView::Tree; - using SubTree = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; - using NodeToRangeEntry = Dune::Functions::DefaultNodeToRangeMap<SubTree>; - - using GridView = typename GlobalBasis::GridView; - using EntitySet = Dune::Functions::GridViewEntitySet<GridView, 0>; - - using Domain = typename EntitySet::GlobalCoordinate; - using Range = RangeType_t<SubTree>; - static_assert(std::is_arithmetic<RangeType>::value, ""); - // Don't know how to determine Range with non-trivial RangeType - - using RawSignature = typename Dune::Functions::SignatureTraits<Range(Domain)>::RawSignature; - using DerivativeTraits = Dune::Functions::DefaultDerivativeTraits<RawSignature>; - using DerivativeRange = typename DerivativeTraits::Range; - - using LocalDomain = typename EntitySet::LocalCoordinate; - using Element = typename EntitySet::Element; - using Geometry = typename Element::Geometry; - - enum { hasDerivative = false }; - - public: // a local view on the gradients - - /// A LocalFunction representing the derivative of the DOFVector - class GradientLocalFunction - { - public: - using Domain = LocalDomain; - using Range = DerivativeRange; - - enum { hasDerivative = false }; - - private: - using LocalView = typename GlobalBasis::LocalView; - - template <class LeafNode> - using LocalBasisJacobian = typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::JacobianType; - - template <class Node> - using NodeData = typename std::vector<LocalBasisJacobian<Node>>; - - using ReferenceGradientContainer = Dune::Functions::TreeData<SubTree, NodeData, true>; - - public: - GradientLocalFunction(DOFVectorConstView const& globalFunction) - : globalFunction_(&globalFunction) - , localView_(globalFunction_->basis().localView()) - , subTree_(&Dune::TypeTree::child(localView_.tree(), globalFunction_->treePath())) - { - referenceGradientContainer_.init(*subTree_); - } - - void bind(Element const& element) - { - localView_.bind(element); - geometry_.emplace(element.geometry()); - bound_ = true; - } - - void unbind() - { - localView_.unbind(); - geometry_.reset(); - bound_ = false; - } - - /// Evaluate Gradient at bound element in local coordinates - Range operator()(Domain const& x) const; - - friend int order(GradientLocalFunction const& self) - { - assert( self.bound_ ); - return std::max(0, polynomialDegree(*self.subTree_)-1); - } - - /// Return the bound element - Element const& localContext() const - { - assert( bound_ ); - return localView_.element(); - } - - private: - DOFVectorConstView const* globalFunction_; - LocalView localView_; - - SubTree const* subTree_; - mutable ReferenceGradientContainer referenceGradientContainer_; - - Dune::Std::optional<Geometry> geometry_; - bool bound_ = false; - }; - - - public: // a local view on the values - - /// A LocalFunction, i.e., an element local view on the DOFVector - class LocalFunction - { - public: - using Domain = typename DOFVectorConstView::LocalDomain; - using Range = typename DOFVectorConstView::Range; - - enum { hasDerivative = true }; - - private: - using LocalView = typename GlobalBasis::LocalView; - - template <class LeafNode> - using LocalBasisRange = RangeType_t<LeafNode>; - // = typename LeafNode::FiniteElement::Traits::LocalBasisType::Traits::RangeType; - - template <class Node> - using NodeData = typename std::vector<LocalBasisRange<Node>>; - - using ShapeFunctionValueContainer = Dune::Functions::TreeData<SubTree, NodeData, true>; - - public: - LocalFunction(DOFVectorConstView const& globalFunction) - : globalFunction_(&globalFunction) - , localView_(globalFunction_->basis().localView()) - , subTree_(&Dune::TypeTree::child(localView_.tree(), globalFunction_->treePath())) - { - shapeFunctionValueContainer_.init(*subTree_); - } - - void bind(Element const& element) - { - localView_.bind(element); - bound_ = true; - } - - void unbind() - { - localView_.unbind(); - bound_ = false; - } - - /// Evaluate LocalFunction at bound element in local coordinates - Range operator()(Domain const& x) const; - - /// \brief Create a LocalFunction representing the gradient. \relates GradientLocalFunction - friend GradientLocalFunction derivative(LocalFunction const& localFunction) - { - static_assert(isValidRange<DerivativeTraits>(),"Derivative of DOFVector not defined."); - return GradientLocalFunction{*localFunction.globalFunction_}; - } - - friend int order(LocalFunction const& self) - { - assert( self.bound_ ); - return polynomialDegree(*self.subTree_); - } - - /// Return the bound element - Element const& localContext() const - { - assert( bound_ ); - return localView_.element(); - } - - private: - DOFVectorConstView const* globalFunction_; - LocalView localView_; - SubTree const* subTree_; - - mutable ShapeFunctionValueContainer shapeFunctionValueContainer_; - - bool bound_ = false; - }; + using Self = DOFVectorView; + using Super = DiscreteFunction<GB, VT, TP>; + using GlobalBasis = GB; + using TreePath = TP; public: - /// Constructor. Stores a pointer to the dofVector and a copy of the treePath. - DOFVectorConstView(DOFVector<GlobalBasis,RangeType> const& dofVector, TreePath const& treePath) - : dofVector_(&dofVector) - , treePath_(treePath) - , entitySet_(dofVector.basis().gridView()) - , nodeToRangeEntry_(Dune::Functions::makeDefaultNodeToRangeMap(dofVector.basis(), treePath)) + /// Constructor forwards to the treePath constructor, with empty TreePath + DOFVectorView(DOFVector<GB,VT>& dofVector) + : DOFVectorView{dofVector, Dune::TypeTree::hybridTreePath()} {} - /// Evaluate the view on this DOFVector in global coordinates - Range operator()(Domain const& x) const - { - error_exit("Not implemented."); - return Range(0); - } - - /// \brief Create a local function for this view on the DOFVector. \relates LocalFunction - friend LocalFunction localFunction(DOFVectorConstView const& self) - { - return LocalFunction{self}; - } - - EntitySet const& entitySet() const - { - return entitySet_; - } - - public: - /// Return global basis - GlobalBasis const& basis() const - { - return dofVector_->basis(); - } - - /// Return treePath associated with this view - TreePath const& treePath() const - { - return treePath_; - } - - /// Return const coefficient vector - DOFVector<GlobalBasis,RangeType> const& coefficients() const - { - return *dofVector_; - } - - protected: - DOFVector<GlobalBasis,RangeType> const* dofVector_; - TreePath const treePath_; - - EntitySet entitySet_; - NodeToRangeEntry nodeToRangeEntry_; - }; - - - // A mutable version of DOFVectorView - template <class GlobalBasisType, class RangeType, class TreePathType> - class DOFVectorMutableView - : public DOFVectorConstView<GlobalBasisType, RangeType, TreePathType> - { - using Super = DOFVectorConstView<GlobalBasisType, RangeType, TreePathType>; - - using GlobalBasis = GlobalBasisType; - using TreePath = TreePathType; - - public: /// Constructor. Stores a pointer to the mutable `dofvector`. - DOFVectorMutableView(DOFVector<GlobalBasis,RangeType>& dofVector, TreePath const& treePath) + DOFVectorView(DOFVector<GB,VT>& dofVector, TP const& treePath) : Super(dofVector, treePath) , mutableDofVector_(&dofVector) {} public: - /// Interpolation of GridFunction to DOFVector + /// \brief Interpolation of GridFunction to DOFVector, assuming that there is no + /// reference to this DOFVector in the expression. + /** + * **Example:** + * ``` + * auto v = makeDOFVectorView(prob.getSolutionVector(),0); + * v.interpolate([](auto const& x) { return x[0]; }); + * ``` + **/ template <class Expr> - DOFVectorMutableView& interpolate(Expr&& expr) + void interpolate_noalias(Expr&& expr) { - auto const& basis = Super::basis(); - auto const& treePath = Super::treePath(); + auto const& basis = this->basis(); + auto const& treePath = this->treePath(); auto&& gridFct = makeGridFunction(std::forward<Expr>(expr), basis.gridView()); - DOFVector<GlobalBasis,RangeType> tmp(*mutableDofVector_); - Dune::Functions::interpolate(basis, treePath, tmp, std::forward<decltype(gridFct)>(gridFct)); + Dune::Functions::interpolate(basis, treePath, coefficients(), + std::forward<decltype(gridFct)>(gridFct)); + } + + /// \brief Interpolation of GridFunction to DOFVector + /** + * **Example:** + * ``` + * auto v = makeDOFVectorView(prob.getSolutionVector(),0); + * v.interpolate(v + [](auto const& x) { return x[0]; }); + * ``` + * Allows to have a reference to the DOFVector in the expression, e.g. as + * \ref DiscreteFunction or \ref gradientAtQP() of a DiscreteFunction. + **/ + template <class Expr> + void interpolate(Expr&& expr) + { + // create temporary copy of data + DOFVector<GB,VT> tmp(coefficients()); + Self tmpView{tmp, this->treePath()}; + tmpView.interpolate_noalias(std::forward<Expr>(expr)); // move data from temporary vector into stored DOFVector - mutableDofVector_->vector() = std::move(tmp.vector()); + coefficients().vector() = std::move(tmp.vector()); + } + + /// \brief Interpolation of GridFunction to DOFVector, alias to \ref interpolate() + template <class Expr> + DOFVectorView& operator<<(Expr&& expr) + { + interpolate(expr); return *this; } + /// \brief interpolate `(*this) + expr` to DOFVector template <class Expr> - DOFVectorMutableView& operator<<(Expr&& expr) + DOFVectorView& operator+=(Expr&& expr) { - return interpolate(expr); + interpolate((*this) + expr); + return *this; } + /// \brief interpolate `(*this) - expr` to DOFVector + template <class Expr> + DOFVectorView& operator-=(Expr&& expr) + { + interpolate((*this) - expr); + return *this; + } /// Return the mutable DOFVector - DOFVector<GlobalBasis,RangeType>& coefficients() { return *mutableDofVector_; } + DOFVector<GB,VT>& coefficients() { return *mutableDofVector_; } /// Return the const DOFVector using Super::coefficients; protected: - DOFVector<GlobalBasis,RangeType>* mutableDofVector_; + DOFVector<GB,VT>* mutableDofVector_; }; - /** @} **/ - - -#ifndef DOXYGEN - // A Generator for a const \ref DOFVectorView. - template <class GlobalBasis, class RangeType, class TreePath> - auto makeDOFVectorView(DOFVector<GlobalBasis, RangeType> const& dofVector, TreePath const& treePath) - { - return DOFVectorConstView<GlobalBasis, RangeType, TreePath>{dofVector, treePath}; - } - - // A Generator for a mutable \ref DOFVectorView. - template <class GlobalBasis, class RangeType, class TreePath> - auto makeDOFVectorView(DOFVector<GlobalBasis, RangeType>& dofVector, TreePath const& treePath) - { - return DOFVectorMutableView<GlobalBasis, RangeType, TreePath>{dofVector, treePath}; - } +#if DUNE_HAVE_CXX_CLASS_TEMPLATE_ARGUMENT_DEDUCTION + // Deduction guide for DOFVectorView class + template <class GlobalBasis, class ValueType> + DOFVectorView(DOFVector<GlobalBasis, ValueType>& dofVector) + -> DOFVectorView<GlobalBasis, ValueType, Dune::TypeTree::HybridTreePath<>>; +#endif - // A Generator for a const \ref DOFVectorView. - template <class GlobalBasis, class RangeType> - auto makeDOFVectorView(DOFVector<GlobalBasis, RangeType> const& dofVector) + /// A Generator for a mutable \ref DOFVectorView + template <class GlobalBasis, class ValueType, class TreePath> + auto makeDOFVectorView(DOFVector<GlobalBasis, ValueType>& dofVector, TreePath const& treePath) { - auto treePath = Dune::TypeTree::hybridTreePath(); - return DOFVectorConstView<GlobalBasis, RangeType, decltype(treePath)>{dofVector, treePath}; + return DOFVectorView<GlobalBasis, ValueType, TreePath>{dofVector, treePath}; } - // A Generator for a mutable \ref DOFVectorView. - template <class GlobalBasis, class RangeType> - auto makeDOFVectorView(DOFVector<GlobalBasis, RangeType>& dofVector) + /// A Generator for a mutable \ref DOFVectorView + template <class GlobalBasis, class ValueType> + auto makeDOFVectorView(DOFVector<GlobalBasis, ValueType>& dofVector) { auto treePath = Dune::TypeTree::hybridTreePath(); - return DOFVectorMutableView<GlobalBasis, RangeType, decltype(treePath)>{dofVector, treePath}; + return DOFVectorView<GlobalBasis, ValueType, Dune::TypeTree::HybridTreePath<>>{dofVector, treePath}; } -#endif } // end namespace AMDiS - -#include "DOFVectorView.inc.hpp" diff --git a/src/amdis/gridfunctions/DOFVectorView.inc.hpp b/src/amdis/gridfunctions/DOFVectorView.inc.hpp deleted file mode 100644 index 939b89a015e10a60317f97599ff5a834dcd38d8e..0000000000000000000000000000000000000000 --- a/src/amdis/gridfunctions/DOFVectorView.inc.hpp +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -#include <amdis/common/FieldMatVec.hpp> -#include <amdis/LocalBasisCache.hpp> - -namespace AMDiS { - -template <class GB, class RT, class TP> -typename DOFVectorConstView<GB,RT,TP>::Range DOFVectorConstView<GB,RT,TP>:: -LocalFunction::operator()(LocalDomain const& x) const -{ - assert( bound_ ); - auto y = Range(0); - - auto&& coefficients = *globalFunction_->dofVector_; - auto&& nodeToRangeEntry = globalFunction_->nodeToRangeEntry_; - forEachLeafNode_(*subTree_, [&,this](auto const& node, auto const& tp) - { - auto&& fe = node.finiteElement(); - auto&& localBasis = fe.localBasis(); - - LocalBasisCache<std::decay_t<decltype(localBasis)>> localBasisCache(localBasis); - auto const& shapeFunctionValues = localBasisCache.evaluateFunction(localView_.element(),x); - - // Get range entry associated to this node - auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, y)); - - for (std::size_t i = 0; i < localBasis.size(); ++i) { - auto&& multiIndex = localView_.index(node.localIndex(i)); - - // Get coefficient associated to i-th shape function - auto c = Dune::Functions::flatVectorView(coefficients[multiIndex]); - - // Get value of i-th shape function - auto v = Dune::Functions::flatVectorView(shapeFunctionValues[i]); - - std::size_t dimC = c.size(); - std::size_t dimV = v.size(); - assert(dimC*dimV == std::size_t(re.size())); - for(std::size_t j = 0; j < dimC; ++j) { - auto&& c_j = c[j]; - for(std::size_t k = 0; k < dimV; ++k) - re[j*dimV + k] += c_j*v[k]; - } - } - }); - - return y; -} - - -template <class GB, class RT, class TP> -typename DOFVectorConstView<GB,RT,TP>::DerivativeRange DOFVectorConstView<GB,RT,TP>:: -GradientLocalFunction::operator()(LocalDomain const& x) const -{ - assert( bound_ ); - Range dy; - for (std::size_t j = 0; j < dy.size(); ++j) - dy[j] = 0; - - auto&& coefficients = *globalFunction_->dofVector_; - auto&& nodeToRangeEntry = globalFunction_->nodeToRangeEntry_; - forEachLeafNode_(*subTree_, [&,this](auto const& node, auto const& tp) - { - // TODO: may DOFVectorView::Range to FieldVector type if necessary - using LocalDerivativeTraits = Dune::Functions::DefaultDerivativeTraits<Dune::FieldVector<double,1>(Domain)>; - using GradientBlock = typename LocalDerivativeTraits::Range; - - auto&& fe = node.finiteElement(); - auto&& localBasis = fe.localBasis(); - - LocalBasisCache<std::decay_t<decltype(localBasis)>> localBasisCache(localBasis); - auto const& referenceGradients = localBasisCache.evaluateJacobian(localView_.element(),x); - - // The transposed inverse Jacobian of the map from the reference element to the element - auto&& jacobian = geometry_.value().jacobianInverseTransposed(x); - - // Compute the shape function gradients on the real element - std::vector<GradientBlock> gradients(referenceGradients.size()); - for (std::size_t i = 0; i < gradients.size(); ++i) - multiplies_ABt(referenceGradients[i], jacobian, gradients[i]); // D[phi] * J^(-1) -> grad - - // Get range entry associated to this node - auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, dy)); - - for (std::size_t i = 0; i < localBasis.size(); ++i) { - auto&& multiIndex = localView_.index(node.localIndex(i)); - - // Get coefficient associated to i-th shape function - auto c = Dune::Functions::flatVectorView(coefficients[multiIndex]); - - // Get value of i-th transformed reference gradient - auto grad = Dune::Functions::flatVectorView(gradients[i]); - - std::size_t dimC = c.size(); - std::size_t dimV = grad.size(); - assert(dimC*dimV == std::size_t(re.size())); - for(std::size_t j = 0; j < dimC; ++j) { - auto&& c_j = c[j]; - for(std::size_t k = 0; k < dimV; ++k) - re[j*dimV + k] += c_j*grad[k]; - } - } - }); - - return dy; -} - -} // end namespace AMDiS diff --git a/src/amdis/gridfunctions/DerivativeGridFunction.hpp b/src/amdis/gridfunctions/DerivativeGridFunction.hpp index e4c0adc7f64304342260e00b5f0aa3d5f4bee75c..c9b3811d32947e7c0beaeac09980024874c58864 100644 --- a/src/amdis/gridfunctions/DerivativeGridFunction.hpp +++ b/src/amdis/gridfunctions/DerivativeGridFunction.hpp @@ -3,6 +3,7 @@ #include <type_traits> #include <dune/functions/common/defaultderivativetraits.hh> +#include <dune/grid/utility/hierarchicsearch.hh> #include <amdis/gridfunctions/GridFunctionConcepts.hpp> @@ -52,11 +53,22 @@ namespace AMDiS static_assert(isValidRange<DerivativeTraits>(), "Derivative of GridFunction not defined"); } - /// NOTE: no global derivative available + /// Evaluate derivative in global coordinates. NOTE: expensive Range operator()(Domain const& x) const { - error_exit("Not implemented"); - return Range(0); + auto gv = entitySet().gridView(); + + using GridView = decltype(gv); + using Grid = typename GridView::Grid; + using IS = typename GridView::IndexSet; + + Dune::HierarchicSearch<Grid,IS> hsearch{gv.grid(), gv.indexSet()}; + + auto element = hsearch.findEntity(x); + auto geometry = element.geometry(); + auto localFct = derivative(localFunction(gridFct_)); + localFct.bind(element); + return localFct(geometry.local(x)); } /// Return the derivative-localFunction of the GridFunction. diff --git a/src/amdis/gridfunctions/DiscreteFunction.hpp b/src/amdis/gridfunctions/DiscreteFunction.hpp new file mode 100644 index 0000000000000000000000000000000000000000..71b2045e3fc8796c4ca50667e27731a62734d40f --- /dev/null +++ b/src/amdis/gridfunctions/DiscreteFunction.hpp @@ -0,0 +1,141 @@ +#pragma once + +#include <vector> + +#include <dune/common/std/optional.hh> +#include <dune/functions/common/defaultderivativetraits.hh> +#include <dune/functions/functionspacebases/defaultnodetorangemap.hh> +#include <dune/functions/functionspacebases/flatvectorview.hh> +#include <dune/functions/gridfunctions/gridviewentityset.hh> +#include <dune/typetree/childextraction.hh> + +#include <amdis/GridFunctions.hpp> +#include <amdis/utility/FiniteElementType.hpp> + +namespace AMDiS +{ + /// \brief A view on a subspace of a \ref DOFVector + /** + * \ingroup GridFunctions + * + * \tparam GB GlobalBasis type that models \ref Dune::Functions::Concept::GlobalBasis + * \tparam VT Coefficient type of the DOFVector + * \tparam TP TreePath type a realization of \ref Dune::TypeTree::HybridTreePath + **/ + template <class GB, class VT, class TP> + class DiscreteFunction + { + static_assert(std::is_arithmetic<VT>::value, ""); + + private: + using GlobalBasis = GB; + using TreePath = TP; + + using Tree = typename GlobalBasis::LocalView::Tree; + using SubTree = typename Dune::TypeTree::ChildForTreePath<Tree, TreePath>; + using NodeToRangeEntry = Dune::Functions::DefaultNodeToRangeMap<SubTree>; + + using GridView = typename GlobalBasis::GridView; + + public: + /// Set of entities the DiscreteFunction is defined on + using EntitySet = Dune::Functions::GridViewEntitySet<GridView, 0>; + + /// Global coordinates of the EntitySet + using Domain = typename EntitySet::GlobalCoordinate; + + /// Range type of this DiscreteFunction + using Range = RangeType_t<SubTree,VT>; + + /// \brief This GridFunction has no derivative function, it can be created + /// by \ref DiscreteGridFunction. + enum { hasDerivative = false }; + + public: + /// A LocalFunction representing the derivative of the DOFVector on a bound element + class GradientLocalFunction; + + /// A LocalFunction representign the value the DOFVector on a bound element + class LocalFunction; + + public: + /// Constructor forwards to the treePath constructor, with empty TreePath + DiscreteFunction(DOFVector<GB,VT> const& dofVector) + : DiscreteFunction{dofVector, Dune::TypeTree::hybridTreePath()} + {} + + /// Constructor. Stores a pointer to the dofVector and a copy of the treePath. + DiscreteFunction(DOFVector<GB,VT> const& dofVector, TP const& treePath) + : dofVector_(&dofVector) + , treePath_(treePath) + , entitySet_(dofVector.basis().gridView()) + , nodeToRangeEntry_(Dune::Functions::makeDefaultNodeToRangeMap(dofVector.basis(), treePath)) + {} + + /// \brief Evaluate DiscreteFunction in global coordinates. NOTE: expensive + Range operator()(Domain const& x) const; + + /// \brief Create a local function for this view on the DOFVector. \relates LocalFunction + friend LocalFunction localFunction(DiscreteFunction const& self) + { + return LocalFunction{self}; + } + + /// \brief Return a \ref Dune::Functions::GridViewEntitySet + EntitySet const& entitySet() const + { + return entitySet_; + } + + public: + /// \brief Return global basis bound to the DOFVector + GlobalBasis const& basis() const + { + return dofVector_->basis(); + } + + /// \brief Return treePath associated with this discrete function + TreePath const& treePath() const + { + return treePath_; + } + + /// \brief Return const coefficient vector + DOFVector<GB,VT> const& coefficients() const + { + return *dofVector_; + } + + protected: + DOFVector<GB,VT> const* dofVector_; + TreePath treePath_; + EntitySet entitySet_; + NodeToRangeEntry nodeToRangeEntry_; + }; + + +#if DUNE_HAVE_CXX_CLASS_TEMPLATE_ARGUMENT_DEDUCTION + // Deduction guide for DiscreteFunction class + template <class GlobalBasis, class ValueType> + DiscreteFunction(DOFVector<GlobalBasis, ValueType> const& dofVector) + -> DiscreteFunction<GlobalBasis, ValueType, Dune::TypeTree::HybridTreePath<>>; +#endif + + /// A Generator for a \ref DiscreteFunction + template <class GlobalBasis, class ValueType, class TreePath> + auto makeDiscreteFunction(DOFVector<GlobalBasis, ValueType> const& dofVector, TreePath const& treePath) + { + return DiscreteFunction<GlobalBasis, ValueType, TreePath>{dofVector, treePath}; + } + + /// A Generator for a \ref DiscreteFunction + template <class GlobalBasis, class ValueType> + auto makeDiscreteFunction(DOFVector<GlobalBasis, ValueType> const& dofVector) + { + auto treePath = Dune::TypeTree::hybridTreePath(); + return DiscreteFunction<GlobalBasis, ValueType, Dune::TypeTree::HybridTreePath<>>{dofVector, treePath}; + } + +} // end namespace AMDiS + +#include "DiscreteFunction.inc.hpp" diff --git a/src/amdis/gridfunctions/DiscreteFunction.inc.hpp b/src/amdis/gridfunctions/DiscreteFunction.inc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..730e4b82d30dd55733eb438efa376077ff4a83c4 --- /dev/null +++ b/src/amdis/gridfunctions/DiscreteFunction.inc.hpp @@ -0,0 +1,260 @@ +#pragma once + +#include <amdis/common/FieldMatVec.hpp> +#include <amdis/LocalBasisCache.hpp> + +#include <dune/grid/utility/hierarchicsearch.hh> + +namespace AMDiS { + +template <class GB, class VT, class TP> +class DiscreteFunction<GB,VT,TP>::LocalFunction +{ +public: + using Domain = typename EntitySet::LocalCoordinate; + using Range = typename DiscreteFunction::Range; + + enum { hasDerivative = true }; + +private: + using LocalView = typename GlobalBasis::LocalView; + using Element = typename EntitySet::Element; + using Geometry = typename Element::Geometry; + +public: + LocalFunction(DiscreteFunction const& globalFunction) + : globalFunction_(&globalFunction) + , localView_(globalFunction_->basis().localView()) + , subTree_(&child(localView_.tree(), globalFunction_->treePath())) + {} + + /// \brief Bind the LocalView to the element + void bind(Element const& element) + { + localView_.bind(element); + bound_ = true; + } + + /// \brief Unbind the LocalView from the element + void unbind() + { + localView_.unbind(); + bound_ = false; + } + + /// \brief Evaluate LocalFunction at bound element in local coordinates + Range operator()(Domain const& x) const; + + /// \brief Create a LocalFunction representing the gradient. \relates GradientLocalFunction + friend GradientLocalFunction derivative(LocalFunction const& localFunction) + { + return GradientLocalFunction{*localFunction.globalFunction_}; + } + + /// \brief The \ref polynomialDegree() of the LocalFunctions + friend int order(LocalFunction const& self) + { + assert( self.bound_ ); + return polynomialDegree(*self.subTree_); + } + + /// \brief Return the bound element + Element const& localContext() const + { + assert( bound_ ); + return localView_.element(); + } + +private: + DiscreteFunction const* globalFunction_; + LocalView localView_; + SubTree const* subTree_; + bool bound_ = false; +}; + + +template <class GB, class VT, class TP> +class DiscreteFunction<GB,VT,TP>::GradientLocalFunction +{ + using R = typename DiscreteFunction::Range; + using D = typename DiscreteFunction::Domain; + using RawSignature = typename Dune::Functions::SignatureTraits<R(D)>::RawSignature; + using DerivativeTraits = Dune::Functions::DefaultDerivativeTraits<RawSignature>; + +public: + using Domain = typename EntitySet::LocalCoordinate; + using Range = typename DerivativeTraits::Range; + + enum { hasDerivative = false }; + +private: + using LocalView = typename GlobalBasis::LocalView; + using Element = typename EntitySet::Element; + using Geometry = typename Element::Geometry; + +public: + GradientLocalFunction(DiscreteFunction const& globalFunction) + : globalFunction_(&globalFunction) + , localView_(globalFunction_->basis().localView()) + , subTree_(&child(localView_.tree(), globalFunction_->treePath())) + {} + + void bind(Element const& element) + { + localView_.bind(element); + geometry_.emplace(element.geometry()); + bound_ = true; + } + + void unbind() + { + localView_.unbind(); + geometry_.reset(); + bound_ = false; + } + + /// Evaluate Gradient at bound element in local coordinates + Range operator()(Domain const& x) const; + + friend int order(GradientLocalFunction const& self) + { + assert( self.bound_ ); + return std::max(0, polynomialDegree(*self.subTree_)-1); + } + + /// Return the bound element + Element const& localContext() const + { + assert( bound_ ); + return localView_.element(); + } + +private: + DiscreteFunction const* globalFunction_; + LocalView localView_; + SubTree const* subTree_; + Dune::Std::optional<Geometry> geometry_; + bool bound_ = false; +}; + + +template <class GB, class VT, class TP> +typename DiscreteFunction<GB,VT,TP>::Range DiscreteFunction<GB,VT,TP>:: +LocalFunction::operator()(Domain const& x) const +{ + assert( bound_ ); + auto y = Range(0); + + auto&& coefficients = *globalFunction_->dofVector_; + auto&& nodeToRangeEntry = globalFunction_->nodeToRangeEntry_; + forEachLeafNode_(*subTree_, [&,this](auto const& node, auto const& tp) + { + auto&& fe = node.finiteElement(); + auto&& localBasis = fe.localBasis(); + + LocalBasisCache<std::decay_t<decltype(localBasis)>> localBasisCache(localBasis); + auto const& shapeFunctionValues = localBasisCache.evaluateFunction(localView_.element(),x); + + // Get range entry associated to this node + auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, y)); + + for (std::size_t i = 0; i < localBasis.size(); ++i) { + auto&& multiIndex = localView_.index(node.localIndex(i)); + + // Get coefficient associated to i-th shape function + auto c = Dune::Functions::flatVectorView(coefficients[multiIndex]); + + // Get value of i-th shape function + auto v = Dune::Functions::flatVectorView(shapeFunctionValues[i]); + + std::size_t dimC = c.size(); + std::size_t dimV = v.size(); + assert(dimC*dimV == std::size_t(re.size())); + for(std::size_t j = 0; j < dimC; ++j) { + auto&& c_j = c[j]; + for(std::size_t k = 0; k < dimV; ++k) + re[j*dimV + k] += c_j*v[k]; + } + } + }); + + return y; +} + + +template <class GB, class VT, class TP> +typename DiscreteFunction<GB,VT,TP>::GradientLocalFunction::Range DiscreteFunction<GB,VT,TP>:: +GradientLocalFunction::operator()(Domain const& x) const +{ + assert( bound_ ); + Range dy; + for (std::size_t j = 0; j < dy.size(); ++j) + dy[j] = 0; + + auto&& coefficients = *globalFunction_->dofVector_; + auto&& nodeToRangeEntry = globalFunction_->nodeToRangeEntry_; + forEachLeafNode_(*subTree_, [&,this](auto const& node, auto const& tp) + { + // TODO: may DOFVectorView::Range to FieldVector type if necessary + using LocalDerivativeTraits = Dune::Functions::DefaultDerivativeTraits<Dune::FieldVector<double,1>(Domain)>; + using GradientBlock = typename LocalDerivativeTraits::Range; + + auto&& fe = node.finiteElement(); + auto&& localBasis = fe.localBasis(); + + LocalBasisCache<std::decay_t<decltype(localBasis)>> localBasisCache(localBasis); + auto const& referenceGradients = localBasisCache.evaluateJacobian(localView_.element(),x); + + // The transposed inverse Jacobian of the map from the reference element to the element + auto&& jacobian = geometry_.value().jacobianInverseTransposed(x); + + // Compute the shape function gradients on the real element + std::vector<GradientBlock> gradients(referenceGradients.size()); + for (std::size_t i = 0; i < gradients.size(); ++i) + multiplies_ABt(referenceGradients[i], jacobian, gradients[i]); // D[phi] * J^(-1) -> grad + + // Get range entry associated to this node + auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, dy)); + + for (std::size_t i = 0; i < localBasis.size(); ++i) { + auto&& multiIndex = localView_.index(node.localIndex(i)); + + // Get coefficient associated to i-th shape function + auto c = Dune::Functions::flatVectorView(coefficients[multiIndex]); + + // Get value of i-th transformed reference gradient + auto grad = Dune::Functions::flatVectorView(gradients[i]); + + std::size_t dimC = c.size(); + std::size_t dimV = grad.size(); + assert(dimC*dimV == std::size_t(re.size())); + for(std::size_t j = 0; j < dimC; ++j) { + auto&& c_j = c[j]; + for(std::size_t k = 0; k < dimV; ++k) + re[j*dimV + k] += c_j*grad[k]; + } + } + }); + + return dy; +} + + +template <class GB, class VT, class TP> +typename DiscreteFunction<GB,VT,TP>::Range DiscreteFunction<GB,VT,TP>:: +operator()(Domain const& x) const +{ + using Grid = typename GlobalBasis::GridView::Grid; + using IS = typename GlobalBasis::GridView::IndexSet; + + auto const& gv = this->basis().gridView(); + Dune::HierarchicSearch<Grid,IS> hsearch{gv.grid(), gv.indexSet()}; + + auto element = hsearch.findEntity(x); + auto geometry = element.geometry(); + auto localFct = localFunction(*this); + localFct.bind(element); + return localFct(geometry.local(x)); +} + +} // end namespace AMDiS diff --git a/src/amdis/utility/RangeType.hpp b/src/amdis/utility/RangeType.hpp index cee8d959e970b86769290983aa38c2797ec0d86d..27116104982776775ae39e9369c5d117623c9536 100644 --- a/src/amdis/utility/RangeType.hpp +++ b/src/amdis/utility/RangeType.hpp @@ -11,31 +11,43 @@ namespace AMDiS { namespace Impl { - template <class Node, bool isLeaf, bool isPower, bool isComposite> + template <class Node, class R, bool isLeaf, bool isPower, bool isComposite> struct RangeTypeImpl { static_assert( isLeaf || isPower || isComposite, "Unknown node-type for range definition" ); }; } - template <class Node> + /// \brief Range type of a node in the basis tree, composed of the leaf basis + /// range types. + /** + * Generate the range type by recursively combining leaf range types to a + * hybrid node range type. Range types for PowerNodes are thereby constructed + * as Dune::FieldVector of child range types. CompositeNodes produce a + * MultiTypeVector of the difference child range types. + * + * \tparam Node Type of a basis-tree node + * \tparam R Coefficient type [double] + **/ + template <class Node, class R = double> using RangeType_t = - typename Impl::RangeTypeImpl<Node, Node::isLeaf, Node::isPower, Node::isComposite>::type; + typename Impl::RangeTypeImpl<Node, R, Node::isLeaf, Node::isPower, Node::isComposite>::type; namespace Impl { // Leaf node - template <class Node> - struct RangeTypeImpl<Node, true, false, false> + template <class Node, class R> + struct RangeTypeImpl<Node, R, true, false, false> { using LocalBasis = typename Node::FiniteElement::Traits::LocalBasisType; - using type = typename LocalBasis::Traits::RangeType; + using T = typename LocalBasis::Traits::RangeType; + using type = std::decay_t<decltype(std::declval<R>() * std::declval<T>())>; }; // Power node - template <class Node> - struct RangeTypeImpl<Node, false, true, false> + template <class Node, class R> + struct RangeTypeImpl<Node, R, false, true, false> { using ChildNode = typename Node::template Child<0>::type; @@ -49,12 +61,12 @@ namespace AMDiS using type = Dune::FieldVector<T, int(Node::CHILDREN)>; }; - using type = typename FlatType<ChildNode::isLeaf, RangeType_t<ChildNode>>::type; + using type = typename FlatType<ChildNode::isLeaf, RangeType_t<ChildNode,R>>::type; }; // Composite node - template <class Node> - struct RangeTypeImpl<Node, false, false, true> + template <class Node, class R> + struct RangeTypeImpl<Node, R, false, false, true> { template <class Idx> struct RangeTypeGenerator; template <std::size_t... I> @@ -62,7 +74,7 @@ namespace AMDiS { template <std::size_t J> using ChildNode = typename Node::template Child<J>::type; - using type = MultiTypeVector<RangeType_t<ChildNode<I>>...>; + using type = MultiTypeVector<RangeType_t<ChildNode<I>,R>...>; }; using type = typename RangeTypeGenerator<MakeSeq_t<Node::CHILDREN>>::type; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 88b9c3065228b2ad046d74d12d152311399ec091..57754d5dc8a495c3869ad293c8482b29928803de 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,6 +11,10 @@ dune_add_test(SOURCES ConceptsTest.cpp dune_add_test(SOURCES DOFVectorTest.cpp LINK_LIBRARIES amdis) +dune_add_test(SOURCES DiscreteFunctionTest.cpp + LINK_LIBRARIES amdis + CMD_ARGS "${CMAKE_SOURCE_DIR}/examples/init/ellipt.dat.2d") + dune_add_test(SOURCES ExpressionsTest.cpp LINK_LIBRARIES amdis) diff --git a/test/DiscreteFunctionTest.cpp b/test/DiscreteFunctionTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f390af6ea44eb63d875770f46b22bce15768193e --- /dev/null +++ b/test/DiscreteFunctionTest.cpp @@ -0,0 +1,107 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#include <iostream> +#include <type_traits> + +#include <amdis/AMDiS.hpp> +#include <amdis/ProblemStat.hpp> +#include <amdis/Operators.hpp> +#include <amdis/common/Literals.hpp> +#include <amdis/common/FieldMatVec.hpp> +#include <amdis/gridfunctions/Integrate.hpp> + +#include "Tests.hpp" + +using namespace AMDiS; + +using ElliptParam = YaspGridBasis<2, 2>; +using ElliptProblem = ProblemStat<ElliptParam>; + +template <class GB, class T> +bool comp(DOFVector<GB,T> const& U, DOFVector<GB,T> const& V) +{ + if (U.size() != V.size()) + return false; + + using Int = typename DOFVector<GB,T>::size_type; + for (Int i = 0; i < U.size(); ++i) { + if (U[i] != V[i]) + return false; + } + return true; +} + +int main(int argc, char** argv) +{ + AMDiS::init(argc, argv); + + using namespace Dune::Indices; + + ElliptProblem prob("ellipt"); + prob.initialize(INIT_ALL); + + // create 3 copies of the solution vector + auto U0 = prob.getSolutionVector(); + auto U1 = prob.getSolutionVector(); + auto U2 = prob.getSolutionVector(); + + auto u0 = makeDOFVectorView(U0); + auto u1 = makeDOFVectorView(U1); + auto u2 = makeDOFVectorView(U2); + + auto expr = [](auto const& x) { return x[0] + x[1]; }; + u0.interpolate_noalias(expr); + u1.interpolate(expr); + u2 << expr; + + AMDIS_TEST( comp(U0, U1) ); + AMDIS_TEST( comp(U0, U2) ); + + auto expr2 = [](auto const& x) { return 2*x[0] - 3*x[1]; }; + + u0.interpolate_noalias(u2 + expr2); + u1.interpolate(u1 + expr2); + u2 += expr2; + + AMDIS_TEST( comp(U0, U1) ); + AMDIS_TEST( comp(U0, U2) ); + + auto expr3 = [](auto const& x) { return -0.5*x[0] - 2*x[1]; }; + + u0.interpolate_noalias(u2 - expr3); + u1.interpolate(u1 - expr3); + u2 -= expr3; + + AMDIS_TEST( comp(U0, U1) ); + AMDIS_TEST( comp(U0, U2) ); + + auto du0 = derivative(u0); + auto localFct = localFunction(u0); + auto dlocalFct = derivative(localFct); + for (auto const& e : elements(prob.gridView())) + { + localFct.bind(e); + auto geo = e.geometry(); + auto local = referenceElement(geo).position(0,0); + auto y0 = localFct(local); + auto y1 = u0(geo.global(local)); + + AMDIS_TEST(std::abs(y0 - y1) < 1.e-10); + localFct.unbind(); + + dlocalFct.bind(e); + auto g0 = dlocalFct(local); + auto g1 = du0(geo.global(local)); + + AMDIS_TEST(two_norm(g0 - g1) < 1.e-10); + dlocalFct.unbind(); + } + + auto V0 = makeDOFVector<float>(prob.globalBasis()); + auto v0 = makeDOFVectorView(V0); + v0 << expr; + + AMDiS::finalize(); + return 0; +}