pax_global_header00006660000000000000000000000064151363402220014510gustar00rootroot0000000000000052 comment=9a7658002ca991bbd8e0592a004f0b752fb237e2 dune-functions-2.11.0+dfsg/000077500000000000000000000000001513634022200154515ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/.git-blame-ignore-revs000066400000000000000000000004021513634022200215450ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # Reindentation of a large code block 142d56951733f3ef6259565d660490c9a7e14d29 dune-functions-2.11.0+dfsg/.gitignore000066400000000000000000000004141513634022200174400ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # ignore all build folders /build*/ # ignore backup files *~ # ignore Python files *.pyc dune-functions-2.11.0+dfsg/.gitlab-ci.yml000066400000000000000000000043541513634022200201130ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later --- include: - project: 'core/ci-config' ref: master file: 'config/common/releases/2.11.yml' - project: 'core/ci-config' ref: master file: 'jobs/common/releases/2.11.yml' before_script: - . /duneci/bin/duneci-init-job - duneci-install-module https://gitlab.dune-project.org/core/dune-common.git - duneci-install-module https://gitlab.dune-project.org/core/dune-geometry.git - duneci-install-module https://gitlab.dune-project.org/core/dune-localfunctions.git - duneci-install-module https://gitlab.dune-project.org/staging/dune-uggrid.git - duneci-install-module https://gitlab.dune-project.org/core/dune-grid.git - duneci-install-module https://gitlab.dune-project.org/core/dune-istl.git # Check for spelling mistakes in text code-spelling-check: stage: .pre # Avoid the global 'before_script' before_script: "" image: registry.dune-project.org/docker/ci/debian:11 tags: [duneci] script: - codespell --ignore-words-list ans,nd,ba,ser,elemente # Check SPDX compliance of license information reuse: stage: .pre image: name: docker.io/fsfe/reuse:latest entrypoint: [""] tags: [duneci] before_script: "" script: - reuse lint ubuntu:24.04 gcc-13-20 (debug, with dune-typetree): extends: .common image: registry.dune-project.org/docker/ci/ubuntu:24.04 before_script: - . /duneci/bin/duneci-init-job - duneci-install-module https://gitlab.dune-project.org/core/dune-common.git - duneci-install-module https://gitlab.dune-project.org/core/dune-geometry.git - duneci-install-module https://gitlab.dune-project.org/core/dune-localfunctions.git - duneci-install-module https://gitlab.dune-project.org/staging/dune-uggrid.git - duneci-install-module https://gitlab.dune-project.org/core/dune-grid.git - duneci-install-module https://gitlab.dune-project.org/core/dune-istl.git - duneci-install-module https://gitlab.dune-project.org/staging/dune-typetree.git variables: DUNECI_CMAKE_FLAGS: "-DDUNE_GRID_SKIP_VTK_PYTHON_TESTS:BOOL=ON" DUNECI_TOOLCHAIN: gcc-13-20 DUNECI_BUILD_TYPE: Debug dune-functions-2.11.0+dfsg/AUTHORS.md000066400000000000000000000014201513634022200171150ustar00rootroot00000000000000 The `dune-functions` module contains contributions by: * Klaus Böhnlein * Jakub Both * Ansgar Burchardt * Andreas Dedner * Christian Engwer * Elisa Friebel * Christoph Gersbacher * Stefan Girke * Carsten Gräser * Felix Gruber * Christoph Grüninger * René Heß * Lasse Hinrichsen-Bischoff * Patrick Jaap * Max Kahnt * Dominic Kempf * Angela Klewinghaus * Timo Koch * Tobias Malkmus * Alexander Müller * Felix Müller * Steffen Müthing * Martin Nolte * Santiago Ospina De Los Rios * Elias Pipping * Maik Porrmann * Simon Praetorius * Lukas Renelt * Oliver Sander * Henrik Stolzmann * Jonathan Youett dune-functions-2.11.0+dfsg/CHANGELOG.md000066400000000000000000000470571513634022200172770ustar00rootroot00000000000000 # Dune-functions changes Any version of dune-functions is supposed to be compatible with the corresponding version of the Dune core modules. # Release 2.11 - Dune-functions no longer has a hard dependency on dune-typetree. If dune-typetree is not present, all features of dune-functions can be used, except for the deprecated `NodeTag` member typedefs of the node classes. After 2.11 the optional dependency on dune-typetree will be dropped, too. Hence downstream modules depending on typetree features that have not been moved to dune-common will have to make dune-typetree an explicit dependency. - Dune-functions now only depends on the modernized typetree interface provided in the `dune/common/typetree/` subdirectory of the dune-typetree module. The plan is to eventually move this code to the dune-common module and then drop dune-typetree as dependency of dune-functions. - The nodes of a `LocalView::Tree` no longer derive from the base classes in dune-typetree but are still compatible with the utilities and algorithms from dune-typetree. The node members `isLeaf`, `isPower`, `isComposite`, and `NodeTag` are deprecated and the respective properties can be derived by checking the new node concepts from `dune/common/typetree/nodeconcepts.hh`. The node members `Child`, `ChildType`, and `ChildTypes` are deprecated in favour of using `Dune::TypeTree::Child`. - There is now an implementation of the Argyris finite element. - The `RaviartThomasBasis` can now be used with mixed grids in 3d including cubes, tetrahedra, pyramids, prisms. - Using the methods `power` or `composite` to construct function space basis trees without an explicit index merging strategy argument will now issue a warning. This warning informs that the default index merging strategy will change after the 2.11 release. More specifically, it will change from `BlockedLexicographic` to `FlatLexicographic` for the `composite` method, and from `BlockedInterleaved` to `FlatInterleaved` for the `power` method. The consequence is that default indexing is always flat, which is the most common form of indexing. To ensure a smooth transition we suggest to not rely on default index merging strategies until 2.11 has been released. - Similarly, when calling `functions.Power` to construct a product of finite element bases in a Python program, you will now get a warning saying that the default for the `layout` parameter will change from `lexicographic` to `interleaved` after the 2.11 release. After these changes, C++ and Python will have the same defaults. - The `GridFunction` class (and indeed everything else that inherits from `TypeErasureBase`) now implements the `operator bool` cast operator, which allows to check whether the `GridFunction` object contains a valid object or not. - The `LagrangeDG(Pre)Basis` supports selecting the polynomial order at runtime. Its static variables `LagrangeDGPreBasis::dofsPer.*` are deprecated. - Add function wrappers `CoarseFunctionOnFineGridView` and `FineFunctionOnCoarseGridView` that allow to represent a grid function on a grid view if the function is itself only defined on an entity set that is coarser or finer than the the target grid view. - Add the utility class `GeometryInAncestor` providing the geometric fine-in-coarse element embedding across multiple levels. - Add the function `makeISTLVector` to construct an istl vector type compatible with a container descriptor of a basis. - Add `HierarchicalLagrangeWithElementBubblePreBasis` implemented in terms of `LFEPreBasisMixin` - The new `MorleyPreBasis` implements the non-conforming C1 Morley element on triangles. - The new `CubicHermitePreBasis` implements the cubic Hermite basis on simplices in 1d,2d, and 3d. This C1 element is in general non-conforming since differentiability is only guaranteed in vertices (except for 1d where it coincides with cubic C1 splines). For 2d there is also the reduced variant which is part of the mixed DKT-element. - The new utility class `TransformedFiniteElementMixin` can be used to implement local finite elements where the element specific basis functions are obtained by a linear transformation of the reference basis functions (additionally to the classical geometric pull back). This can e.g. be used to implement Hermite-type C1 elements. - The deprecated header `functionspacebases/sizeinfo.hh` was removed. - The deprecated header `functionspacebases/defaultnodetorangemap.hh` was removed. - The deprecated header `functionspacebases/hierarchicvectorwrapper.hh`was removed. - The consistency guarantees between leaf-nodes and the corresponding finiteElement got relaxed. This is necessary to add multidomain support. The change is that if `node.size() == 0` accessing `finiteElement` or `element()` is undefined behaviour. To check for this, the basis nodes have a new member function `node.empty()` which is equivalent to `node.size()==0`. These changes are also reflected in the updated tests. - Any global basis implementation is required to implement a method `.containerDescriptor()` returning a container descriptor or `ContainerDescriptors::Unknown` as a fallback type. The default behavior implemented in `DefaultGlobalBasis` is to return the container descriptor provided by its pre-basis. - One overload of `forEachBoundaryDOF` is now available via the Python interface. ## Release 2.10 - The new class `MonomialSet` implements a vector-valued differentiable function containing all `dim`-variate monomials up to order `n` as components. - The module dune-uggrid is now officially a hard dependency of dune-functions. - Added a constructor for `LocalViewWrapper` from a `LocalView` object and added accessor to base class for python bindings. - The class `Polynomial` now allows to explicitly specify the used coefficient container. Supported containers are `std::vector` (default, as before), `std::array`, `std::tuple`, and `std::integer_sequence`. Class template argument deduction allows to deduce the scalar type from homogeneous containers. The scalar type can be explicitly specified to be different with the `makePolynomial()` helper function, while the coefficients container type is still deduced. - Make `AnalyticGridViewFunction` copyable - The method `operator()` of the class `DiscreteGlobalBasisFunction` has been implemented. It evaluates a piecewise function on a grid at a point given in global coordinates. As this evaluation requires finding the element that the evaluation point is in, it can be very slow. - Reimplement `HasStaticSize` in terms by checking `Hybrid::size()` - Add utility `StaticSizeOrZero` that defaults to the `integral_constant` in case the container does only have dynamic size. - Add global coordinate evaluation for `CompositeGridFunction` - Add new global basis function based on refined Lagrange local-functions. The pre-basis is called `template class RefinedLagrangePreBasis` and a corresponding pre-basis-factory `refinedLagrange()` with `k=[1|2]` currently implemented. - All basis implementations are now copyable and assignable - Add mixin class `LeafPreBasisMixin` and make available the mixin class `LeafPreBasisMapperMixin` to simplify writing of new pre-bases. - Add a dynamic power basis with a runtime exponent that can be constructed with the factory `power(, k)` or `power(, k, )`. - Add mixin class `LFEPreBasisMixin` to create leaf pre-bases based on a local finite-element and a layout. - The class `LeafPreBasisMapperMixIn` is renamed into `LeafPreBasisMapperMixin`. - Add data structures for container descriptors in namespace `Dune::Functions::ContainerDescriptors`. - Introduce a new member-function `.containerDescriptor()` for all pre-bases that return an instance of one of the container descriptors to represent a container that can be indexed by the multi-indices of that pre-basis. - Container descriptor for power bases with blocked-interleaved index-merging-strategy added. ### Python - The Nédélec and Raviart-Thomas function space bases are now accessible via the Python interface. Deprecations and removals - The class `DefaultNodeToRangeMap` and the corresponding header `functionspacebases/defaultnodetorangemap.hh` have been deprecated. This has been replaced as default by `HierarchicNodeToRangeMap` for some time. - The `BasisBuilder` namespace is deprecated and will be removed after the 2.10 release. Use `BasisFactory` instead. - The headers `common/callable.hh` and `common/functionfromcallable.hh` providing adaptors the removed `VirtualFunction` interface from dune-common have been removed. - Remove deprecated `DefaultLocalView::isBound()`, use `bound()` instead. - The class `HierarchicalVectorWrapper` and the corresponding header `functionspacebases/hierarchicvectorwrapper.hh` have been deprecated. Use `istlVectorBackend()` from `backends/istlvectorbackend.hh` instead. - The class `SizeInfo` and the corresponding header `functionspacebases/sizeinfo.hh` have been deprecated. One now can directly use `vectorBackend.resize(globalBasis)` instead of `vectorBackend.resize(sizeInfo(globalBasis))`. - The deprecated header `common/treedata.hh` was removed. - The deprecated header `common/referencehelper.hh` was removed. Use the corresponding header from dune-common instead. ## Release 2.9 - The `MultiIndex` used by `DefaultGlobalBasis` is now a `StaticMultiIndex` if it's size is known at compile time. Otherwise its a `ReservedVector`. - The template class `StaticMultiIndex` for a statically sized multiindex was added. This essentially adds `operator<<` to `std::array` for writing to a stream and a cast to the first entry for `size()==0`. `FlatMultiIndex` is now an alias for `StaticMultiIndex`. - The template class `OverflowArray` was added. It mostly behaves like `Dune::ReservedVector` but derives from a statically sized array base class. This allows to have temporary dynamic size but cast to the result to the statically sized base class. - The new `FaceNormalGridFunction` implements the unit outer normal of the closest face within an element as a grid function. - SubspaceBases will no longer be nested. Instead, `SubspaceBasis(SubspaceBases(rootBasis,innerTP),outerTP)` is resolved to a `SubspaceBases(rootBasis, tp)` where the tree path `tp` is obtained by joining `innerTP` and `outerTP`. This is implemented for the helper function `subspaceBasis()` and the newly added class template argument deduction guides. - The way multi index types are generated for global bases has been refactored. This changes nothing for the user but gives more freedom in the construction of global bases. But it is a breaking change for the `PreBasis` and `PreBasisFactory` interface. If you maintain your own prebasis implementations, they have to be adapted. The necessary modifications are documented in detail here: https://gitlab.dune-project.org/staging/dune-functions/-/merge_requests/326. - Support for `PreBasis` implementations not providing `PreBasis::indices()` has been removed. - The added `ComposedGridFunction` implements the composition of a lambda function with one or several grid functions while itself providing the grid function interface. - The new utility `resolveRef(t)` allows to seamlessly work with `std::reference_wrapper` by returning either `t.get()` or `t` depending on whether `t` is a `std::reference_wrapper` or not. - The header treedata.hh and the class `TreeData` are deprecated. Please use `TreeContainer` from dune-typetree instead. - Function `derivative()` of `LocalAnalyticGridFunction` returns a bound local-function if the underlying value-local-function was bound before, i.e., the bound-to element and geometry are passed to the derivative local-function. - The concepts for `LocalFunction` and `LocalView` were extended with a `bound()` member function. It tells whether the object was already bound with a call to the `bind()` method or not. Implementations of the two concepts outside dune-functions will have to implement this additional method. - The derivative of a `DiscreteGlobalBasisFunction` is now implemented. ## Release 2.8 - `PreBasis` implementations are now required to provide a method `PreBasis::indices(node,iterator)` that replaces binding a `NodeIndexSet` to `node` and then calling `NodeIndexSet::indices(iterator)`. As a consequence `PreBasis::IndexSet` and `PreBasis::makeIndexSet` are no longer needed. - The `RaviartThomasBasis` and `BrezziDouglasMariniBasis` now return Piola-transformed shape functions. This is implemented by changing the return value of `tree().finiteElement()`: It is not an object of type `RaviartThomasLocalFiniteElement` or `BrezziDouglasMariniLocalFiniteElement` anymore. Rather, it is an object of a new type `GlobalValuedLocalFiniteElement`, which wraps other `LocalFiniteElement` implementations and applies a range-space transformation. Domain-space transformations still have to be done by the calling code. The `GlobalValuedLocalFiniteElement` still implements the `LocalFiniteElement` interface of `dune-localfunctions`. - The `RaviartThomasBasis` class now supports tetrahedral grids for `order=0`, quadrilateral grids for `order=2`, and hexahedral grids for `order=1`. - The `RannacherTurekBasis` class now supports Crouzeix-Raviart elements. Grids containing simplices, cubes or both in 2d and 3d are supported now. - The `dune-functions` module now contains an implementation of a Nedelec basis (for problems posed in H(curl)). While the interface caters to different basis orders, grid dimensions and element types, only the first-order basis called "of the first kind" is implemented, and only for grids containing simplices, cubes or both in 2d and 3d. - The `dune-functions` module now contains an implementation of a Hierarchical Lagrange basis for second order on simplex grids - There is now an experimental implementation of a periodic basis in form of a class `PeriodicBasis`. It is a meta basis, i.e., a basis that is parametrized with another basis (the host basis). In a `PeriodicBasis`, global degrees of freedom of the host basis can be grouped into equivalence classes, which are then treated as single global degrees of freedom. This allows, in particular, to implement periodic boundary conditions for discretizations without intersection integrals. The `PeriodicBasis` class can only be constructed by using the `periodic` method from the namespace `Dune::Functions::BasisFactory::Experimental`. It can change at any moment without much advance notice. Use it at your own risk, and give us feedback! - Imported the Python bindings from the 2.7 branch of dune-python and fixed remaining issues. Added a CI test that builds various global bases in 2d and 3d and verifies the correct number of dofs. - `interpolate` is now capable of interpolating vector-valued finite element functions correctly. The method of using scalar basis functions combined with vector-valued coefficients to mock a power basis is still supported. ## Release 2.7 - The `LagrangeBasis` is extended by a template parameter to set the range type of the underlying LocalBasis. This parameter is also added to the basis factory. One can write `lagrange()` to create a lagrange basis with compile-time order `k` and range type `float`. The range type defaults to `double` if nothing is given. The run-time order lagrange functions can be created by `lagrange(k)`, correspondingly. - The `LagrangeBasis` class can now be used with a run-time polynomial order. For this, the template parameter setting the order now has a default value of -1. If this default value is used, the class accepts an integer constructor argument with a run-time order. As the decision whether to use a compile-time or run-time order is taken at compile-time there should be no efficiency decrease for the compile-time-order case. (Note: The implementation currently uses the `LagrangeFiniteElement` implementation of `dune-localfunctions`, which violates strict-aliasing rules. This may lead to surprising crashes and miscalculations when compiling with optimization.) ## Release 2.6 - The functionality provided by `LocalIndexSet`, namely the `indices` method and the `MultiIndex` typedef, have been merged into `LocalView`. The global multi-indices are now precomputed and cached when binding the `LocalView`. `LocalIndexSet` as well as the `localIndexSet()` method of global bases have been deprecated. - When using `DiscreteGlobalBasisFunction` and `interpolate` the function range is accessed hierarchically. I.e. a leaf node with tree path i0,...,in is mapped to the range entry `r[i0]...[in]`. - If the vector passed to `DiscreteGlobalBasisFunction` or `interpolate()` does not provide the `VectorBackend` interface, it is automatically wrapped using `istlVectorBackend(vector)`. - The new method `istlVectorBackend(vector)` creates a `VectorBackend` suitable for being used as coefficient vector for `interpolate()` and `DiscreteGlobalBasisFunction`. The underlying `vector` can be a nested vector composed by stl and dune random access containers. Notice that the only scalar coefficients per basis function are supported. - The algorithm `forEachBoundaryDOF()` was added in a new header `boundarydofs.hh`. It allows to iterate over all DOFs of a given global basis associated to sub-entities located at the boundary of the domain. - The class `SubEntityDOFs` was added along with some `subEnitityDOFs` helper functions for creation in the new header `subentitydofs.hh`. After binding an object of this class to a local view and a sub-entity it can be used a range of local indices of DOFs associated to sub-sub-entities of this sub-entity. - `FlatVectorBackend` is now officially an implementation detail and thus moved to the namespace `Impl::`. The header `flatvectorbackend.hh` was removed. As a replacement the new free function `flatVectorView(c)` create a view object providing `operator[]` and `size()` methods for flat-vector-like access to the underlying container `c`. - The `BasisBuilder` namespace has been renamed to `BasisFactory`. The old name still exists and should work as before, but its use is discouraged. - Added an implementation of a Rannacher-Turek basis - Add a set of unit tests for bases - Extend the documentation of these bases - Remove `DiscreteScalarGlobalBasisFunction`. This was replaced by `DiscreteGlobalBasisFunction`. - Introduce interface method `NodeIndexSet::indices(it)` that must write all global indices for the bound tree into the container pointed to by the iterator `it` - Remove `NodeIndexSet::index(i)` for computation of individual global indices. - `DefaultLocalIndexSet::bind(...)` now computes and caches all global indices while `DefaultLocalIndexSet::index(...)` is just a cheap lookup in the cache. - `Dune::Functions::Optional` was removed. Please use `Dune::Std::optional` instead. - Dune-functions now requires at least GCC 5 and uses C++14. As a consequence Debian 8 is no longer supported and tested. - An implementation of a global Raviart-Thomas basis provided by Jakub Both was merged. This is still incomplete and considered experimental. - An implementation of a global Brezzi-Douglas-Marini-Basis was added. Thanks to Jakub Both for the code. - `Dune::Functions::TupleVector` was deprecated. Use `Dune::TupleVector` from dune-common instead. ## Release 2.5 TODO... dune-functions-2.11.0+dfsg/CMakeLists.txt000066400000000000000000000024741513634022200202200ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later cmake_minimum_required(VERSION 3.16) project(dune-functions CXX) if(NOT (dune-common_DIR OR dune-common_ROOT OR "${CMAKE_PREFIX_PATH}" MATCHES ".*dune-common.*")) string(REPLACE ${CMAKE_PROJECT_NAME} dune-common dune-common_DIR ${PROJECT_BINARY_DIR}) endif() #find dune-common and set the module path find_package(dune-common REQUIRED) list(APPEND CMAKE_MODULE_PATH ${dune-common_MODULE_PATH}) #include the dune macros include(DuneMacros) # deactivate global include-directories dune_policy(SET DP_DEFAULT_INCLUDE_DIRS NEW) # deactivate global calls to add_dune_all_flags in tests dune_policy(SET DP_TEST_ADD_ALL_FLAGS NEW) # start a dune project with information from dune.module dune_project() dune_add_library(dunefunctions INTERFACE EXPORT_NAME Functions LINK_LIBRARIES ${DUNE_LIBS}) # set include directories for dunefunctions library dune_default_include_directories(dunefunctions INTERFACE) add_subdirectory("dune") add_subdirectory("doc") add_subdirectory("examples") if( DUNE_ENABLE_PYTHONBINDINGS ) add_subdirectory(python) endif() # finalize the dune project, e.g. generating config.h etc. finalize_dune_project() dune-functions-2.11.0+dfsg/COPYING000066400000000000000000000051331513634022200165060ustar00rootroot00000000000000Copyright holders: 2023--2025 Markus Blatt 2016--2017 Jakub Both 2021 Klaus Böhnlein 2016--2023 Ansgar Burchardt 2021 Andreas Dedner 2013--2025 Christian Engwer 2015 Elisa Friebel 2013 Christoph Gersbacher 2013 Stefan Girke 2013--2025 Carsten Gräser 2015--2018 Felix Gruber 2015--2025 Christoph Grüninger 2021--2024 Patrick Jaap 2017--2021 Lasse Hinrichsen 2020 René Heß 2017--2019 Max Kahnt 2016--2018 Dominic Kempf 2020 Timo Koch 2015 Angela Klewinghaus 2017 Tobias Malkmus 2023 Alexander Müller 2020 Felix Müller 2013--2019 Steffen Müthing 2017 Martin Nolte 2015--2017 Elias Pipping 2024--2025 Maik Porrmann 2022--2025 Santiago Ospina De Los Ríos 2018--2025 Simon Praetorius 2018 Lukas Renelt 2013--2025 Oliver Sander 2020--2025 Henrik Stolzmann 2015 Jonathan Youett The dune-functions library, headers and test programs are copyrighted free software. You can use, modify and/or redistribute it under the terms of either one of the two following licenses: * The GNU Lesser General Public License as published by the Free Software Foundation, either Version 3 of the license or (at your option) any later version. You can find a copy of the GNU Lesser General Public License, Version 3, in the files GPL-3 and LGPL-3 or at . * Version 2 of the GNU General Public License as published by the Free Software Foundation, with the following special exception for linking and compiling against the dune-functions library, the so-called "runtime exception": As a special exception, you may use the dune-functions source files as part of a software library or application without restriction. Specifically, if other files instantiate templates or use macros or inline functions from one or more of the dune-functions source files, or you compile one or more of the dune-functions source files and link them with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License. This license is intended to be similar to the GNU Lesser General Public License, Version 2, which by itself isn't suitable for a template library. You can find a copy of the GNU General Public License, Version 2, in the file GPL-2 or at . dune-functions-2.11.0+dfsg/LICENSES/000077500000000000000000000000001513634022200166565ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/LICENSES/BSD-3-Clause.txt000066400000000000000000000026641513634022200214110ustar00rootroot00000000000000Copyright (c) . Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. dune-functions-2.11.0+dfsg/LICENSES/CC-BY-ND-4.0.txt000066400000000000000000000406761513634022200210270ustar00rootroot00000000000000Creative Commons Attribution-NoDerivatives 4.0 International Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. Creative Commons Attribution-NoDerivatives 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NoDerivatives 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 – Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. c. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. d. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. e. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. f. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. g. Licensor means the individual(s) or entity(ies) granting rights under this Public License. h. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. i. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. j. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 – Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: A. reproduce and Share the Licensed Material, in whole or in part; and B. produce and reproduce, but not Share, Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 5. Downstream recipients. A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 – License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material, You must: A. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. For the avoidance of doubt, You do not have permission under this Public License to Share Adapted Material. 3. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 4. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. Section 4 – Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database, provided You do not Share Adapted Material; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 – Disclaimer of Warranties and Limitation of Liability. a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 – Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 – Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 – Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. dune-functions-2.11.0+dfsg/LICENSES/LGPL-3.0-or-later.txt000066400000000000000000001221621513634022200222020ustar00rootroot00000000000000GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . dune-functions-2.11.0+dfsg/LICENSES/LicenseRef-GPL-2.0-only-with-DUNE-exception.txt000066400000000000000000000451341513634022200270370ustar00rootroot00000000000000The DUNE library and headers are licensed under version 2 of the GNU General Public License (see below), with a special exception for linking and compiling against DUNE, the so-called "runtime exception." The license is intended to be similar to the GNU Lesser General Public License, which by itself isn't suitable for a template library. The exact wording of the exception reads as follows: As a special exception, you may use the DUNE source files as part of a software library or application without restriction. Specifically, if other files instantiate templates or use macros or inline functions from one or more of the DUNE source files, or you compile one or more of the DUNE source files and link them with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. dune-functions-2.11.0+dfsg/LICENSES/MIT.txt000066400000000000000000000020141513634022200200450ustar00rootroot00000000000000MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dune-functions-2.11.0+dfsg/README.md000066400000000000000000000173761513634022200167460ustar00rootroot00000000000000 # The dune-functions module ## Functionality The _dune-functions_ module provides an abstraction layer for global finite element functions. Its two main concepts are functions implemented as callable objects, and bases of finite element spaces. ### Functions _dune-functions_ provides an interface to "functions" in the mathematical sense, in particular to finite element functions defined on a grid, but going far beyond that. The interface revolves around the concept of a "callable". This encompasses any type of C++ object that can be evaluated with `operator()`, like free functions, function objects, and lambdas. Dynamic polymorphism is realized using type erasure and the `std::function` class, which does not sacrifice efficiency in purely static code. _dune-functions_ extends the "callable" concept into several directions. First, it allows for differentiable functions. Such functions can hand out their derivative as new function objects. Second, for functions defined piecewisely on a finite element grid, the concept of local function is introduced. Local functions can be bound to grid elements. All further evaluations of a function bound to an element are in local coordinates of that element. This approach allows to avoid overhead when there are many consecutive evaluations of a function on a single element. ### Function space bases The second part of _dune-functions_ provides a well-defined interface to bases of finite element function spaces. For this interface, a finite element basis is a set of functions with a prescribed ordering, and a way to index them. The core functionality has three parts: 1. For a given grid element, obtain the restrictions of all basis functions to this element, except for those functions where the restriction is zero. In other words: get the shape functions for the element. 2. Get a local numbering for these shape functions. This is needed to index the element stiffness matrix. 3. Get a global numbering for the shape functions. This is needed to index the global stiffness matrix. While local numbers are always integers, global numbers can be multi-indices, if appropriate. A central feature of _dune-functions_ is the construction of finite element bases for vector-valued and mixed spaces by tensor multiplication of simpler bases. The resulting tensor multiplication expressions can be interpreted as rooted trees. For example, the tree for the three-dimensional Taylor-Hood basis is shown below. This tree structure is directly exposed in the _dune-functions_ interface. ![Sketch of a Taylor-Hood space in tree representation](doc/gfx/dune-functions-taylor-hood-tree.svg "Taylor-Hood space") ### Implementations of Function Space Bases Some of the finite element bases currently available are: * `LagrangeBasis`: A k-th order Lagrangian bases, with k a run-time or compile-time parameter. * `LagrangeDGBasis`: A k-th order DG basis, using Lagrangian shape functions. * `NedelecBasis`: For H(curl)-conforming problems * `RaviartThomasBasis`: For H(div)-conforming problems * `BrezziDouglasMariniBasis`: The Brezzi-Douglas-Marini-Element * `RannacherTurekBasis`: Combines the Rannacher-Turek element on quadrilateral elements with the Crouzeix-Raviart element on simplices * `BSplineBasis`: A basis of B-Spline functions of given arbitrary order on a structured grid. ## Documentation ### Class documentation The module contains a class documentation which can be build using [doxygen]. After the module has been build, you can build the documentation using `make doc` Additionally the pre-build doxygen documentation for the _master_ and release branches is also hosted on the [documentation section][dune docs] of the Dune website. ### Manual There are two documents describing the concepts and use of _dune-functions_. The interface for functions is described in the article C. Engwer, C. Gräser, S. Müthing, and O. Sander. The interface for functions in the dune-functions module. Archive of Numerical Software, 5(1):95--109, 2017. This is freely available via the [website of the journal][functions paper] and as [arXiv:1512.06136][functions paper arxiv] preprint. The interface for the function space bases is described in the article C. Engwer, C. Gräser, S. Müthing, and O. Sander. Function space bases in the dune-functions module. Preprint, arxiv:1806.09545, 2018. This is freely available as [arXiv:1806.09545][bases paper arxiv] preprint. The LaTeX source code for both is also contained in the module. Like the class documentation, it is build by `make doc`. Finally, the [Dune book](https://link.springer.com/book/10.1007/978-3-030-59702-3) also contains a chapter on _dune-functions_, which is, however, mainly an improved presentation of the two articles. ### Examples Several example applications demonstrate how to use the module. These example applications are contained in the `examples/` directory and they are built with `make examples`. The `stokes-taylorhood` example is described in detail in the book. ### Communication channels _dune-functions_ development and discussions happen mainly on the [dune-functions GitLab page](https://gitlab.dune-project.org/staging/dune-functions). There is also a [dune-functions mailing list](https://lists.dune-project.org/mailman/listinfo/dune-functions), but it has very little traffic these days. ## Using dune-functions and licensing The module is licensed by different variants of the GPL licence. Please have a look at the `COPYING` file for more information and a list of all contributors. When using _dune-functions_ please cite the publications on the [functions interface][functions paper] and the [bases interface][bases paper] listed above. ## Building dune-functions Dune-functions integrates into the `cmake`-based Dune build system. Hence it can be build (like any other module) using the `dunecontrol` script of the _dune-common_ module. For details on how to use this build system and how to specify build options have a look at the book or the documentation in the _dune-common_ module. ### Dependencies _Dune-functions_ depends on the dune [core modules][core] and the [dune-typetree][typetree] module. All of them are available in major Linux distributions. If you want their source codes you can find them at: * https://gitlab.dune-project.org/core/dune-common * https://gitlab.dune-project.org/core/dune-geometry * https://gitlab.dune-project.org/core/dune-grid * https://gitlab.dune-project.org/core/dune-istl * https://gitlab.dune-project.org/core/dune-localfunctions * https://gitlab.dune-project.org/staging/dune-typetree Release of _dune-functions_ happen together with releases of the Dune core modules, and use the same numbering. Unless explicitly stated otherwise for a specific version, _dune-functions_ supports/requires the same build tools (compilers, cmake) as the corresponding version of the core modules. ## Maintainers _dune-functions_ has been originally written by 1. Christian Engwer 2. Carsten Gräser 3. Steffen Müthing 4. Oliver Sander Additionally, significant contributions came from Ansgar Burchardt, Simon Praetorius, and many others. See the git history for a complete list. We welcome interest and contributions by additional developers. [core]: https://dune-project.org/groups/core [typetree]: https://gitlab.dune-project.org/staging/dune-typetree [dune docs]: https://dune-project.org/doxygen [functions paper arxiv]: https://arxiv.org/abs/1512.06136 [functions paper]: http://journals.ub.uni-heidelberg.de/index.php/ans/article/view/27683 [bases paper arxiv]: https://arxiv.org/abs/1806.09545 [bases paper]: https://arxiv.org/abs/1806.09545 [doxygen]: http://www.stack.nl/~dimitri/doxygen/ dune-functions-2.11.0+dfsg/TODO.md000066400000000000000000000045251513634022200165460ustar00rootroot00000000000000 # Open questions Feel free to put your wishes and questions into this file. It would be nice to prefix your statements with your initials or one more letter if needed. * OS: How do I specialize code for a particular basis? For example, how can I write an 'interpolate' method only for BSPlineBasis objects? ## Development ## Function space basis interface * Here is another point that needs future discussion: I just added the first implementation of a basis of a DG space. This one uses Lagrangian shape functions. I expect other DG spaces using other shape functions to appear eventually. Here is the issue: Currently, the LagrangeDGBasis has the shape function type hard-wired into the code. Any implementation for a DG space using a different set of shape functions will differ very little from it. Hence one may consider implementing a single DGBasis, and making the shape functions a template parameter. However, it is not clear what exactly this template parameter should be. It cannot be a LocalFiniteElement, because that would force us to pick a grid element type at compile time (you currently could not use the VirtualLocalFiniteElement here, because the code does not properly set it up). You cannot expect a factory here either, because then you cannot currently hard-wire the element type if that is what you want. I don't think this is a difficult issue, it just needs a bit of discussion. ## Function interface ### EntitySet interface * CaG: It seems that the EntitySet concept and GridViewEntitySet are candidates for dune-grid. ### GridFunction concept * CaG: Should GridFunction export the grid type? * CaG: If yes, should GridFunction store/export a pointer to the grid? * CaG: While LocalFunction is defined in a grid-agnostig way with local coordinates from a 'LocalContext'. In contrast to this the naming of GridFunction is linked to Grids: entitySet(), LocalContext=decltype(entitySet())::Element. There seems to be no need for this and we could simply rename this in a grid-agnostic way if someone comes up with better names than LocalizableFunction, localContextSet() dune-functions-2.11.0+dfsg/config.h.cmake000066400000000000000000000030361513634022200201500ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later /* begin dune-functions put the definitions for config.h specific to your project here. Everything above will be overwritten */ /* begin private */ /* Name of package */ #define PACKAGE "@DUNE_MOD_NAME@" /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "@DUNE_MAINTAINER@" /* Define to the full name of this package. */ #define PACKAGE_NAME "@DUNE_MOD_NAME@" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "@DUNE_MOD_NAME@ @DUNE_MOD_VERSION@" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "@DUNE_MOD_NAME@" /* Define to the home page for this package. */ #define PACKAGE_URL "@DUNE_MOD_URL@" /* Define to the version of this package. */ #define PACKAGE_VERSION "@DUNE_MOD_VERSION@" /* end private */ /* Define to the version of dune-functions */ #define DUNE_FUNCTIONS_VERSION "@DUNE_FUNCTIONS_VERSION@" /* Define to the major version of dune-functions */ #define DUNE_FUNCTIONS_VERSION_MAJOR @DUNE_FUNCTIONS_VERSION_MAJOR@ /* Define to the minor version of dune-functions */ #define DUNE_FUNCTIONS_VERSION_MINOR @DUNE_FUNCTIONS_VERSION_MINOR@ /* Define to the revision of dune-functions */ #define DUNE_FUNCTIONS_VERSION_REVISION @DUNE_FUNCTIONS_VERSION_REVISION@ /* end dune-functions Everything below here will be overwritten */ dune-functions-2.11.0+dfsg/doc/000077500000000000000000000000001513634022200162165ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/doc/CMakeLists.txt000066400000000000000000000003521513634022200207560ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory("doxygen") add_subdirectory("manual") dune-functions-2.11.0+dfsg/doc/doxygen/000077500000000000000000000000001513634022200176735ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/doc/doxygen/.gitignore000066400000000000000000000003631513634022200216650ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later Doxyfile Doxyfile.in doxyerr.log doxygen.log doxygen-tag html dune-functions-2.11.0+dfsg/doc/doxygen/CMakeLists.txt000066400000000000000000000003751513634022200224400ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # shortcut for creating the Doxyfile.in and Doxyfile add_doxygen_target() dune-functions-2.11.0+dfsg/doc/doxygen/Doxylocal000066400000000000000000000050731513634022200215610ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # This file contains local changes to the doxygen configuration # please us '+=' to add file/directories to the lists PROJECT_NAME = Dune-Functions PROJECT_LOGO = @top_srcdir@/doc/doxygen/dune-functions.png # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT += @top_srcdir@/dune/functions \ @top_srcdir@/doc/doxygen/mainpage.md \ @top_srcdir@/doc/doxygen/license.md \ @top_srcdir@/doc/doxygen/examples.md \ @top_srcdir@/doc/doxygen/examples/poisson-mfem-py.md \ @top_srcdir@/doc/doxygen/examples/poisson-pq2-cpp.md \ @top_srcdir@/doc/doxygen/examples/poisson-pq2-py.md \ @top_srcdir@/doc/doxygen/doxygen_groups.hh USE_MDFILE_AS_MAINPAGE = @top_srcdir@/doc/doxygen/mainpage.md # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # EXCLUDE += @top_srcdir@/dune/functions/test # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH += @top_srcdir@/examples EXAMPLE_PATH += @top_srcdir@/LICENSES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). # IMAGE_PATH += @top_srcdir@/dune/functions/pics # Generate documentation for friend functions HIDE_FRIEND_COMPOUNDS = NO EXTRA_PACKAGES = amsmath amssymb USE_MATHJAX = YES GENERATE_TODOLIST = NO GENERATE_TREEVIEW = YES DISABLE_INDEX = NO FULL_SIDEBAR = NO HTML_EXTRA_STYLESHEET = @top_srcdir@/doc/doxygen/doxygen-awesome/doxygen-awesome.css \ @top_srcdir@/doc/doxygen/doxygen-awesome/doxygen-awesome-sidebar-only.css \ @top_srcdir@/doc/doxygen/custom.css HTML_COLORSTYLE = LIGHT TREEVIEW_WIDTH = 300 dune-functions-2.11.0+dfsg/doc/doxygen/custom.css000066400000000000000000000003571513634022200217240ustar00rootroot00000000000000/* SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md */ /* SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later */ html { --side-nav-fixed-width: 300px; } dune-functions-2.11.0+dfsg/doc/doxygen/doxygen-awesome/000077500000000000000000000000001513634022200230065ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/doc/doxygen/doxygen-awesome/doxygen-awesome-sidebar-only.css000066400000000000000000000065271513634022200312330ustar00rootroot00000000000000/* SPDX-FileCopyrightText: Copyright © 2021 - 2023 jothepro */ /* SPDX-License-Identifier: MIT */ /** Doxygen Awesome https://github.com/jothepro/doxygen-awesome-css MIT License Copyright (c) 2021 - 2023 jothepro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ html { /* side nav width. MUST be = `TREEVIEW_WIDTH`. * Make sure it is wide enough to contain the page title (logo + title + version) */ --side-nav-fixed-width: 335px; --menu-display: none; --top-height: 120px; --toc-sticky-top: -25px; --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px); } #projectname { white-space: nowrap; } @media screen and (min-width: 768px) { html { --searchbar-background: var(--page-background-color); } #side-nav { min-width: var(--side-nav-fixed-width); max-width: var(--side-nav-fixed-width); top: var(--top-height); overflow: visible; } #nav-tree, #side-nav { height: calc(100vh - var(--top-height)) !important; } #nav-tree { padding: 0; } #top { display: block; border-bottom: none; height: var(--top-height); margin-bottom: calc(0px - var(--top-height)); max-width: var(--side-nav-fixed-width); overflow: hidden; background: var(--side-nav-background); } #main-nav { float: left; padding-right: 0; } .ui-resizable-handle { cursor: default; width: 1px !important; background: var(--separator-color); box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color); } #nav-path { position: fixed; right: 0; left: var(--side-nav-fixed-width); bottom: 0; width: auto; } #doc-content { height: calc(100vh - 31px) !important; padding-bottom: calc(3 * var(--spacing-large)); padding-top: calc(var(--top-height) - 80px); box-sizing: border-box; margin-left: var(--side-nav-fixed-width) !important; } #MSearchBox { width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium))); } #MSearchField { width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px); } #MSearchResultsWindow { left: var(--spacing-medium) !important; right: auto; } } dune-functions-2.11.0+dfsg/doc/doxygen/doxygen-awesome/doxygen-awesome.css000066400000000000000000002041351513634022200266400ustar00rootroot00000000000000/* SPDX-FileCopyrightText: Copyright © 2021 - 2023 jothepro */ /* SPDX-License-Identifier: MIT */ /** Doxygen Awesome https://github.com/jothepro/doxygen-awesome-css MIT License Copyright (c) 2021 - 2023 jothepro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ html { /* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */ --primary-color: #1779c4; --primary-dark-color: #335c80; --primary-light-color: #70b1e9; --on-primary-color: #ffffff; /* page base colors */ --page-background-color: #ffffff; --page-foreground-color: #2f4153; --page-secondary-foreground-color: #6f7e8e; /* color for all separators on the website: hr, borders, ... */ --separator-color: #dedede; /* border radius for all rounded components. Will affect many components, like dropdowns, memitems, codeblocks, ... */ --border-radius-large: 8px; --border-radius-small: 4px; --border-radius-medium: 6px; /* default spacings. Most components reference these values for spacing, to provide uniform spacing on the page. */ --spacing-small: 5px; --spacing-medium: 10px; --spacing-large: 16px; /* default box shadow used for raising an element above the normal content. Used in dropdowns, search result, ... */ --box-shadow: 0 2px 8px 0 rgba(0,0,0,.075); --odd-color: rgba(0,0,0,.028); /* font-families. will affect all text on the website * font-family: the normal font for text, headlines, menus * font-family-monospace: used for preformatted text in memtitle, code, fragments */ --font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif; --font-family-monospace: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; /* font sizes */ --page-font-size: 15.6px; --navigation-font-size: 14.4px; --toc-font-size: 13.4px; --code-font-size: 14px; /* affects code, fragment */ --title-font-size: 22px; /* content text properties. These only affect the page content, not the navigation or any other ui elements */ --content-line-height: 27px; /* The content is centered and constraint in it's width. To make the content fill the whole page, set the variable to auto.*/ --content-maxwidth: 1050px; --table-line-height: 24px; --toc-sticky-top: var(--spacing-medium); --toc-width: 200px; --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 85px); /* colors for various content boxes: @warning, @note, @deprecated @bug */ --warning-color: #faf3d8; --warning-color-dark: #f3a600; --warning-color-darker: #5f4204; --note-color: #e4f3ff; --note-color-dark: #1879C4; --note-color-darker: #274a5c; --todo-color: #e4dafd; --todo-color-dark: #5b2bdd; --todo-color-darker: #2a0d72; --deprecated-color: #ecf0f3; --deprecated-color-dark: #5b6269; --deprecated-color-darker: #43454a; --bug-color: #f8d1cc; --bug-color-dark: #b61825; --bug-color-darker: #75070f; --invariant-color: #d8f1e3; --invariant-color-dark: #44b86f; --invariant-color-darker: #265532; /* blockquote colors */ --blockquote-background: #f8f9fa; --blockquote-foreground: #636568; /* table colors */ --tablehead-background: #f1f1f1; --tablehead-foreground: var(--page-foreground-color); /* menu-display: block | none * Visibility of the top navigation on screens >= 768px. On smaller screen the menu is always visible. * `GENERATE_TREEVIEW` MUST be enabled! */ --menu-display: block; --menu-focus-foreground: var(--on-primary-color); --menu-focus-background: var(--primary-color); --menu-selected-background: rgba(0,0,0,.05); --header-background: var(--page-background-color); --header-foreground: var(--page-foreground-color); /* searchbar colors */ --searchbar-background: var(--side-nav-background); --searchbar-foreground: var(--page-foreground-color); /* searchbar size * (`searchbar-width` is only applied on screens >= 768px. * on smaller screens the searchbar will always fill the entire screen width) */ --searchbar-height: 33px; --searchbar-width: 210px; --searchbar-border-radius: var(--searchbar-height); /* code block colors */ --code-background: #f5f5f5; --code-foreground: var(--page-foreground-color); /* fragment colors */ --fragment-background: #F8F9FA; --fragment-foreground: #37474F; --fragment-keyword: #bb6bb2; --fragment-keywordtype: #8258b3; --fragment-keywordflow: #d67c3b; --fragment-token: #438a59; --fragment-comment: #969696; --fragment-link: #5383d6; --fragment-preprocessor: #46aaa5; --fragment-linenumber-color: #797979; --fragment-linenumber-background: #f4f4f5; --fragment-linenumber-border: #e3e5e7; --fragment-lineheight: 20px; /* sidebar navigation (treeview) colors */ --side-nav-background: #fbfbfb; --side-nav-foreground: var(--page-foreground-color); --side-nav-arrow-opacity: 0; --side-nav-arrow-hover-opacity: 0.9; --toc-background: var(--side-nav-background); --toc-foreground: var(--side-nav-foreground); /* height of an item in any tree / collapsible table */ --tree-item-height: 30px; --memname-font-size: var(--code-font-size); --memtitle-font-size: 18px; --webkit-scrollbar-size: 7px; --webkit-scrollbar-padding: 4px; --webkit-scrollbar-color: var(--separator-color); --animation-duration: .12s } @media screen and (max-width: 767px) { html { --page-font-size: 16px; --navigation-font-size: 16px; --toc-font-size: 15px; --code-font-size: 15px; /* affects code, fragment */ --title-font-size: 22px; } } @media (prefers-color-scheme: dark) { html:not(.light-mode) { color-scheme: dark; --primary-color: #1982d2; --primary-dark-color: #86a9c4; --primary-light-color: #4779ac; --box-shadow: 0 2px 8px 0 rgba(0,0,0,.35); --odd-color: rgba(100,100,100,.06); --menu-selected-background: rgba(0,0,0,.4); --page-background-color: #1C1D1F; --page-foreground-color: #d2dbde; --page-secondary-foreground-color: #859399; --separator-color: #38393b; --side-nav-background: #252628; --code-background: #2a2c2f; --tablehead-background: #2a2c2f; --blockquote-background: #222325; --blockquote-foreground: #7e8c92; --warning-color: #3b2e04; --warning-color-dark: #f1b602; --warning-color-darker: #ceb670; --note-color: #163750; --note-color-dark: #1982D2; --note-color-darker: #dcf0fa; --todo-color: #2a2536; --todo-color-dark: #7661b3; --todo-color-darker: #ae9ed6; --deprecated-color: #2e323b; --deprecated-color-dark: #738396; --deprecated-color-darker: #abb0bd; --bug-color: #2e1917; --bug-color-dark: #ad2617; --bug-color-darker: #f5b1aa; --invariant-color: #303a35; --invariant-color-dark: #76ce96; --invariant-color-darker: #cceed5; --fragment-background: #282c34; --fragment-foreground: #dbe4eb; --fragment-keyword: #cc99cd; --fragment-keywordtype: #ab99cd; --fragment-keywordflow: #e08000; --fragment-token: #7ec699; --fragment-comment: #999999; --fragment-link: #98c0e3; --fragment-preprocessor: #65cabe; --fragment-linenumber-color: #cccccc; --fragment-linenumber-background: #35393c; --fragment-linenumber-border: #1f1f1f; } } /* dark mode variables are defined twice, to support both the dark-mode without and with doxygen-awesome-darkmode-toggle.js */ html.dark-mode { color-scheme: dark; --primary-color: #1982d2; --primary-dark-color: #86a9c4; --primary-light-color: #4779ac; --box-shadow: 0 2px 8px 0 rgba(0,0,0,.30); --odd-color: rgba(100,100,100,.06); --menu-selected-background: rgba(0,0,0,.4); --page-background-color: #1C1D1F; --page-foreground-color: #d2dbde; --page-secondary-foreground-color: #859399; --separator-color: #38393b; --side-nav-background: #252628; --code-background: #2a2c2f; --tablehead-background: #2a2c2f; --blockquote-background: #222325; --blockquote-foreground: #7e8c92; --warning-color: #3b2e04; --warning-color-dark: #f1b602; --warning-color-darker: #ceb670; --note-color: #163750; --note-color-dark: #1982D2; --note-color-darker: #dcf0fa; --todo-color: #2a2536; --todo-color-dark: #7661b3; --todo-color-darker: #ae9ed6; --deprecated-color: #2e323b; --deprecated-color-dark: #738396; --deprecated-color-darker: #abb0bd; --bug-color: #2e1917; --bug-color-dark: #ad2617; --bug-color-darker: #f5b1aa; --invariant-color: #303a35; --invariant-color-dark: #76ce96; --invariant-color-darker: #cceed5; --fragment-background: #282c34; --fragment-foreground: #dbe4eb; --fragment-keyword: #cc99cd; --fragment-keywordtype: #ab99cd; --fragment-keywordflow: #e08000; --fragment-token: #7ec699; --fragment-comment: #999999; --fragment-link: #98c0e3; --fragment-preprocessor: #65cabe; --fragment-linenumber-color: #cccccc; --fragment-linenumber-background: #35393c; --fragment-linenumber-border: #1f1f1f; } body { color: var(--page-foreground-color); background-color: var(--page-background-color); font-size: var(--page-font-size); } body, table, div, p, dl, #nav-tree .label, .title, .sm-dox a, .sm-dox a:hover, .sm-dox a:focus, #projectname, .SelectItem, #MSearchField, .navpath li.navelem a, .navpath li.navelem a:hover, p.reference, p.definition, div.toc li, div.toc h3 { font-family: var(--font-family); } h1, h2, h3, h4, h5 { margin-top: 1em; font-weight: 600; line-height: initial; } p, div, table, dl, p.reference, p.definition { font-size: var(--page-font-size); } p.reference, p.definition { color: var(--page-secondary-foreground-color); } a:link, a:visited, a:hover, a:focus, a:active { color: var(--primary-color) !important; font-weight: 500; background: none; } a.anchor { scroll-margin-top: var(--spacing-large); display: block; } /* Title and top navigation */ #top { background: var(--header-background); border-bottom: 1px solid var(--separator-color); } @media screen and (min-width: 768px) { #top { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; } } #main-nav { flex-grow: 5; padding: var(--spacing-small) var(--spacing-medium); } #titlearea { width: auto; padding: var(--spacing-medium) var(--spacing-large); background: none; color: var(--header-foreground); border-bottom: none; } @media screen and (max-width: 767px) { #titlearea { padding-bottom: var(--spacing-small); } } #titlearea table tbody tr { height: auto !important; } #projectname { font-size: var(--title-font-size); font-weight: 600; } #projectnumber { font-family: inherit; font-size: 60%; } #projectbrief { font-family: inherit; font-size: 80%; } #projectlogo { vertical-align: middle; } #projectlogo img { max-height: calc(var(--title-font-size) * 2); margin-right: var(--spacing-small); } .sm-dox, .tabs, .tabs2, .tabs3 { background: none; padding: 0; } .tabs, .tabs2, .tabs3 { border-bottom: 1px solid var(--separator-color); margin-bottom: -1px; } .main-menu-btn-icon, .main-menu-btn-icon:before, .main-menu-btn-icon:after { background: var(--page-secondary-foreground-color); } @media screen and (max-width: 767px) { .sm-dox a span.sub-arrow { background: var(--code-background); } #main-menu a.has-submenu span.sub-arrow { color: var(--page-secondary-foreground-color); border-radius: var(--border-radius-medium); } #main-menu a.has-submenu:hover span.sub-arrow { color: var(--page-foreground-color); } } @media screen and (min-width: 768px) { .sm-dox li, .tablist li { display: var(--menu-display); } .sm-dox a span.sub-arrow { border-color: var(--header-foreground) transparent transparent transparent; } .sm-dox a:hover span.sub-arrow { border-color: var(--menu-focus-foreground) transparent transparent transparent; } .sm-dox ul a span.sub-arrow { border-color: transparent transparent transparent var(--page-foreground-color); } .sm-dox ul a:hover span.sub-arrow { border-color: transparent transparent transparent var(--menu-focus-foreground); } } .sm-dox ul { background: var(--page-background-color); box-shadow: var(--box-shadow); border: 1px solid var(--separator-color); border-radius: var(--border-radius-medium) !important; padding: var(--spacing-small); animation: ease-out 150ms slideInMenu; } @keyframes slideInMenu { from { opacity: 0; transform: translate(0px, -2px); } to { opacity: 1; transform: translate(0px, 0px); } } .sm-dox ul a { color: var(--page-foreground-color) !important; background: var(--page-background-color); font-size: var(--navigation-font-size); } .sm-dox>li>ul:after { border-bottom-color: var(--page-background-color) !important; } .sm-dox>li>ul:before { border-bottom-color: var(--separator-color) !important; } .sm-dox ul a:hover, .sm-dox ul a:active, .sm-dox ul a:focus { font-size: var(--navigation-font-size) !important; color: var(--menu-focus-foreground) !important; text-shadow: none; background-color: var(--menu-focus-background); border-radius: var(--border-radius-small) !important; } .sm-dox a, .sm-dox a:focus, .tablist li, .tablist li a, .tablist li.current a { text-shadow: none; background: transparent; background-image: none !important; color: var(--header-foreground) !important; font-weight: normal; font-size: var(--navigation-font-size); border-radius: var(--border-radius-small) !important; } .sm-dox a:focus { outline: auto; } .sm-dox a:hover, .sm-dox a:active, .tablist li a:hover { text-shadow: none; font-weight: normal; background: var(--menu-focus-background); color: var(--menu-focus-foreground) !important; border-radius: var(--border-radius-small) !important; font-size: var(--navigation-font-size); } .tablist li.current { border-radius: var(--border-radius-small); background: var(--menu-selected-background); } .tablist li { margin: var(--spacing-small) 0 var(--spacing-small) var(--spacing-small); } .tablist a { padding: 0 var(--spacing-large); } /* Search box */ #MSearchBox { height: var(--searchbar-height); background: var(--searchbar-background); border-radius: var(--searchbar-border-radius); border: 1px solid var(--separator-color); overflow: hidden; width: var(--searchbar-width); position: relative; box-shadow: none; display: block; margin-top: 0; } /* until Doxygen 1.9.4 */ .left img#MSearchSelect { left: 0; user-select: none; padding-left: 8px; } /* Doxygen 1.9.5 */ .left span#MSearchSelect { left: 0; user-select: none; margin-left: 8px; padding: 0; } .left #MSearchSelect[src$=".png"] { padding-left: 0 } .SelectionMark { user-select: none; } .tabs .left #MSearchSelect { padding-left: 0; } .tabs #MSearchBox { position: absolute; right: var(--spacing-medium); } @media screen and (max-width: 767px) { .tabs #MSearchBox { position: relative; right: 0; margin-left: var(--spacing-medium); margin-top: 0; } } #MSearchSelectWindow, #MSearchResultsWindow { z-index: 9999; } #MSearchBox.MSearchBoxActive { border-color: var(--primary-color); box-shadow: inset 0 0 0 1px var(--primary-color); } #main-menu > li:last-child { margin-right: 0; } @media screen and (max-width: 767px) { #main-menu > li:last-child { height: 50px; } } #MSearchField { font-size: var(--navigation-font-size); height: calc(var(--searchbar-height) - 2px); background: transparent; width: calc(var(--searchbar-width) - 64px); } .MSearchBoxActive #MSearchField { color: var(--searchbar-foreground); } #MSearchSelect { top: calc(calc(var(--searchbar-height) / 2) - 11px); } #MSearchBox span.left, #MSearchBox span.right { background: none; background-image: none; } #MSearchBox span.right { padding-top: calc(calc(var(--searchbar-height) / 2) - 12px); position: absolute; right: var(--spacing-small); } .tabs #MSearchBox span.right { top: calc(calc(var(--searchbar-height) / 2) - 12px); } @keyframes slideInSearchResults { from { opacity: 0; transform: translate(0, 15px); } to { opacity: 1; transform: translate(0, 20px); } } #MSearchResultsWindow { left: auto !important; right: var(--spacing-medium); border-radius: var(--border-radius-large); border: 1px solid var(--separator-color); transform: translate(0, 20px); box-shadow: var(--box-shadow); animation: ease-out 280ms slideInSearchResults; background: var(--page-background-color); } iframe#MSearchResults { margin: 4px; } iframe { color-scheme: normal; } @media (prefers-color-scheme: dark) { html:not(.light-mode) iframe#MSearchResults { filter: invert() hue-rotate(180deg); } } html.dark-mode iframe#MSearchResults { filter: invert() hue-rotate(180deg); } #MSearchResults .SRPage { background-color: transparent; } #MSearchResults .SRPage .SREntry { font-size: 10pt; padding: var(--spacing-small) var(--spacing-medium); } #MSearchSelectWindow { border: 1px solid var(--separator-color); border-radius: var(--border-radius-medium); box-shadow: var(--box-shadow); background: var(--page-background-color); padding-top: var(--spacing-small); padding-bottom: var(--spacing-small); } #MSearchSelectWindow a.SelectItem { font-size: var(--navigation-font-size); line-height: var(--content-line-height); margin: 0 var(--spacing-small); border-radius: var(--border-radius-small); color: var(--page-foreground-color) !important; font-weight: normal; } #MSearchSelectWindow a.SelectItem:hover { background: var(--menu-focus-background); color: var(--menu-focus-foreground) !important; } @media screen and (max-width: 767px) { #MSearchBox { margin-top: var(--spacing-medium); margin-bottom: var(--spacing-medium); width: calc(100vw - 30px); } #main-menu > li:last-child { float: none !important; } #MSearchField { width: calc(100vw - 110px); } @keyframes slideInSearchResultsMobile { from { opacity: 0; transform: translate(0, 15px); } to { opacity: 1; transform: translate(0, 20px); } } #MSearchResultsWindow { left: var(--spacing-medium) !important; right: var(--spacing-medium); overflow: auto; transform: translate(0, 20px); animation: ease-out 280ms slideInSearchResultsMobile; width: auto !important; } /* * Overwrites for fixing the searchbox on mobile in doxygen 1.9.2 */ label.main-menu-btn ~ #searchBoxPos1 { top: 3px !important; right: 6px !important; left: 45px; display: flex; } label.main-menu-btn ~ #searchBoxPos1 > #MSearchBox { margin-top: 0; margin-bottom: 0; flex-grow: 2; float: left; } } /* Tree view */ #side-nav { padding: 0 !important; background: var(--side-nav-background); min-width: 8px; max-width: 50vw; } @media screen and (max-width: 767px) { #side-nav { display: none; } #doc-content { margin-left: 0 !important; } } #nav-tree { background: transparent; margin-right: 1px; } #nav-tree .label { font-size: var(--navigation-font-size); } #nav-tree .item { height: var(--tree-item-height); line-height: var(--tree-item-height); overflow: hidden; text-overflow: ellipsis; } #nav-tree .item > a:focus { outline: none; } #nav-sync { bottom: 12px; right: 12px; top: auto !important; user-select: none; } #nav-tree .selected { text-shadow: none; background-image: none; background-color: transparent; position: relative; color: var(--primary-color) !important; font-weight: 500; } #nav-tree .selected::after { content: ""; position: absolute; top: 1px; bottom: 1px; left: 0; width: 4px; border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; background: var(--primary-color); } #nav-tree a { color: var(--side-nav-foreground) !important; font-weight: normal; } #nav-tree a:focus { outline-style: auto; } #nav-tree .arrow { opacity: var(--side-nav-arrow-opacity); background: none; } .arrow { color: inherit; cursor: pointer; font-size: 45%; vertical-align: middle; margin-right: 2px; font-family: serif; height: auto; text-align: right; } #nav-tree div.item:hover .arrow, #nav-tree a:focus .arrow { opacity: var(--side-nav-arrow-hover-opacity); } #nav-tree .selected a { color: var(--primary-color) !important; font-weight: bolder; font-weight: 600; } .ui-resizable-e { width: 4px; background: transparent; box-shadow: inset -1px 0 0 0 var(--separator-color); } /* Contents */ div.header { border-bottom: 1px solid var(--separator-color); background-color: var(--page-background-color); background-image: none; } @media screen and (min-width: 1000px) { #doc-content > div > div.contents, .PageDoc > div.contents { display: flex; flex-direction: row-reverse; flex-wrap: nowrap; align-items: flex-start; } div.contents .textblock { min-width: 200px; flex-grow: 1; } } div.contents, div.header .title, div.header .summary { max-width: var(--content-maxwidth); } div.contents, div.header .title { line-height: initial; margin: calc(var(--spacing-medium) + .2em) auto var(--spacing-medium) auto; } div.header .summary { margin: var(--spacing-medium) auto 0 auto; } div.headertitle { padding: 0; } div.header .title { font-weight: 600; font-size: 225%; padding: var(--spacing-medium) var(--spacing-large); word-break: break-word; } div.header .summary { width: auto; display: block; float: none; padding: 0 var(--spacing-large); } td.memSeparator { border-color: var(--separator-color); } span.mlabel { background: var(--primary-color); color: var(--on-primary-color); border: none; padding: 4px 9px; border-radius: 12px; margin-right: var(--spacing-medium); } span.mlabel:last-of-type { margin-right: 2px; } div.contents { padding: 0 var(--spacing-large); } div.contents p, div.contents li { line-height: var(--content-line-height); } div.contents div.dyncontent { margin: var(--spacing-medium) 0; } @media (prefers-color-scheme: dark) { html:not(.light-mode) div.contents div.dyncontent img, html:not(.light-mode) div.contents center img, html:not(.light-mode) div.contents > table img, html:not(.light-mode) div.contents div.dyncontent iframe, html:not(.light-mode) div.contents center iframe, html:not(.light-mode) div.contents table iframe, html:not(.light-mode) div.contents .dotgraph iframe { filter: brightness(89%) hue-rotate(180deg) invert(); } } html.dark-mode div.contents div.dyncontent img, html.dark-mode div.contents center img, html.dark-mode div.contents > table img, html.dark-mode div.contents div.dyncontent iframe, html.dark-mode div.contents center iframe, html.dark-mode div.contents table iframe, html.dark-mode div.contents .dotgraph iframe { filter: brightness(89%) hue-rotate(180deg) invert(); } h2.groupheader { border-bottom: 0px; color: var(--page-foreground-color); box-shadow: 100px 0 var(--page-background-color), -100px 0 var(--page-background-color), 100px 0.75px var(--separator-color), -100px 0.75px var(--separator-color), 500px 0 var(--page-background-color), -500px 0 var(--page-background-color), 500px 0.75px var(--separator-color), -500px 0.75px var(--separator-color), 900px 0 var(--page-background-color), -900px 0 var(--page-background-color), 900px 0.75px var(--separator-color), -900px 0.75px var(--separator-color), 1400px 0 var(--page-background-color), -1400px 0 var(--page-background-color), 1400px 0.75px var(--separator-color), -1400px 0.75px var(--separator-color), 1900px 0 var(--page-background-color), -1900px 0 var(--page-background-color), 1900px 0.75px var(--separator-color), -1900px 0.75px var(--separator-color); } blockquote { margin: 0 var(--spacing-medium) 0 var(--spacing-medium); padding: var(--spacing-small) var(--spacing-large); background: var(--blockquote-background); color: var(--blockquote-foreground); border-left: 0; overflow: visible; border-radius: var(--border-radius-medium); overflow: visible; position: relative; } blockquote::before, blockquote::after { font-weight: bold; font-family: serif; font-size: 360%; opacity: .15; position: absolute; } blockquote::before { content: "“"; left: -10px; top: 4px; } blockquote::after { content: "”"; right: -8px; bottom: -25px; } blockquote p { margin: var(--spacing-small) 0 var(--spacing-medium) 0; } .paramname, .paramname em { font-weight: 600; color: var(--primary-dark-color); } .paramname > code { border: 0; } table.params .paramname { font-weight: 600; font-family: var(--font-family-monospace); font-size: var(--code-font-size); padding-right: var(--spacing-small); line-height: var(--table-line-height); } h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { text-shadow: 0 0 15px var(--primary-light-color); } .alphachar a { color: var(--page-foreground-color); } .dotgraph { max-width: 100%; overflow-x: scroll; } .dotgraph .caption { position: sticky; left: 0; } /* Wrap Graphviz graphs with the `interactive_dotgraph` class if `INTERACTIVE_SVG = YES` */ .interactive_dotgraph .dotgraph iframe { max-width: 100%; } /* Table of Contents */ div.contents .toc { max-height: var(--toc-max-height); min-width: var(--toc-width); border: 0; border-left: 1px solid var(--separator-color); border-radius: 0; background-color: var(--page-background-color); box-shadow: none; position: sticky; top: var(--toc-sticky-top); padding: 0 var(--spacing-large); margin: var(--spacing-small) 0 var(--spacing-large) var(--spacing-large); } div.toc h3 { color: var(--toc-foreground); font-size: var(--navigation-font-size); margin: var(--spacing-large) 0 var(--spacing-medium) 0; } div.toc li { padding: 0; background: none; line-height: var(--toc-font-size); margin: var(--toc-font-size) 0 0 0; } div.toc li::before { display: none; } div.toc ul { margin-top: 0 } div.toc li a { font-size: var(--toc-font-size); color: var(--page-foreground-color) !important; text-decoration: none; } div.toc li a:hover, div.toc li a.active { color: var(--primary-color) !important; } div.toc li a.aboveActive { color: var(--page-secondary-foreground-color) !important; } @media screen and (max-width: 999px) { div.contents .toc { max-height: 45vh; float: none; width: auto; margin: 0 0 var(--spacing-medium) 0; position: relative; top: 0; position: relative; border: 1px solid var(--separator-color); border-radius: var(--border-radius-medium); background-color: var(--toc-background); box-shadow: var(--box-shadow); } div.contents .toc.interactive { max-height: calc(var(--navigation-font-size) + 2 * var(--spacing-large)); overflow: hidden; } div.contents .toc > h3 { -webkit-tap-highlight-color: transparent; cursor: pointer; position: sticky; top: 0; background-color: var(--toc-background); margin: 0; padding: var(--spacing-large) 0; display: block; } div.contents .toc.interactive > h3::before { content: ""; width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid var(--primary-color); display: inline-block; margin-right: var(--spacing-small); margin-bottom: calc(var(--navigation-font-size) / 4); transform: rotate(-90deg); transition: transform var(--animation-duration) ease-out; } div.contents .toc.interactive.open > h3::before { transform: rotate(0deg); } div.contents .toc.interactive.open { max-height: 45vh; overflow: auto; transition: max-height 0.2s ease-in-out; } div.contents .toc a, div.contents .toc a.active { color: var(--primary-color) !important; } div.contents .toc a:hover { text-decoration: underline; } } /* Code & Fragments */ code, div.fragment, pre.fragment { border-radius: var(--border-radius-small); border: 1px solid var(--separator-color); overflow: hidden; } code { display: inline; background: var(--code-background); color: var(--code-foreground); padding: 2px 6px; } div.fragment, pre.fragment { margin: var(--spacing-medium) 0; padding: calc(var(--spacing-large) - (var(--spacing-large) / 6)) var(--spacing-large); background: var(--fragment-background); color: var(--fragment-foreground); overflow-x: auto; } @media screen and (max-width: 767px) { div.fragment, pre.fragment { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right: 0; } .contents > div.fragment, .textblock > div.fragment, .textblock > pre.fragment, .textblock > .tabbed > ul > li > div.fragment, .textblock > .tabbed > ul > li > pre.fragment, .contents > .doxygen-awesome-fragment-wrapper > div.fragment, .textblock > .doxygen-awesome-fragment-wrapper > div.fragment, .textblock > .doxygen-awesome-fragment-wrapper > pre.fragment, .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > div.fragment, .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > pre.fragment { margin: var(--spacing-medium) calc(0px - var(--spacing-large)); border-radius: 0; border-left: 0; } .textblock li > .fragment, .textblock li > .doxygen-awesome-fragment-wrapper > .fragment { margin: var(--spacing-medium) calc(0px - var(--spacing-large)); } .memdoc li > .fragment, .memdoc li > .doxygen-awesome-fragment-wrapper > .fragment { margin: var(--spacing-medium) calc(0px - var(--spacing-medium)); } .textblock ul, .memdoc ul { overflow: initial; } .memdoc > div.fragment, .memdoc > pre.fragment, dl dd > div.fragment, dl dd pre.fragment, .memdoc > .doxygen-awesome-fragment-wrapper > div.fragment, .memdoc > .doxygen-awesome-fragment-wrapper > pre.fragment, dl dd > .doxygen-awesome-fragment-wrapper > div.fragment, dl dd .doxygen-awesome-fragment-wrapper > pre.fragment { margin: var(--spacing-medium) calc(0px - var(--spacing-medium)); border-radius: 0; border-left: 0; } } code, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span { font-family: var(--font-family-monospace); font-size: var(--code-font-size) !important; } div.line:after { margin-right: var(--spacing-medium); } div.fragment .line, pre.fragment { white-space: pre; word-wrap: initial; line-height: var(--fragment-lineheight); } div.fragment span.keyword { color: var(--fragment-keyword); } div.fragment span.keywordtype { color: var(--fragment-keywordtype); } div.fragment span.keywordflow { color: var(--fragment-keywordflow); } div.fragment span.stringliteral { color: var(--fragment-token) } div.fragment span.comment { color: var(--fragment-comment); } div.fragment a.code { color: var(--fragment-link) !important; } div.fragment span.preprocessor { color: var(--fragment-preprocessor); } div.fragment span.lineno { display: inline-block; width: 27px; border-right: none; background: var(--fragment-linenumber-background); color: var(--fragment-linenumber-color); } div.fragment span.lineno a { background: none; color: var(--fragment-link) !important; } div.fragment > .line:first-child .lineno { box-shadow: -999999px 0px 0 999999px var(--fragment-linenumber-background), -999998px 0px 0 999999px var(--fragment-linenumber-border); background-color: var(--fragment-linenumber-background) !important; } div.line { border-radius: var(--border-radius-small); } div.line.glow { background-color: var(--primary-light-color); box-shadow: none; } /* dl warning, attention, note, deprecated, bug, ... */ dl.bug dt a, dl.deprecated dt a, dl.todo dt a { font-weight: bold !important; } dl.warning, dl.attention, dl.note, dl.deprecated, dl.bug, dl.invariant, dl.pre, dl.post, dl.todo, dl.remark { padding: var(--spacing-medium); margin: var(--spacing-medium) 0; color: var(--page-background-color); overflow: hidden; margin-left: 0; border-radius: var(--border-radius-small); } dl.section dd { margin-bottom: 2px; } dl.warning, dl.attention { background: var(--warning-color); border-left: 8px solid var(--warning-color-dark); color: var(--warning-color-darker); } dl.warning dt, dl.attention dt { color: var(--warning-color-dark); } dl.note, dl.remark { background: var(--note-color); border-left: 8px solid var(--note-color-dark); color: var(--note-color-darker); } dl.note dt, dl.remark dt { color: var(--note-color-dark); } dl.todo { background: var(--todo-color); border-left: 8px solid var(--todo-color-dark); color: var(--todo-color-darker); } dl.todo dt a { color: var(--todo-color-dark) !important; } dl.bug dt a { color: var(--todo-color-dark) !important; } dl.bug { background: var(--bug-color); border-left: 8px solid var(--bug-color-dark); color: var(--bug-color-darker); } dl.bug dt a { color: var(--bug-color-dark) !important; } dl.deprecated { background: var(--deprecated-color); border-left: 8px solid var(--deprecated-color-dark); color: var(--deprecated-color-darker); } dl.deprecated dt a { color: var(--deprecated-color-dark) !important; } dl.section dd, dl.bug dd, dl.deprecated dd, dl.todo dd { margin-inline-start: 0px; } dl.invariant, dl.pre, dl.post { background: var(--invariant-color); border-left: 8px solid var(--invariant-color-dark); color: var(--invariant-color-darker); } dl.invariant dt, dl.pre dt, dl.post dt { color: var(--invariant-color-dark); } /* memitem */ div.memdoc, div.memproto, h2.memtitle { box-shadow: none; background-image: none; border: none; } div.memdoc { padding: 0 var(--spacing-medium); background: var(--page-background-color); } h2.memtitle, div.memitem { border: 1px solid var(--separator-color); box-shadow: var(--box-shadow); } h2.memtitle { box-shadow: 0px var(--spacing-medium) 0 -1px var(--fragment-background), var(--box-shadow); } div.memitem { transition: none; } div.memproto, h2.memtitle { background: var(--fragment-background); } h2.memtitle { font-weight: 500; font-size: var(--memtitle-font-size); font-family: var(--font-family-monospace); border-bottom: none; border-top-left-radius: var(--border-radius-medium); border-top-right-radius: var(--border-radius-medium); word-break: break-all; position: relative; } h2.memtitle:after { content: ""; display: block; background: var(--fragment-background); height: var(--spacing-medium); bottom: calc(0px - var(--spacing-medium)); left: 0; right: -14px; position: absolute; border-top-right-radius: var(--border-radius-medium); } h2.memtitle > span.permalink { font-size: inherit; } h2.memtitle > span.permalink > a { text-decoration: none; padding-left: 3px; margin-right: -4px; user-select: none; display: inline-block; margin-top: -6px; } h2.memtitle > span.permalink > a:hover { color: var(--primary-dark-color) !important; } a:target + h2.memtitle, a:target + h2.memtitle + div.memitem { border-color: var(--primary-light-color); } div.memitem { border-top-right-radius: var(--border-radius-medium); border-bottom-right-radius: var(--border-radius-medium); border-bottom-left-radius: var(--border-radius-medium); overflow: hidden; display: block !important; } div.memdoc { border-radius: 0; } div.memproto { border-radius: 0 var(--border-radius-small) 0 0; overflow: auto; border-bottom: 1px solid var(--separator-color); padding: var(--spacing-medium); margin-bottom: -1px; } div.memtitle { border-top-right-radius: var(--border-radius-medium); border-top-left-radius: var(--border-radius-medium); } div.memproto table.memname { font-family: var(--font-family-monospace); color: var(--page-foreground-color); font-size: var(--memname-font-size); text-shadow: none; } div.memproto div.memtemplate { font-family: var(--font-family-monospace); color: var(--primary-dark-color); font-size: var(--memname-font-size); margin-left: 2px; text-shadow: none; } table.mlabels, table.mlabels > tbody { display: block; } td.mlabels-left { width: auto; } td.mlabels-right { margin-top: 3px; position: sticky; left: 0; } table.mlabels > tbody > tr:first-child { display: flex; justify-content: space-between; flex-wrap: wrap; } .memname, .memitem span.mlabels { margin: 0 } /* reflist */ dl.reflist { box-shadow: var(--box-shadow); border-radius: var(--border-radius-medium); border: 1px solid var(--separator-color); overflow: hidden; padding: 0; } dl.reflist dt, dl.reflist dd { box-shadow: none; text-shadow: none; background-image: none; border: none; padding: 12px; } dl.reflist dt { font-weight: 500; border-radius: 0; background: var(--code-background); border-bottom: 1px solid var(--separator-color); color: var(--page-foreground-color) } dl.reflist dd { background: none; } /* Table */ .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname), .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody { display: inline-block; max-width: 100%; } .contents > table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname):not(.classindex) { margin-left: calc(0px - var(--spacing-large)); margin-right: calc(0px - var(--spacing-large)); max-width: calc(100% + 2 * var(--spacing-large)); } table.fieldtable, table.markdownTable tbody, table.doxtable tbody { border: none; margin: var(--spacing-medium) 0; box-shadow: 0 0 0 1px var(--separator-color); border-radius: var(--border-radius-small); } table.markdownTable, table.doxtable, table.fieldtable { padding: 1px; } table.doxtable caption { display: block; } table.fieldtable { border-collapse: collapse; width: 100%; } th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone, table.doxtable th { background: var(--tablehead-background); color: var(--tablehead-foreground); font-weight: 600; font-size: var(--page-font-size); } th.markdownTableHeadLeft:first-child, th.markdownTableHeadRight:first-child, th.markdownTableHeadCenter:first-child, th.markdownTableHeadNone:first-child, table.doxtable tr th:first-child { border-top-left-radius: var(--border-radius-small); } th.markdownTableHeadLeft:last-child, th.markdownTableHeadRight:last-child, th.markdownTableHeadCenter:last-child, th.markdownTableHeadNone:last-child, table.doxtable tr th:last-child { border-top-right-radius: var(--border-radius-small); } table.markdownTable td, table.markdownTable th, table.fieldtable td, table.fieldtable th, table.doxtable td, table.doxtable th { border: 1px solid var(--separator-color); padding: var(--spacing-small) var(--spacing-medium); } table.markdownTable td:last-child, table.markdownTable th:last-child, table.fieldtable td:last-child, table.fieldtable th:last-child, table.doxtable td:last-child, table.doxtable th:last-child { border-right: none; } table.markdownTable td:first-child, table.markdownTable th:first-child, table.fieldtable td:first-child, table.fieldtable th:first-child, table.doxtable td:first-child, table.doxtable th:first-child { border-left: none; } table.markdownTable tr:first-child td, table.markdownTable tr:first-child th, table.fieldtable tr:first-child td, table.fieldtable tr:first-child th, table.doxtable tr:first-child td, table.doxtable tr:first-child th { border-top: none; } table.markdownTable tr:last-child td, table.markdownTable tr:last-child th, table.fieldtable tr:last-child td, table.fieldtable tr:last-child th, table.doxtable tr:last-child td, table.doxtable tr:last-child th { border-bottom: none; } table.markdownTable tr, table.doxtable tr { border-bottom: 1px solid var(--separator-color); } table.markdownTable tr:last-child, table.doxtable tr:last-child { border-bottom: none; } .full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) { display: block; } .full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody { display: table; width: 100%; } table.fieldtable th { font-size: var(--page-font-size); font-weight: 600; background-image: none; background-color: var(--tablehead-background); color: var(--tablehead-foreground); } table.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fieldinit, .fieldtable td.fielddoc, .fieldtable th { border-bottom: 1px solid var(--separator-color); border-right: 1px solid var(--separator-color); } table.fieldtable tr:last-child td:first-child { border-bottom-left-radius: var(--border-radius-small); } table.fieldtable tr:last-child td:last-child { border-bottom-right-radius: var(--border-radius-small); } .memberdecls td.glow, .fieldtable tr.glow { background-color: var(--primary-light-color); box-shadow: none; } table.memberdecls { display: block; -webkit-tap-highlight-color: transparent; } table.memberdecls tr[class^='memitem'] { font-family: var(--font-family-monospace); font-size: var(--code-font-size); } table.memberdecls tr[class^='memitem'] .memTemplParams { font-family: var(--font-family-monospace); font-size: var(--code-font-size); color: var(--primary-dark-color); white-space: normal; } table.memberdecls .memItemLeft, table.memberdecls .memItemRight, table.memberdecls .memTemplItemLeft, table.memberdecls .memTemplItemRight, table.memberdecls .memTemplParams { transition: none; padding-top: var(--spacing-small); padding-bottom: var(--spacing-small); border-top: 1px solid var(--separator-color); border-bottom: 1px solid var(--separator-color); background-color: var(--fragment-background); } table.memberdecls .memTemplItemLeft, table.memberdecls .memTemplItemRight { padding-top: 2px; } table.memberdecls .memTemplParams { border-bottom: 0; border-left: 1px solid var(--separator-color); border-right: 1px solid var(--separator-color); border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; padding-bottom: var(--spacing-small); } table.memberdecls .memTemplItemLeft { border-radius: 0 0 0 var(--border-radius-small); border-left: 1px solid var(--separator-color); border-top: 0; } table.memberdecls .memTemplItemRight { border-radius: 0 0 var(--border-radius-small) 0; border-right: 1px solid var(--separator-color); padding-left: 0; border-top: 0; } table.memberdecls .memItemLeft { border-radius: var(--border-radius-small) 0 0 var(--border-radius-small); border-left: 1px solid var(--separator-color); padding-left: var(--spacing-medium); padding-right: 0; } table.memberdecls .memItemRight { border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; border-right: 1px solid var(--separator-color); padding-right: var(--spacing-medium); padding-left: 0; } table.memberdecls .mdescLeft, table.memberdecls .mdescRight { background: none; color: var(--page-foreground-color); padding: var(--spacing-small) 0; } table.memberdecls .memItemLeft, table.memberdecls .memTemplItemLeft { padding-right: var(--spacing-medium); } table.memberdecls .memSeparator { background: var(--page-background-color); height: var(--spacing-large); border: 0; transition: none; } table.memberdecls .groupheader { margin-bottom: var(--spacing-large); } table.memberdecls .inherit_header td { padding: 0 0 var(--spacing-medium) 0; text-indent: -12px; color: var(--page-secondary-foreground-color); } table.memberdecls img[src="closed.png"], table.memberdecls img[src="open.png"], div.dynheader img[src="open.png"], div.dynheader img[src="closed.png"] { width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid var(--primary-color); margin-top: 8px; display: block; float: left; margin-left: -10px; transition: transform var(--animation-duration) ease-out; } table.memberdecls img { margin-right: 10px; } table.memberdecls img[src="closed.png"], div.dynheader img[src="closed.png"] { transform: rotate(-90deg); } .compoundTemplParams { font-family: var(--font-family-monospace); color: var(--primary-dark-color); font-size: var(--code-font-size); } @media screen and (max-width: 767px) { table.memberdecls .memItemLeft, table.memberdecls .memItemRight, table.memberdecls .mdescLeft, table.memberdecls .mdescRight, table.memberdecls .memTemplItemLeft, table.memberdecls .memTemplItemRight, table.memberdecls .memTemplParams { display: block; text-align: left; padding-left: var(--spacing-large); margin: 0 calc(0px - var(--spacing-large)) 0 calc(0px - var(--spacing-large)); border-right: none; border-left: none; border-radius: 0; white-space: normal; } table.memberdecls .memItemLeft, table.memberdecls .mdescLeft, table.memberdecls .memTemplItemLeft { border-bottom: 0; padding-bottom: 0; } table.memberdecls .memTemplItemLeft { padding-top: 0; } table.memberdecls .mdescLeft { margin-bottom: calc(0px - var(--page-font-size)); } table.memberdecls .memItemRight, table.memberdecls .mdescRight, table.memberdecls .memTemplItemRight { border-top: 0; padding-top: 0; padding-right: var(--spacing-large); overflow-x: auto; } table.memberdecls tr[class^='memitem']:not(.inherit) { display: block; width: calc(100vw - 2 * var(--spacing-large)); } table.memberdecls .mdescRight { color: var(--page-foreground-color); } table.memberdecls tr.inherit { visibility: hidden; } table.memberdecls tr[style="display: table-row;"] { display: block !important; visibility: visible; width: calc(100vw - 2 * var(--spacing-large)); animation: fade .5s; } @keyframes fade { 0% { opacity: 0; max-height: 0; } 100% { opacity: 1; max-height: 200px; } } } /* Horizontal Rule */ hr { margin-top: var(--spacing-large); margin-bottom: var(--spacing-large); height: 1px; background-color: var(--separator-color); border: 0; } .contents hr { box-shadow: 100px 0 var(--separator-color), -100px 0 var(--separator-color), 500px 0 var(--separator-color), -500px 0 var(--separator-color), 900px 0 var(--separator-color), -900px 0 var(--separator-color), 1400px 0 var(--separator-color), -1400px 0 var(--separator-color), 1900px 0 var(--separator-color), -1900px 0 var(--separator-color); } .contents img, .contents .center, .contents center, .contents div.image object { max-width: 100%; overflow: auto; } @media screen and (max-width: 767px) { .contents .dyncontent > .center, .contents > center { margin-left: calc(0px - var(--spacing-large)); margin-right: calc(0px - var(--spacing-large)); max-width: calc(100% + 2 * var(--spacing-large)); } } /* Directories */ div.directory { border-top: 1px solid var(--separator-color); border-bottom: 1px solid var(--separator-color); width: auto; } table.directory { font-family: var(--font-family); font-size: var(--page-font-size); font-weight: normal; width: 100%; } table.directory td.entry, table.directory td.desc { padding: calc(var(--spacing-small) / 2) var(--spacing-small); line-height: var(--table-line-height); } table.directory tr.even td:last-child { border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; } table.directory tr.even td:first-child { border-radius: var(--border-radius-small) 0 0 var(--border-radius-small); } table.directory tr.even:last-child td:last-child { border-radius: 0 var(--border-radius-small) 0 0; } table.directory tr.even:last-child td:first-child { border-radius: var(--border-radius-small) 0 0 0; } table.directory td.desc { min-width: 250px; } table.directory tr.even { background-color: var(--odd-color); } table.directory tr.odd { background-color: transparent; } .icona { width: auto; height: auto; margin: 0 var(--spacing-small); } .icon { background: var(--primary-color); border-radius: var(--border-radius-small); font-size: var(--page-font-size); padding: calc(var(--page-font-size) / 5); line-height: var(--page-font-size); transform: scale(0.8); height: auto; width: var(--page-font-size); user-select: none; } .iconfopen, .icondoc, .iconfclosed { background-position: center; margin-bottom: 0; height: var(--table-line-height); } .icondoc { filter: saturate(0.2); } @media screen and (max-width: 767px) { div.directory { margin-left: calc(0px - var(--spacing-large)); margin-right: calc(0px - var(--spacing-large)); } } @media (prefers-color-scheme: dark) { html:not(.light-mode) .iconfopen, html:not(.light-mode) .iconfclosed { filter: hue-rotate(180deg) invert(); } } html.dark-mode .iconfopen, html.dark-mode .iconfclosed { filter: hue-rotate(180deg) invert(); } /* Class list */ .classindex dl.odd { background: var(--odd-color); border-radius: var(--border-radius-small); } .classindex dl.even { background-color: transparent; } /* Class Index Doxygen 1.8 */ table.classindex { margin-left: 0; margin-right: 0; width: 100%; } table.classindex table div.ah { background-image: none; background-color: initial; border-color: var(--separator-color); color: var(--page-foreground-color); box-shadow: var(--box-shadow); border-radius: var(--border-radius-large); padding: var(--spacing-small); } div.qindex { background-color: var(--odd-color); border-radius: var(--border-radius-small); border: 1px solid var(--separator-color); padding: var(--spacing-small) 0; } /* Footer and nav-path */ #nav-path { width: 100%; } #nav-path ul { background-image: none; background: var(--page-background-color); border: none; border-top: 1px solid var(--separator-color); border-bottom: 1px solid var(--separator-color); border-bottom: 0; box-shadow: 0 0.75px 0 var(--separator-color); font-size: var(--navigation-font-size); } img.footer { width: 60px; } .navpath li.footer { color: var(--page-secondary-foreground-color); } address.footer { color: var(--page-secondary-foreground-color); margin-bottom: var(--spacing-large); } #nav-path li.navelem { background-image: none; display: flex; align-items: center; } .navpath li.navelem a { text-shadow: none; display: inline-block; color: var(--primary-color) !important; } .navpath li.navelem b { color: var(--primary-dark-color); font-weight: 500; } li.navelem { padding: 0; margin-left: -8px; } li.navelem:first-child { margin-left: var(--spacing-large); } li.navelem:first-child:before { display: none; } #nav-path li.navelem:after { content: ''; border: 5px solid var(--page-background-color); border-bottom-color: transparent; border-right-color: transparent; border-top-color: transparent; transform: translateY(-1px) scaleY(4.2); z-index: 10; margin-left: 6px; } #nav-path li.navelem:before { content: ''; border: 5px solid var(--separator-color); border-bottom-color: transparent; border-right-color: transparent; border-top-color: transparent; transform: translateY(-1px) scaleY(3.2); margin-right: var(--spacing-small); } .navpath li.navelem a:hover { color: var(--primary-color); } /* Scrollbars for Webkit */ #nav-tree::-webkit-scrollbar, div.fragment::-webkit-scrollbar, pre.fragment::-webkit-scrollbar, div.memproto::-webkit-scrollbar, .contents center::-webkit-scrollbar, .contents .center::-webkit-scrollbar, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar, div.contents .toc::-webkit-scrollbar, .contents .dotgraph::-webkit-scrollbar, .contents .tabs-overview-container::-webkit-scrollbar { background: transparent; width: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); height: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); } #nav-tree::-webkit-scrollbar-thumb, div.fragment::-webkit-scrollbar-thumb, pre.fragment::-webkit-scrollbar-thumb, div.memproto::-webkit-scrollbar-thumb, .contents center::-webkit-scrollbar-thumb, .contents .center::-webkit-scrollbar-thumb, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-thumb, div.contents .toc::-webkit-scrollbar-thumb, .contents .dotgraph::-webkit-scrollbar-thumb, .contents .tabs-overview-container::-webkit-scrollbar-thumb { background-color: transparent; border: var(--webkit-scrollbar-padding) solid transparent; border-radius: calc(var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); background-clip: padding-box; } #nav-tree:hover::-webkit-scrollbar-thumb, div.fragment:hover::-webkit-scrollbar-thumb, pre.fragment:hover::-webkit-scrollbar-thumb, div.memproto:hover::-webkit-scrollbar-thumb, .contents center:hover::-webkit-scrollbar-thumb, .contents .center:hover::-webkit-scrollbar-thumb, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody:hover::-webkit-scrollbar-thumb, div.contents .toc:hover::-webkit-scrollbar-thumb, .contents .dotgraph:hover::-webkit-scrollbar-thumb, .contents .tabs-overview-container:hover::-webkit-scrollbar-thumb { background-color: var(--webkit-scrollbar-color); } #nav-tree::-webkit-scrollbar-track, div.fragment::-webkit-scrollbar-track, pre.fragment::-webkit-scrollbar-track, div.memproto::-webkit-scrollbar-track, .contents center::-webkit-scrollbar-track, .contents .center::-webkit-scrollbar-track, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-track, div.contents .toc::-webkit-scrollbar-track, .contents .dotgraph::-webkit-scrollbar-track, .contents .tabs-overview-container::-webkit-scrollbar-track { background: transparent; } #nav-tree::-webkit-scrollbar-corner { background-color: var(--side-nav-background); } #nav-tree, div.fragment, pre.fragment, div.memproto, .contents center, .contents .center, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody, div.contents .toc { overflow-x: auto; overflow-x: overlay; } #nav-tree { overflow-x: auto; overflow-y: auto; overflow-y: overlay; } /* Scrollbars for Firefox */ #nav-tree, div.fragment, pre.fragment, div.memproto, .contents center, .contents .center, .contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody, div.contents .toc, .contents .dotgraph, .contents .tabs-overview-container { scrollbar-width: thin; } /* Optional Dark mode toggle button */ doxygen-awesome-dark-mode-toggle { display: inline-block; margin: 0 0 0 var(--spacing-small); padding: 0; width: var(--searchbar-height); height: var(--searchbar-height); background: none; border: none; border-radius: var(--searchbar-height); vertical-align: middle; text-align: center; line-height: var(--searchbar-height); font-size: 22px; display: flex; align-items: center; justify-content: center; user-select: none; cursor: pointer; } doxygen-awesome-dark-mode-toggle > svg { transition: transform var(--animation-duration) ease-in-out; } doxygen-awesome-dark-mode-toggle:active > svg { transform: scale(.5); } doxygen-awesome-dark-mode-toggle:hover { background-color: rgba(0,0,0,.03); } html.dark-mode doxygen-awesome-dark-mode-toggle:hover { background-color: rgba(0,0,0,.18); } /* Optional fragment copy button */ .doxygen-awesome-fragment-wrapper { position: relative; } doxygen-awesome-fragment-copy-button { opacity: 0; background: var(--fragment-background); width: 28px; height: 28px; position: absolute; right: calc(var(--spacing-large) - (var(--spacing-large) / 2.5)); top: calc(var(--spacing-large) - (var(--spacing-large) / 2.5)); border: 1px solid var(--fragment-foreground); cursor: pointer; border-radius: var(--border-radius-small); display: flex; justify-content: center; align-items: center; } .doxygen-awesome-fragment-wrapper:hover doxygen-awesome-fragment-copy-button, doxygen-awesome-fragment-copy-button.success { opacity: .28; } doxygen-awesome-fragment-copy-button:hover, doxygen-awesome-fragment-copy-button.success { opacity: 1 !important; } doxygen-awesome-fragment-copy-button:active:not([class~=success]) svg { transform: scale(.91); } doxygen-awesome-fragment-copy-button svg { fill: var(--fragment-foreground); width: 18px; height: 18px; } doxygen-awesome-fragment-copy-button.success svg { fill: rgb(14, 168, 14); } doxygen-awesome-fragment-copy-button.success { border-color: rgb(14, 168, 14); } @media screen and (max-width: 767px) { .textblock > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, .textblock li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, .memdoc li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, .memdoc > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, dl dd > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button { right: 0; } } /* Optional paragraph link button */ a.anchorlink { font-size: 90%; margin-left: var(--spacing-small); color: var(--page-foreground-color) !important; text-decoration: none; opacity: .15; display: none; transition: opacity var(--animation-duration) ease-in-out, color var(--animation-duration) ease-in-out; } a.anchorlink svg { fill: var(--page-foreground-color); } h3 a.anchorlink svg, h4 a.anchorlink svg { margin-bottom: -3px; margin-top: -4px; } a.anchorlink:hover { opacity: .45; } h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a.anchorlink { display: inline-block; } /* Optional tab feature */ .tabbed > ul { padding-inline-start: 0px; margin: 0; padding: var(--spacing-small) 0; } .tabbed > ul > li { display: none; } .tabbed > ul > li.selected { display: block; } .tabs-overview-container { overflow-x: auto; display: block; overflow-y: visible; } .tabs-overview { border-bottom: 1px solid var(--separator-color); display: flex; flex-direction: row; } @media screen and (max-width: 767px) { .tabs-overview-container { margin: 0 calc(0px - var(--spacing-large)); } .tabs-overview { padding: 0 var(--spacing-large) } } .tabs-overview button.tab-button { color: var(--page-foreground-color); margin: 0; border: none; background: transparent; padding: calc(var(--spacing-large) / 2) 0; display: inline-block; font-size: var(--page-font-size); cursor: pointer; box-shadow: 0 1px 0 0 var(--separator-color); position: relative; -webkit-tap-highlight-color: transparent; } .tabs-overview button.tab-button .tab-title::before { display: block; content: attr(title); font-weight: 600; height: 0; overflow: hidden; visibility: hidden; } .tabs-overview button.tab-button .tab-title { float: left; white-space: nowrap; font-weight: normal; padding: calc(var(--spacing-large) / 2) var(--spacing-large); border-radius: var(--border-radius-medium); transition: background-color var(--animation-duration) ease-in-out, font-weight var(--animation-duration) ease-in-out; } .tabs-overview button.tab-button:not(:last-child) .tab-title { box-shadow: 8px 0 0 -7px var(--separator-color); } .tabs-overview button.tab-button:hover .tab-title { background: var(--separator-color); box-shadow: none; } .tabs-overview button.tab-button.active .tab-title { font-weight: 600; } .tabs-overview button.tab-button::after { content: ''; display: block; position: absolute; left: 0; bottom: 0; right: 0; height: 0; width: 0%; margin: 0 auto; border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; background-color: var(--primary-color); transition: width var(--animation-duration) ease-in-out, height var(--animation-duration) ease-in-out; } .tabs-overview button.tab-button.active::after { width: 100%; box-sizing: border-box; height: 3px; } /* Navigation Buttons */ .section_buttons:not(:empty) { margin-top: calc(var(--spacing-large) * 3); } .section_buttons table.markdownTable { display: block; width: 100%; } .section_buttons table.markdownTable tbody { display: table !important; width: 100%; box-shadow: none; border-spacing: 10px; } .section_buttons table.markdownTable td { padding: 0; } .section_buttons table.markdownTable th { display: none; } .section_buttons table.markdownTable tr.markdownTableHead { border: none; } .section_buttons tr th, .section_buttons tr td { background: none; border: none; padding: var(--spacing-large) 0 var(--spacing-small); } .section_buttons a { display: inline-block; border: 1px solid var(--separator-color); border-radius: var(--border-radius-medium); color: var(--page-secondary-foreground-color) !important; text-decoration: none; transition: color var(--animation-duration) ease-in-out, background-color var(--animation-duration) ease-in-out; } .section_buttons a:hover { color: var(--page-foreground-color) !important; background-color: var(--odd-color); } .section_buttons tr td.markdownTableBodyLeft a { padding: var(--spacing-medium) var(--spacing-large) var(--spacing-medium) calc(var(--spacing-large) / 2); } .section_buttons tr td.markdownTableBodyRight a { padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large); } .section_buttons tr td.markdownTableBodyLeft a::before, .section_buttons tr td.markdownTableBodyRight a::after { color: var(--page-secondary-foreground-color) !important; display: inline-block; transition: color .08s ease-in-out, transform .09s ease-in-out; } .section_buttons tr td.markdownTableBodyLeft a::before { content: '〈'; padding-right: var(--spacing-large); } .section_buttons tr td.markdownTableBodyRight a::after { content: '〉'; padding-left: var(--spacing-large); } .section_buttons tr td.markdownTableBodyLeft a:hover::before { color: var(--page-foreground-color) !important; transform: translateX(-3px); } .section_buttons tr td.markdownTableBodyRight a:hover::after { color: var(--page-foreground-color) !important; transform: translateX(3px); } @media screen and (max-width: 450px) { .section_buttons a { width: 100%; box-sizing: border-box; } .section_buttons tr td:nth-of-type(1).markdownTableBodyLeft a { border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium); border-right: none; } .section_buttons tr td:nth-of-type(2).markdownTableBodyRight a { border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0; } } dune-functions-2.11.0+dfsg/doc/doxygen/doxygen_groups.hh000066400000000000000000000043151513634022200232730ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil -*- // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later /** @file @brief This file is an internal part of the doxygen documentation. This file only serves for documenting the doxygen groups to structure the documentation. It is a C++ header, because doxygen will always create a root level entry for markdown files which is undesired here. # Modules @defgroup Functions Functions @brief Interfaces and implementations of functions Since interfaces in dune-functions rely on duck-typing there are no base classes to ensure interfaces. Instead of this each type can be checked, if it satisfies a certain concept as defined in the Dune::Functions::Concept namespace. Additionally there is a polymorphic interface consisting of type-erasure wrappers in the spirit of std::function. @defgroup FunctionConcepts Function concepts @ingroup Functions @brief Concept definitions for function interfaces @defgroup FunctionInterface Function interface wrappers @ingroup Functions @brief Type-erasure based polymorphic wrappers @defgroup FunctionImplementations Function implementations @ingroup Functions @brief Concrete function implementations @defgroup FunctionUtility Function related utilities @ingroup Functions @brief Helper classes and functions related to functions @defgroup FunctionSpaceBases Function space bases @brief Interfaces and implementation of global bases for grid function space @defgroup FunctionSpaceBasesConcepts Function space basis concepts @ingroup FunctionSpaceBases @brief Concept definitions and checks related to global bases @defgroup FunctionSpaceBasesImplementations Function space basis implementations @ingroup FunctionSpaceBases @brief Implementations of the GlobalBasis concept @defgroup FunctionSpaceBasesUtilities Function space basis utilities @ingroup FunctionSpaceBases @brief Utilities for implementing and using global bases @defgroup Utility Utility @brief Various helper classes and functions @defgroup TypeErasure Utilities for type-erasure @brief Helper classes for implementing type-erased interfaces @ingroup Utility */ dune-functions-2.11.0+dfsg/doc/doxygen/dune-functions.png000066400000000000000000000362601513634022200233510ustar00rootroot00000000000000PNG  IHDR,,y}usBIT|d pHYs _ _$~ tEXtSoftwarewww.inkscape.org< IDATxw`TUSRIBH'@VlkòªZڰ+bÆk]{î+l( %BHIi̼);Dp-sReQq.1 @d&E3H 0DXπ.1Pn0m.*߃?du"#L<1\"dž~>@ HB8`{5Z%DRmkZUH#$$"/}iJ0N`= F2(FD^@ra9k`vڎC# +̫HY]+2|#k<vdMa׻SI œn-e"P Nd΀G5G}N}YUWeu"<:aUo#`&;zO"`h(VekՁ $aŹy}PI :(%P p5^ћ9]cqx"IXq s(Ga` 39-9`K5jp} GV`@UEp,3DzE>n^ OC,MI$Etf=VbPPLz֔:RJe,eyyC{%O" D(I̼d+$aPuQhp. '$DC3D= y hQqYEIX&1@ KyfD.h\Rzh$,)+]AL`Dxsv^| ȫw) Jax\ .CwB FPCV /~z2$ -ڍ5e12N%b̴RO\﮶:x$ k _؜{df!"f 1^"Rys61b\XRRhu<5"no/ׅ\*~uYYl Y8m )'D '3o=/[EZXZx,5؅/da J : fuYQ`~ q<} 05~`e&c8!3B;q-̫Hz ;$Y Lb_gXe +O+{H {*KVuY` ` >d+W!"B$h۪|hL{X**:, "1خ_~c=]0\V 5iPYu1}l4wS9TA0s=/+.)3%D2Dhٵ#I)}/bj>vr@3VgP@#H^Lkj?sP,-L D̀cxU]]WPO|.X7B~S6{**ҳ]k9 !Rq{pׅsy=Aγ~A! Lia~3&z+ !aj*gڰzXmmQă¹Vژy0εa%,"pB8XR k~YydU"<K 1{_ ĦBe&/.EBDĪ%F/0xK!@%VghŅv4}BuYyF7Eau9p@BTZ[W4 ',8 `Y # .*/`D֎;!c$ap1dTF@ Fv[_Zxkkѳt}Vqd#w1Ocu3]ꐒO<É&UֺPZPZx.%@*3#}X|<oGĽwt~-})?޺Q2z{EE77',+\"ɖ/FYОսoTջ]ob@-(-82%!0Qtieט}>B%dAdQ:odIhpYOlߊ-7#xc}>jh$ GI)Fy7&~ :dGȮb⇟ cQV?*ԇ**s<- dD.ėpfmヘjt~~W-'' CcgJȽ~ݎ3B٥Nѳrμmogu7|A8K˂7bFw;k }]`*$ `1U cOy)kzP;g{P1[҃q4j.h!e#Fy72'mB>e |~ d[v/<_/,pI<`-B2!Fyyzd[yp??43dcu @)wQTjmFt6{ÉwߋkTòJε> g tCFJa υav`9h;ȉ8PregjЃ}y}w|4zq@voU Iɳxzix2.hGq1ʯ5"#[\|j_s$YŹO>F?nDa7ΌM@QB@GV k$85V˟fu.>Y D{6Y'cA4QC˘1i[~U M6YQ" ~Ǭtg'Vq*1 L T14X|VZ8m4P*3wٴMu|I1QmWsɅ^4H tcW_.w6rT "hnVHf>SPߠ+^QĜCEe/<7D!>L,f ϛw1ܶs~|ZI=$FzE%"mByH)s6Jn;R<kwxQTVF~ AmC-Qh^vEڈQ&+nց tQ)ǯ(Gc̰o4TGw^VYDW`:[Z o8z8fJb][M%PYm}}CO{Xs51HDP>J.' bޛl󄥰;<>lt7"V,;htȂ,jua0-a1aG$)Z8+r8Pz񒸱]tdL{ JKhCQ>ݾ,vD|n6/nW,(癒p֐4iqJgcNVYY~?"r/N=wdYK7{/mY-;qŻ8i!<]~̳ ݳW<ف["]b&# f~9t0N9r= oBz(>?Z#|.W0ȎچߟYV0uv I&Cj{ t|i!-48 ]]hy(G?F~"w y&:>u"Vfð[oC'~'@GGT.[NK-dpF"q*65)BQ$32ܶExL()EٕWG%r81&/w e&^`H`iH)%6hǍ3ֳz5| Qhk3 [1-/dꚢO1]1 6cDȶH"b5f/aq%'kr? )'Stn<]'Vxm/P #3b7eLݷϛU=gy׼xs`Wx`cؠHBňwv-f"M(.Ƹ^A侷_5:mpSm_Jo*6޿LRfV[Q/cn{ܔ{3XJ]>UiÇc+A6kkgc9\gvs<'?Ox& Y;dm`zt/9mm1 h(FX3JS+*=o=eLgc=Mf@ f9JTƗ9Hu>~u-].b;v} ͱV^(TSdƿFU-6 gU)S;+(0@)}6y&Ưxu=T(Z]ktx3lyp yœ6cj_ 56Q\A&"UbB9{s}=CL_SU.7u\o?я>!P&,=zW"F4do9Y]TK &,ѹhatBZE2 ꩩFS:>gTK'<ݸ0³z5u8Zl:VeLdjyGO 3@(@ҥ1_wگ(E]ko z+1lfߵºZGOͪk̝Y, l H`prV5"_sjupSFmkǎCDvA\ˎ|MM R`3@@(&$eDVՔmr0W9N6ԓ\`nfE }XÉQ>tecٱo5>MX*-m (0Jkz/xŠw4ܾ{ٲ=@kz߰;^$KN'v myߨ䢄ߢBNLh.&&Udl~8 tdnolLr|#Ԡr_}r7b4=:5v]dV]x~'+w{R_cvZ&E\:ɥBLgﲫ*JVݝw[;uw:QY/koou7ik&sbrCjw`ol[f`r" uwfꚴQdV"Ǟ'4St VA+_k}g&,p=z;Znb]"0?x?JXJh2=G6>f6HewA'Fq'@W 21c{z " r(͜$΅=v8˧w!IDAT<򷴄u}+:LT{Je (D{Y;M2ܾӏ<@0(I2c ԬqvzB P~v-% Z8 #5r4w+bΜ)S : jl-W=;Q|y=o0ʯ1QY+E;旃$ 6j;q#olr#Ϛį( 51>?4ES1kHgQ6۷NoFAwRdjs%Ę'A}WWm{m8^w |.sݦ6ǓGi}/D)8S:X" 9^,m}=?_z9{箧3NxokE יs0^xq"n ϋb4bOR +cҶ\PmY!q'{dY(X}囝TK{>2&NP>j<ë@"܆g(01@=DO[}0z6345uOv;Fq#ޭ6# eb4qG1R,aUp$2xJwW^shVߏ/@,Y{6̬ɓQp cJ^1g$2vQٍ~ƽrcu+N;/>?,/qMBʘ8 5lDČ&Re!sh9e8utH='n v\bsg<ṟ}@֛h"Y!U5aY36y>OW@u7/矱tGk5i(Ұ5J̳ 5eu=&&bnVl06ALd56Xkb9~X o%\睋b1G0gᱯ9/fx izQӵk5S _=kǝ )YoAMmP+"#p>IJc"{k66kmwڬs/zWFDplTyK~$|uu`=F:bIeea]KXqIݑd]uwa)( :r=*+ ]{IuWvjhhu2SŁ/6Ve)m<}nn O`E`ӦGf:Z8KaD(JCM;>:jmmWh"BɆ"(,r$9JJ1܈clal99(ںYC9BQ[+j.JIP l"lj\I;].2:M3m6bD _Qp룴X}p=xLb\'@ѩ&iUhC$_/kK/B 3UX3j#Cc?2c/|OZ`߉giTz񑖹v}nya5]W_iLu9Rdn]8ҋP.{s_@+/E%8Wp&!a9I=777c_P)ӇFRaܿ_3Yn8夘'+wkz.V);@OX}~" 1~I` BfKJ" 3#)c`@G;~hC "֛h{k}v>,T [68/UMN+=,#I>r=+W刌k}uCIsh6׻/pv㢷fUu.p"B]"bdfb%tƁblu *- H3 v6|TT<|6omEsQxRٻ{AnwLr }x4>`~Q[5kP0>gvs$rZ"Ȝ-<5'!(X}h|(xyB^V:zXE`$(ɞ2pͣu_|} {m7t5P1HÎK~"Ymjy@?#ٺO?b$пЇ:KP+b0*gϽ0mhG 0D@6UBRϙZߏUo9Ή򿩦0\K?|~rCo'lޣb_gy92 g؟孯ês בowލw r_;Xrȁ%''͚7{,a1@nSZx<h"㓏Qsх!71f щ?z/Wbũ'"Ʌ<"1bT͆!:P[ϯ7Qd2V_LKӊ!r8Qz߰{"c„%["N'z+'%,9 2CmTxzI8UÆako-'"'g}0Qv@'ug'joK=)&k~[3U~ `Ϙa*=>#зԹp!~pB+]5yF̛B.(,Ĉ{Cl֛Xs𮭍QdOԻ6׶e 1w‹ %+ݍKri}^'J!mxEbJ%2YXr!Xq) Ѽ-U"b$ҼCEjfUI33onƊ_/,nmNAv.X'ӎEv@돷V +'+"N󏜆=`Ȧ91*<Ż /`dY;XzXrh}Ly3}/+w~X>nVuILt 69 si2rvz+vAk8%+gPd+%e]ZHynգc@T7=g}~tvǘg {c5Xt!XS9SYZN(כpD=w}d%D Я}%+!x |0md[559h|a"eeV&,u܎Uish}-t|QԎBBDA )..f]Ytf&!WYj q :jȄEuqB"OC zKͼ@%.b>0 \;vB."ռu0` %_ aCӁ~K:Y=愯.?Ni֔^$9L |SCCOz&{ ,]D~E0|؀ᄕ"uBl=9mo8a]C +2!<4{smy@"0IJ:{Tšظ>G!O;UuK\d*a|7dP10^VyŸLz!DJcXU״ M3CoRB>1Υa%I1kZֹsa%@%Bзm(]eZX ;,$BƤ;{} kjMMwL7UՅ ;a@2PBxז6k Pšr[%0BB@dDqFoJ]  &D*]h=#KH9.׌&D) !&.b>Y H~ Cȳ=OYB$+hƓEI1Y2nl"0813 c$=u ۉ$ KB @I.Xym`X} 0cJ]և'K $㘑fu,B^D*\a=5/ Y /D\ Bcaן!YqꂂR8Q͚lC!B +4(m:⦇Unw=)ڛwBⅰ 3qOxxJV@%,u-gC GtCH%!u/:-]zꏐ^P?Wչ>:eWqdϡ\C &nT6RBh ]w?bu(u?V@WBYPZt7XH[nVe|c1"aIb>:5^'_ 7"+\x6e5ADJV@%,`CҪw `ԅ"< |eDJV@%,HzYqY,z˂|+6c"1swX• NC$-!B \\U@"/)<Ȇ$3 !w_Se}V3PI^8x?Яpʲ!~RM^DB$,XP^8zĥRT6ԹZM$,a(L) 9P*HmdJV@%,sf Z-`8{T[ZPZ8L?Hy @|yUn/Zfo0g:!".E'mp),,-I %vkYcu8іtcX}\_kNf. -yD`iΙK*$+ EzX.+8`'Qb"D8R%,XP:dm.+HD^bb;54XN+*F}d@35Q74wtԻwKdhkS D\"V>nrSRJ65ymW~Lg{bIW fLS>d4vnpbu8"{Xze =" @}^s$I+Cv0&IRF ZA+ 'I &܁ފ(`/beCӬZ! ˀꂂRӭ ČDD0DP yg^buHN 5A ?s:D!_0,(.8RBA 1p'H Eiv =440CQ4 Below you can find commented examples explaining the building blocks of dune-functions. The source code of all examples can be found in the `examples/` subdirectory of the [dune-functions][] module and is also available in the @subpage examples-sources section. Currently there is only one commented example in the tutorial: ### C++ examples 1. @subpage poisson-pq2-cpp ### Python examples 1. @subpage poisson-pq2-py 1. @subpage poisson-mfem-py [dune-functions]: https://gitlab.dune-project.org/staging/dune-functions
| Previous | Next | |:--------------|---------------------------:| | @ref index | @ref poisson-pq2-cpp |
@page examples-sources Example sources This contains the raw source files of the examples. The commented versions can be found in the @ref tutorial section. ### C++ examples 1. @subpage source-poisson-pq2-cpp ### Python examples 1. @subpage source-poisson-pq2-py 1. @subpage source-poisson-mfem-py dune-functions-2.11.0+dfsg/doc/doxygen/examples/000077500000000000000000000000001513634022200215115ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/doc/doxygen/examples/poisson-mfem-py.md000066400000000000000000000316711513634022200251050ustar00rootroot00000000000000@page poisson-mfem-py Mixed FE discretization of the Poisson equation (Python) The following example explains how to solve a mixed formulation of the Poisson problem with Lagrange elements for the solution and Raviart-Thomas elements for the gradient. The full example file @ref source-poisson-mfem-py can be found in the `examples/` subdirectory. Be warned that writing a quadrature loop for a stiffness matrix with [dune-functions][] and Python is currently quite slow. This approach is therefore mainly useful for small problems, for educational purposes, or if you need complete control. For faster and more convenient ways to assemble finite element stiffness matrices, have a look at the [dune-fufem](https://gitlab.dune-project.org/fufem/dune-fufem) and [dune-fem](https://gitlab.dune-project.org/dune-fem/dune-fem) modules. ## Mixed formulation of the Poisson problem Let \f$ \Omega \f$ be a bounded open subset of \f$ \mathbb{R}^d \f$. The Poisson problem asks for a scalar function \f$ u : \Omega \to \mathbb{R} \f$ that solves \f[ -\Delta u = f \qquad \text{on $\Omega$} \f] for a given function \f$f : \Omega \to \mathbb{R} \f$. In this example, we consider boundary conditions \f{alignat*}{{2} u & = u_0 \qquad & \text{on $\Gamma_D$} \\ \langle \nabla u, n \rangle & = g & \text{on $\Gamma_N$}, \f} where \f$ \Gamma_D \f$ and \f$ \Gamma_N \f$ form a partition of the domain boundary \f$ \partial \Omega \f$. The mixed formulation of this problem introduces the gradient of \f$ u \f$ as a new variable \f$ \sigma \f$. One obtains a system of two first-order equations for two unknowns \f{align*} \sigma - \nabla u & = 0 \\ -\operatorname{div} \sigma & = f, \f} and the Neumann boundary condition turns into \f[ \langle \sigma, n \rangle = g \qquad \text{on $\Gamma_N$.} \f] Multiplying with test functions \f$ \tau \f$ and \f$ v \f$ (where the normal component of \f$ \tau \f$ is supposed to be zero \f$ \Gamma_n \f$) yields \f{align*} \int_\Omega \langle \sigma, \tau \rangle\,dx - \int_\Omega \langle \nabla u, \tau \rangle \,dx & = 0 \\ \int_\Omega \operatorname{div} \sigma \cdot v\,dx &= -\int_\Omega fv\,dx. \f} To obtain a symmetric bilinear form one applies integration by parts to the first equation. One gets: Find \f$ \sigma \in H_D(\text{div}) \f$ and \f$ u \in L^2(\Omega) \f$ such that \f{alignat*}{{2} \int_\Omega \langle \sigma, \tau \rangle\,dx + \int_\Omega \langle \operatorname{div} \tau, u \rangle\,dx &= \int_{\Gamma_D} \langle \tau, n \rangle u\,ds & \qquad & \forall \tau \in H_0(\text{div})\\ \int_\Omega \operatorname{div} \sigma \cdot v\,dx \qquad \qquad & = - \int_\Omega fv\,dx && \forall v \in L^2(\Omega). \f} Here, the space \f$ H(\text{div}) \f$ is defined as \f[ H(\text{div}) := \Big\{ \tau \in L^2(\Omega)^d \mid \operatorname{div} \tau \in L^2(\Omega) \Big\}. \f] It is a vector space of vector-valued functions, and a strict superset of \f$ H^1(\Omega)^d \f$. The space \f$ H_D(\text{div}) \f$ denotes the subspace of \f$ H(\text{div}) \f$ of vector fields complying with the boundary condition on \f$ \Gamma_N \f$. The space \f$ H_D(\text{div}) \f$ denotes the subspace of \f$ H(\text{div}) \f$ of vector fields having zero normal trace on \f$ \Gamma_N \f$. A natural approximation space for \f$ H(\text{div}) \f$ is the space of Raviart-Thomas elements. It consists of particular piecewise polynomial vector fields whose normal components are continuous across element boundaries, but whose tangential components may jump. The solution \f$ u \f$ lives in \f$ L^2 \f$, and therefore piecewise constant finite elements can be used. Even though the boundary condition for \f$ u \f$ is not explicitly enforced, it can be shown to hold in the limit of vanishing element diameter. ## The implementation The Python implementation starts by importing required external modules. It needs code from `numpy` and `scipy` for the sparse linear algebra, and from `dune` for grids, finite element bases, and quadrature rules. @snippet poisson-mfem.py imports ### Main program The main program first creates the grid. For simplicity, the domain here is the unit square in two space dimensions, and the grid is a 50 by 50 quadrilateral grid: @snippet poisson-mfem.py createGrid The next line creates the bases of the two finite element spaces: The Raviart-Thomas basis for \f$ \sigma \f$, and a zero-order Lagrange basis (i.e., piecewise constant functions) for \f$ u \f$. In [dune-functions], such pairs of bases are represented in a tree data structure with one root and two leaves: Each of the leaves represents one of the two bases, and the root combines them into a single object. This is the purpose of the `functions.composite` method in the following code snippet. Setting up the basis tree also allows to select the way how global degrees of freedom are numbered. Here, we use the default settings, which lead to plain integers (actually, arrays of length 1) being used. @snippet poisson-mfem.py createBasis The method `subspaceBasis` gives access to the leaves of a composite basis. The numbers 0 and 1 passed as arguments single out the desired leaves. Note that accessing the two bases as subspace bases of a tree is not the same as simply having two separate basis objects: The actual basis functions are not affected by this, but the numbers differ. Then, the problem stiffness matrix and right-hand side vector are assembled. The code uses two separate methods for that, which are discussed below. The source term \f$ f \f$ is specified in a lambda expression `f`, which here encodes the constant function \f$ f : x \mapsto 2 \f$. @snippet poisson-mfem.py assembly The next code block manages the handling of the Dirichlet boundary conditions. In the dual formulation used here, Dirichlet values are only applied to the gradient field \f$ \sigma \f$, and only to its normal components. This is easy to implement with Raviart-Thomas elements, whose degrees of freedom are exactly the normal components of the vector field. In this example, the gradient of \f$ u \f$ is prescribed on the upper and lower horizontal edge of the domain boundary. To find all degrees of freedom on these grid boundary parts, the method `markBoundaryDOFsByIndicator` fills the array `isDirichlet` with the corresponding information: The array contains a boolean variable per degree of freedom, which is set to `True` if a degree of freedom is attached to a grid intersection whose center is on the relevant boundary. @snippet poisson-mfem.py dirichletDOFs Then, the function \f$ g \f$ from the Neumann boundary condition \f$ \langle \sigma, n \rangle = g \f$ is defined. In this example, it is given by the closed-form expression \f[ g(x_0,x_1) = \sin(2\pi x_0). \f] This expression is evaluated at the boundary Lagrange nodes by the method `basis.interpolate`. \todo Remove the need to involve the normal vector here! @snippet poisson-mfem.py dirichletValues After this, the information about the boundary condition is integrated into the linear system of equations. This happens by calling a method that appears earlier in the file: @snippet poisson-mfem.py dirichletIntegration The implementation of this method is @snippet poisson-mfem.py dirichletIntegration The code loops over all nonzero entries of the matrix. If an entry belongs to a matrix row that corresponds to a boundary degree of freedom, it is set to 1 if it is the diagonal entry, and to 0 otherwise. This pins the degree of freedom to the corresponding boundary value. This part of the code is completely independent from %Dune. At this point, the linear system of equations has been completely assembled, and can now be solved. For this, the code simply calls a sparse direct solver from the Python `scipy` package. @snippet poisson-mfem.py solving The solver leaves the algebraic solution vector in the vector variable `x`. The solution is then written to a file, in the VTK file format. To do so, the solution vector must be reinterpreted as a finite element functions again. The `asFunction` method does this: @snippet poisson-mfem.py vtkWriting This will write a file called `poisson-mfem-result.vtu`, which contains the grid and the solution \f$ u \f$ in a field called "value", and the approximate gradient vector field \f$ \sigma \f$ in a field called "gradient". It can be opened, for example, with the [ParaView](https://www.paraview.org/) visualization program. ## The assembler The assembler is implemented in four methods above the main method of the program. Two of them implement the local assembler, i.e., they compute stiffness matrix and load vector for a single element. The two global assembler methods then piece together the local matrices and vectors into a single global matrix and vector, respectively. ### Global assembler The function that assembles the global stiffness matrix is `assembleMixedPoissonMatrix`. As described in many text books, this assembly is implemented as a loop over the grid elements. On each element, all relevant integrals are computed and stored in a matrix `elementMatrix`, the *element stiffness matrix*. Since the basis functions of both finite element spaces are zero on most elements by construction, this element stiffness matrix is much smaller than the global stiffness matrix, but it is dense. Computation of `elementMatrix` happens by calling the method `getLocalMatrix` (discussed below). Then, the entries of the element stiffness matrix are added to the appropriate places of the global `stiffnessMatrix` object. The `basis` object provides the correspondence between the entries of the element stiffness matrices and the entries of the global stiffness matrix. Note how this does not require to distinguish between value and gradient degrees of freedom at all. @snippet poisson-mfem.py assembleMixedPoissonMatrix Note that `stiffnessMatrix` is sparse (i.e., it contains mainly zeros). This property has to be exploited. Therefore, a `lil_matrix` object from the Python `scipy` package is used to build it. After assembly has been completed, the matrix is converted to Compressed Sparse Row (CSR) format, because that is more efficient for the solver. The next method does the global assembly for the load vector. Since a nonzero volume term only appears for the second equation, only the second basis (accessed via `functions.subspaceBasis(basis,1)`) is used. The volume term function given by the main method takes as argument a point in world coordinates. However, the local assembler needs a function that takes arguments in element-local coordinates. The C++ interface provides the class Dune::Functions::AnalyticGridViewFunction to do this conversion, but this is not exposed as Python yet. Instead, we project the `volumeTerm` onto the Lagrange finite element space. This serves the same purpose. @snippet poisson-mfem.py assembleMixedPoissonRhs ### Local assembler The element stiffness matrices are computed by a free function called `getLocalMatrix`. Let \f$ (\theta_i) \f$ be the Raviart-Thomas shape functions and \f$ (\varphi_j) \f$ the zero-order Lagrange shape functions. The element and the set of basis functions on the element are passed to the method via a `localView` object. See the Dune::Functions::DefaultLocalView page for documentation of the corresponding C++ interface. For each element \f$ T \f$, the stiffness matrix has a two-by-two block form \f[ (A_T) = \begin{pmatrix} C & B \\ B^T & 0 \end{pmatrix}, \f] with entries \f[ C_{ij} = \int_\Omega \langle \theta_i, \theta_j\,dx \qquad B_{ij} = \int_\Omega \operatorname{div} \theta_i, \phi_j \rangle\,dx \f] Both submatrices are dense objects. In the code, they are referred to as "gradient-gradient coupling" and "value-gradient coupling", respectively. The computation of the integrals happens via numerical quadrature, with a quadrature rule that is defined on a reference element \f$ T_\text{ref} \f$. The transformation of the divergence uses the transformation of the full Jacobian \f$ \nabla \theta_i \f$ as explained in the @ref poisson-pq2-py example. @snippet poisson-mfem.py getLocalMatrix The final method computes the load vector for one element. Since only the second equation has a nontrivial linear term, the entries corresponding to Raviart-Thomas degrees of freedom are zero. The entries for the Lagrange degrees of freedom are \f[ (b_T)_i = \int_T f \varphi_i\,dx. \f] The element load vector is computed by the following code: @snippet poisson-mfem.py getLocalVolumeTerm [dune]: https://dune-project.org [dune-functions]: https://gitlab.dune-project.org/staging/dune-functions
| Previous | Next | |:--------------|-----------------------------:| | @ref poisson-pq2-py | |
@page source-poisson-mfem-py poisson-mfem.py This is the raw source code of the poisson-mfem.py example. There is also a [commented version](@ref poisson-mfem-py) of this example in the @ref examples section. @include{lineno} poisson-mfem.py dune-functions-2.11.0+dfsg/doc/doxygen/examples/poisson-pq2-cpp.md000066400000000000000000000050601513634022200250060ustar00rootroot00000000000000@page poisson-pq2-cpp Poisson equation (C++) The following explains how to solve the Poisson equation using [dune-functions][]. The full example @ref source-poisson-pq2-cpp can be found in the `examples/` subdirectory. ### Local assemblers The program first includes the required headers and then defines free functions for assembling the Poisson problem. The function `getLocalMatrix()` implements the assembler of the local stiffness matrix for the bilinear form @f$(u,v) \mapsto \int_\Omega \nabla u(x)\nabla v(x)dx@f$. @snippet poisson-pq2.cc getLocalMatrix The `getVolumeTerm()` functions implements the local assembler for the volume right hand side term @f$\int_\Omega f(x)v(x)dx@f$. @snippet poisson-pq2.cc getVolumeTerm ### Global assembler Assemble the global matrix pattern. @snippet poisson-pq2.cc getOccupationPattern Assembly of matrix and right-hand-side. @snippet poisson-pq2.cc assembleLaplaceMatrix ### Helper functions Treatment of boundary condition. @snippet poisson-pq2.cc boundaryTreatment Create a mixed grid containing triangles and quadrilaterals. @snippet poisson-pq2.cc createMixedGrid ### Setup and initialization Initialize MPI @snippet poisson-pq2.cc startMainAndMPI ### Create grid and finite element basis Create a mixed grid and obtain a leaf grid view. @note A `GridView` is a view to a subset of the grid's elements, vertices, ... that should be stored by value and can be copied cheaply. Grids in [dune][] are in general hierarchical and composed by elements on several levels. The discretization usually lives on the set of most refined elements that is denote the leaf `GridView` in dune. @snippet poisson-pq2.cc createGridCall As a next step the program creates a global finite element basis on the `GridView`. @snippet poisson-pq2.cc makeBasis And here is the rest of the file: @snippet poisson-pq2.cc theRest [dune]: https://dune-project.org [dune-functions]: https://gitlab.dune-project.org/staging/dune-functions
| Previous | Next | |:--------------|-----------------------------:| | @ref examples | @ref poisson-pq2-py |
@page source-poisson-pq2-cpp poisson-pq2.cc This is the raw source code of the poisson-pq2.cc example. There is also a [commented version](@ref poisson-pq2-cpp) of this example in the @ref examples section. @include{lineno} poisson-pq2.cc dune-functions-2.11.0+dfsg/doc/doxygen/examples/poisson-pq2-py.md000066400000000000000000000216501513634022200246570ustar00rootroot00000000000000@page poisson-pq2-py Poisson equation (Python) The following example explains how to solve the Poisson problem with Lagrange finite elements using the Python interface of [dune-functions][]. The full example file @ref source-poisson-pq2-py can be found in the `examples/` subdirectory. Be warned that writing a quadrature loop for a stiffness matrix with [dune-functions][] and Python is currently quite slow. This approach is therefore mainly useful for small problems, for educational purposes, or if you need complete control. For faster and more efficient ways to assemble finite element stiffness matrices, have a look at the [dune-fufem](https://gitlab.dune-project.org/fufem/dune-fufem) and [dune-fem](https://gitlab.dune-project.org/dune-fem/dune-fem) modules. ## The Poisson problem Let \f$ \Omega \f$ be a bounded open subset of \f$ \mathbb{R}^2 \f$ or \f$ \mathbb{R}^3 \f$. The Poisson problem asks for a scalar function \f$ u : \Omega \to \mathbb{R} \f$ that solves \f[ -\Delta u = f \qquad \text{on $\Omega$} \f] for a given function \f$f : \Omega \to \mathbb{R} \f$. To make the solution unique, it is additionally required that the solution \f$ u \f$ satisfies boundary conditions \f[ u = g \qquad \text{on $\partial \Omega$}. \f] The finite element method replaces the partial differential equation by a so-called *weak formulation*, and solves it in a space \f$ V_h \f$ of continuous, piecewise polynomial functions: The goal is now to find a function \f$ u_h \in V_h \f$ still satisfying the boundary conditions, such that \f[ \int_\Omega \langle \nabla u_h, \nabla v_h \rangle \,dx = \int_\Omega f v_h\,dx \qquad \forall v_h \in V_{h,0}, \f] where \f$ V_{h,0} \f$ is the space of finite element functions that are zero on the boundary. Writing this weak form in terms of a basis \f$ \{ \varphi_i \} \f$ of \f$ V_h \f$, one obtains a sparse linear system of equations \f[ Ax = b, \f] which can be solved using standard algorithms from numerical linear algebra. ## The implementation As every Python program, this one starts by importing required external modules. In this case, we need code from `numpy` and `scipy` for the sparse linear algebra, and from `dune` for grids, finite element bases, and quadrature rules. @snippet poisson-pq2.py imports ### Main program The main program starts by creating a grid. For simplicity, the domain here is the unit square in two space dimensions, and the grid is a 32 by 32 quadrilateral grid: @snippet poisson-pq2.py createGrid The next line creates the basis of the finite element space. We use second-order Lagrange finite elements here, i.e., the space \f$ V_h \f$ consists of continuous functions that are quadratic in each variable on each element. The class `functions.Lagrange` encodes the nodal basis of that space: @snippet poisson-pq2.py createBasis Then, the Poisson problem is assembled. The source term \f$ f \f$ is specified in a lambda expression `f`, which here encodes the constant function \f$ f : x \mapsto 10 \f$. The actual assembly then happens in the method `assembleLaplaceMatrix`, which we discuss below. @snippet poisson-pq2.py assembly The `assembleLaplaceMatrix` method returns *two* objects, the stiffness matrix `A` and the assembled source term vector `b`. The next code block manages the handling of the Dirichlet boundary conditions. In this example, the entire boundary is the Dirichet boundary. To find all degrees of freedom on the grid boundary, the method `forEachBoundaryDOF` visits each degree of freedom (DOF) associated to the grid boundary and calls the callback function `markDOF` with the global index of that degree of freedom. This callback then sets the corresponding entry in an array called `isDirichlet` to `True`. @snippet poisson-pq2.py dirichletDOFs Then, the function \f$ g \f$ from the problem formulation is defined. In this example the Dirichlet values are given by the closed-form expression \f[ g(x_0,x_1) = \sin(2\pi x_0). \f] This closed form expression is evaluated at the boundary Lagrange nodes by the method `basis.interpolate`. @snippet poisson-pq2.py dirichletValues After this, the information about the boundary conditions is integrated into the linear system of equations. The code loops over all nonzero entries of the matrix. If an entry belongs to a matrix row that corresponds to a boundary degree of freedom, it is set to 1 if it is the diagonal entry, and to 0 otherwise. This pins the degree of freedom to the corresponding boundary value. This part of the code is completely independent from %Dune. @snippet poisson-pq2.py dirichletIntegration At this point, the linear system of equations has been completely assembled, and can now be solved. For this, the code simply calls a sparse direct solver from the Python `scipy` package. This is not part of %Dune, either: @snippet poisson-pq2.py solving The solver leaves the algebraic solution vector in the vector variable `x`. The solution is then written to a file, in the VTK file format. To do so, the solution vector must be reinterpreted as a finite element functions again. The `asFunction` method does this: @snippet poisson-pq2.py vtkWriting This will write a file called `poisson-pq2-result.vtu`, which contains the grid and the solution function \f$ u_h \f$. It can be opened, for example, with the [ParaView](https://www.paraview.org/) visualization program. ## The assembler The assembler is implemented in two methods above the main method of the program. ### Global assembler The central function is `assembleLaplaceMatrix`, which computes the stiffness matrix \f$ A \f$ with entries \f[ A_{ij} = \int_\Omega \langle \nabla \varphi_i, \nabla \varphi_j \rangle dx \f] and the load vector \f$ b \f$ with entries \f[ b_i = \int_\Omega f \varphi_i\,dx. \f] As described in many text books, this assembly is implemented as a loop over the grid elements. On each element, all relevant integrals are computed and stored in a matrix `localA`, the *element stiffness matrix*. Since the basis functions \f$ \varphi_i \f$ are zero on most elements by construction, this element stiffness matrix is much smaller than the global stiffness matrix `A`, but it is dense. Computation of `localA` happens by calling yet another method `localAssembler` (discussed below). Afterwards, the entries of the element stiffness matrix are added to the appropriate places of the global stiffness matrix `A`. The `basis` object provides the correspondence between the entries of the element stiffness matrices and the entries of the global stiffness matrix. @snippet poisson-pq2.py assembleLaplaceMatrix Note that `A` is sparse (i.e., it contains mainly zeros). This property has to be exploited. Therefore, a `lil_matrix` object from the Python `scipy` package is used to build it. After assembly has been completed, the matrix is converted to Compressed Sparse Row (CSR) format, because that is more efficient for the solver. ### Local assembler The element stiffness matrices are computed by a free function called `localAssembler`. For a given element \f$ T \f$, it computes the element stiffness matrix \f[ (A_T)_{ij} = \int_T \langle \nabla \varphi_i, \nabla \varphi_j \rangle \,dx \f] and the element load vector \f$ b_T \f$ with entries \f[ (b_T)_i = \int_T f \varphi_i\,dx. \f] Both are dense objects. In this example, where the domain is two-dimensional, the grid consists of quadrilaterals, and the finite element space consists of second-order Lagrange elements, each \f$ A_T \f$ is a \f$ 9 \times 9\f$ matrix. The element and the set of basis functions on the element are passed to the method via a `localView` object. See the @ref Dune::Functions::DefaultLocalView page for documentation of the corresponding C++ interface. The computation of the integrals happens via numerical quadrature, and the quadrature rule is defined on a reference element \f$ T_\text{ref} \f$. The integral therefore needs to be transformed to the reference element, using a mapping \f$ F : T_\text{ref} \to T \f$: \f[ (A_T)_{pq} = \int_{T_\text{ref}} \Big \langle (\nabla_\xi \hat{\varphi}_{T,p}(\xi)) \cdot (\nabla_\xi F_T(\xi))^{-1}, (\nabla_\xi \hat{\varphi}_{T,q}(\xi)) \cdot (\nabla_\xi F_T(\xi))^{-1} \Big\rangle | \det \nabla_\xi F_T(\xi)|\,d\xi. \f] The integrals over \f$ T_\text{ref} \f$ are then computed by a quadrature rule. @snippet poisson-pq2.py localAssembler [dune]: https://dune-project.org [dune-functions]: https://gitlab.dune-project.org/staging/dune-functions
| Previous | Next | |:--------------|-----------------------------:| | @ref poisson-pq2-cpp | @ref poisson-mfem-py |
@page source-poisson-pq2-py poisson-pq2.py This is the raw source code of the poisson-pq2.py example. There is also a [commented version](@ref poisson-pq2-py) of this example in the @ref examples section. @include{lineno} poisson-pq2.py dune-functions-2.11.0+dfsg/doc/doxygen/license.md000066400000000000000000000021251513634022200216370ustar00rootroot00000000000000@page license License All library and header code, examples, doxygen documentation, and build system files are dual licensed under * @subpage GPL-2-dune-exception * @subpage LGPL-3 The manual is licensed under the * @subpage CC-BY-ND-4 The [doxygen][] documentation uses the [doxygen-awesome][] css style licensed under the * @subpage MIT For details refer to the [SPDX][] headers contained in each file which can be processed using the [reuse tool][reuse]. [SPDX]: https://spdx.dev [reuse]: https://reuse.software [doxygen]: https://www.doxygen.nl [doxygen-awesome]: https://jothepro.github.io/doxygen-awesome-css @page GPL-2-dune-exception GPL-2.0 only with DUNE exception @verbinclude LicenseRef-GPL-2.0-only-with-DUNE-exception.txt @page LGPL-3 LGPL-3.0 or later @verbinclude LGPL-3.0-or-later.txt @page CC-BY-ND-4 CC-BY-ND-4.0 license @verbinclude CC-BY-ND-4.0.txt @page MIT MIT license @verbinclude MIT.txt dune-functions-2.11.0+dfsg/doc/doxygen/mainpage.md000066400000000000000000000154471513634022200220110ustar00rootroot00000000000000# Dune-Functions ## Overview The [dune-functions][] module provides an abstraction layer for global finite element functions. Its two main concepts are functions implemented as callable objects, and bases of finite element spaces. ### Functions Dune-functions provides an interface to "functions" in the mathematical sense, in particular to finite element functions defined on a grid, but going far beyond that. The interface revolves around the concept of a "callable". This encompasses any type of C++ object that can be evaluated with `operator()`, like free functions, function objects, and lambdas. Dynamic polymorphism is realized using type erasure and the `std::function` class, which does not sacrifice efficiency in purely static code. Dune-functions extends the "callable" concept into several directions. First, it allows for differentiable functions. Such functions can hand out their derivative as new function objects. Second, for functions defined piecewisely on a finite element grid, the concept of local function is introduced. Local functions can be bound to grid elements. All further evaluations of a function bound to an element are in local coordinates of that element. This approach allows to avoid overhead when there are many evaluations on a single element. For more details refer to the @ref Functions section. ### Function space bases The second part of dune-functions provides a well-defined interface to bases of finite element function spaces. For this interface, a finite element basis is a set of functions with a prescribed ordering, and a way to index them. The core functionality has three parts: 1. For a given grid element, obtain the restrictions of all basis functions to this element, except for those functions where the restriction is zero. In other words: get the shape functions for the element. 2. Get a local numbering for these shape functions. This is needed to index the element stiffness matrix. 3. Get a global numbering for the shape functions. This is needed to index the global stiffness matrix. While local numbers are always integers, global numbers can be multi-indices, if appropriate. A central feature is that finite element bases for vector-valued and mixed spaced can be constructed by tensor multiplication of simpler bases. The resulting expressions can be interpreted as tree structures. For example, the tree for the three-dimensional Taylor-Hood basis is shown above. This tree structure is directly exposed in the dune-functions interface. An easy mechanism allows to construct new spaces. For more details refer to the @ref FunctionSpaceBases section. ## Documentation ### Class documentation The module contains a class documentation which can be build using [doxygen]. After the module has been build, you can build the documentation using `make doc`. Afterwards you can view the documentation by opening the file `doc/doxygen/html/index.html` from the build folder in the browser. Additionally the pre-build doxygen documentation for the _master_ and release branches is also hosted on the [documentation section][dune docs] of the dune-website. For a structured overview of the components of the module you can have a look at the [Topics](topics.html) page. ### Manual There are two documents describing the concepts and usage of dune functions. The interface of function is described in the article C. Engwer, C. Gräser, S. Müthing, and O. Sander. The interface for functions in the dune-functions module. Archive of Numerical Software, 5(1):95--109, 2017. This is freely available via the [website of the journal][functions paper] and as [arXiv:1512.06136][functions paper arxiv] preprint. The interface of the function space bases is described in the article C. Engwer, C. Gräser, S. Müthing, and O. Sander. Function space bases in the dune-functions module. Preprint, arxiv:1806.09545, 2018. This is freely available as [arXiv:1806.09545][bases paper arxiv] preprint. Both are also contained in the module. Like the class documentation this is build on `make doc`. ### Examples Several example applications demonstrate how to use the module. These example applications are contained in the `examples/` directory and build when building the module. The `stokes-taylorhood` example is described in detail in the manual (see above). ## Using dune-functions and licensing ### License Dune-Functions is dual licensed under the [GPL-2.0 with DUNE exception](@ref GPL-2-dune-exception) and [LGPL-3.0 or later](@ref LGPL-3). For details refer to the [license page](@ref license) and the [SPDX][] headers contained in each file. A list of contributors can be found in the `AUTHORS.md` file ### Citing dune-functions When using dune-functions **please make sure to cite the publications on the [functions interface][functions paper] and the [bases interface][bases paper]**. ### Dependencies Dune-functions depends on the dune [core modules][core] and the [dune-typetree module][typetree]. All of them are available using git: * https://gitlab.dune-project.org/core/dune-common * https://gitlab.dune-project.org/core/dune-geometry * https://gitlab.dune-project.org/core/dune-grid * https://gitlab.dune-project.org/core/dune-istl * https://gitlab.dune-project.org/core/dune-localfunctions * https://gitlab.dune-project.org/staging/dune-typetree The versioning of dune-functions follows the scheme used in the core modules. I.e. version x.y of dune-functions will depend on version x.y of the core modules and dune-typetree. Analogously, the _master_ branch will depend on the _master_ branch of these modules. Unless explicitly stated otherwise for a specific version, dune-functions supports/requires the same build tools (compilers, cmake) as the corresponding version of the core modules. ### Building the module Dune-functions integrates into the cmake-based dune build system. Hence it can be build (like any other module) using the `dunecontrol` script provided by the core modules. For details on how to use this build system and how to specify build options have a look at the documentation in the dune-common module. [core]: https://dune-project.org/groups/core [dune-functions]: https://gitlab.dune-project.org/staging/dune-functions [typetree]: https://gitlab.dune-project.org/staging/dune-typetree [dune docs]: https://dune-project.org/doxygen [functions paper arxiv]: https://arxiv.org/abs/1512.06136 [functions paper]: http://journals.ub.uni-heidelberg.de/index.php/ans/article/view/27683 [bases paper arxiv]: https://arxiv.org/abs/1806.09545 [bases paper]: https://arxiv.org/abs/1806.09545 [doxygen]: http://www.stack.nl/~dimitri/doxygen/ [SPDX]: https://spdx.dev dune-functions-2.11.0+dfsg/doc/gfx/000077500000000000000000000000001513634022200170025ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/doc/gfx/dune-functions-taylor-hood-tree.svg000066400000000000000000000663151513634022200256730ustar00rootroot00000000000000 image/svg+xml dune-functions-2.11.0+dfsg/doc/gfx/dune-functions-taylor-hood-tree.svg.license000066400000000000000000000002561513634022200273040ustar00rootroot00000000000000SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later dune-functions-2.11.0+dfsg/dune-functions.pc.in000066400000000000000000000007711513634022200213500ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ CXX=@CXX@ CC=@CC@ DEPENDENCIES=@REQUIRES@ Name: @PACKAGE_NAME@ Version: @VERSION@ Description: dune-functions module URL: http://dune-project.org/ Requires: dune-localfunctions dune-grid dune-typetree Libs: -L${libdir} Cflags: -I${includedir} dune-functions-2.11.0+dfsg/dune.module000066400000000000000000000007361513634022200176210ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # Dune module information file Module: dune-functions Version: 2.11 Maintainer: dune-functions@lists.dune-project.org Python-Requires: scipy Depends: dune-localfunctions (>= 2.11) dune-grid (>= 2.11) dune-istl (>= 2.11) dune-uggrid (>= 2.11) Suggests: dune-typetree (>= 2.11) Whitespace-Hook: Yes dune-functions-2.11.0+dfsg/dune/000077500000000000000000000000001513634022200164045ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/CMakeLists.txt000066400000000000000000000004261513634022200211460ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory(functions) if( DUNE_ENABLE_PYTHONBINDINGS ) add_subdirectory("python") endif() dune-functions-2.11.0+dfsg/dune/functions/000077500000000000000000000000001513634022200204145ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/CMakeLists.txt000066400000000000000000000005421513634022200231550ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #install headers add_subdirectory(analyticfunctions) add_subdirectory(backends) add_subdirectory(common) add_subdirectory(functionspacebases) add_subdirectory(gridfunctions) dune-functions-2.11.0+dfsg/dune/functions/analyticfunctions/000077500000000000000000000000001513634022200241515ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/analyticfunctions/CMakeLists.txt000066400000000000000000000005731513634022200267160ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory("test") install(FILES monomialset.hh polynomial.hh trigonometricfunction.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/functions/analyticfunctions) dune-functions-2.11.0+dfsg/dune/functions/analyticfunctions/monomialset.hh000066400000000000000000000361451513634022200270320ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_ANALYTICFUNCTIONS_MONOMIALSET_HH #define DUNE_FUNCTIONS_ANALYTICFUNCTIONS_MONOMIALSET_HH #include #include #include #include #include namespace Dune::Functions { namespace Impl { // Computes `y = [1, x, x^2, x^3, ..., x^maxDegree]` with `x` a single coordinate template void computePowers(const DomainFieldType& x, RangeType& y) { if constexpr(maxDegree >= 0) { y[0] = 1; for(auto k : Dune::range(maxDegree)) y[k+1] = y[k]*x; } } } // namespace Impl /** * \brief Function, which evaluates all monomials up to degree \p maxDegree in * a given coordinate. * * \ingroup FunctionImplementations * * \tparam RangeFieldType scalar type. * \tparam dim Domain dimension. * \tparam maxDegree Maximal monomial degree. * * The `Range` of this (differentiable) function is a vector of monomial evaluations * `[1,x,y,z,xx,xy,yy,xz,yz,zz,...]` in the coordinate vector `[x,y,z]`. The * maximal degree of the provided monomial evaluations is given by the parameter * \p maxDegree. The number of coordinate components is given by the \p dimension. * From \p maxDegree and \p dimension the total number of monomials can be computed * as `binomial(maxDegree+dimension, dimension)` and is provided as static * constant `::size` in the class. * The function models the \ref Concept::DifferentiableFunction * concept. * * The MonomialSet function is specialized for \p dim = 1, \p dim = 2, and * \p dim = 3 only. **/ template struct MonomialSet { static constexpr int dim = dimension; static constexpr int size = Dune::binomial(maxDegree + dim, dim); static_assert(1 <= dim && dim <= 3, "MonomialSet is specialized for dimensions 1,2,3 only."); /** * \brief Return array of monomial exponents with shape `size x dim` * * The k-the entry of the returned array is the exponent * multi-index of the monomial corresponding the the k-th * component of the function. Note that the ordering is tensor based, * e.g., for the set of 3d monomials `1,x,y,z,xx,xy,yy,xz,yz,zz, ...` we get * the exponents: * `[ [0,0,0], [1,0,0], [0,1,0], [0,0,1], [2,0,0], [1,1,0], [0,2,0], [1,0,1], * [0,1,1], [0,0,2], ...]` **/ static constexpr std::array,size> exponents(); /** * \brief Return array of monomial evaluations * * The k-the entry of the returned array is the value * of the monomial corresponding the the k-th * entry of the return value of exponents(). * * \tparam DomainFieldType The scalar type of the domain. **/ template constexpr Dune::FieldVector operator()(const Dune::FieldVector& x) const; /** * \brief Set of all first order derivatives of monomials up to degree * \p maxDegree as vector of vector valued functions. **/ struct Derivative { /** * \brief Return array of arrays of monomial derivatives * * The ith component of the k-the entry of the returned structure * is the derivative in direction i of the monomial corresponding * the the k-th entry of the return value of exponents(). * * \tparam DomainFieldType The scalar type of the domain. **/ template constexpr Dune::FieldMatrix operator()(const Dune::FieldVector& x) const; /** * \brief Set of all second order derivatives of monomials up to degree * \p maxDegree as vector of matrix valued functions. **/ struct Hessian { /** * \brief Return array of Matrices of monomial derivatives * * The (i,j)th component of the k-the entry of the returned structure * is the derivative in direction (i,j) of the monomial corresponding * the the k-th entry of the return value of exponents(). * * \tparam DomainFieldType The scalar type of the domain. **/ template constexpr std::array, size> operator()(const Dune::FieldVector& x) const; }; /** * \brief Construct the Hessian object from a Derivative **/ constexpr friend auto derivative(const Derivative & d) { return Hessian{}; } }; /** * \brief Construct the Derivative object from a MonomialSet **/ constexpr friend auto derivative(const MonomialSet& m) { return Derivative{}; } }; #ifndef DOXYGEN // Specialization for dim = 1 template struct MonomialSet { static constexpr int dim = 1; static constexpr int size = maxDegree+1; static constexpr auto exponents() { auto p = std::array,size>{}; for(auto k : Dune::range(size)) p[k][0] = k; return p; } template constexpr auto operator()(const Dune::FieldVector& x) const { auto y = Dune::FieldVector{}; Impl::computePowers(x[0], y); return y; } struct Derivative { template constexpr auto operator()(const Dune::FieldVector& x) const { auto xPowers = Dune::FieldVector{}; Impl::computePowers(x[0], xPowers); auto y = Dune::FieldMatrix{}; for(auto degree : Dune::range(1, maxDegree+1)) y[degree][0] = degree*xPowers[degree-1]; return y; } struct Hessian { template constexpr auto operator()(const Dune::FieldVector& x) const { auto xPowers = std::array{}; Impl::computePowers(x[0],xPowers); auto y = std::array,size>{}; for(auto degree : Dune::range(maxDegree+1)) if (degree-1 > 0) y[degree][0][0] = degree*(degree-1)*xPowers[degree-2]; return y; } }; constexpr friend auto derivative(const Derivative& d) { return Hessian{}; } }; constexpr friend auto derivative(const MonomialSet& m) { return Derivative{}; } }; // Specialization for dim = 2 template struct MonomialSet { static constexpr int dim = 2; static constexpr int size = (maxDegree+1)*(maxDegree+2)/2; static constexpr auto exponents() { auto p = std::array,size>{}; std::size_t index = 0; for(auto degree : Dune::range(maxDegree+1)) { for(auto k : Dune::range(degree+1)) { p[index][0] = degree-k; p[index][1] = k; ++index; } } return p; } template constexpr auto operator()(const Dune::FieldVector& x) const { auto xPowers = std::array,dim>{}; for(auto j : Dune::range(dim)) Impl::computePowers(x[j], xPowers[j]); auto y = Dune::FieldVector{}; std::size_t index = 0; for(auto degree : Dune::range(maxDegree+1)) { for(auto k : Dune::range(degree+1)) { y[index] = xPowers[0][degree-k]*xPowers[1][k]; ++index; } } return y; } struct Derivative { template constexpr auto operator()(const Dune::FieldVector& x) const { auto xPowers = std::array,dim>{}; for(auto j : Dune::range(dim)) Impl::computePowers(x[j], xPowers[j]); auto y = Dune::FieldMatrix{}; std::size_t index = 0; for(auto degree : Dune::range(maxDegree+1)) { for(auto k : Dune::range(degree+1)) { if (degree-k > 0) y[index][0] = (degree-k)*xPowers[0][degree-k-1]*xPowers[1][k]; if (k > 0) y[index][1] = k*xPowers[0][degree-k]*xPowers[1][k-1]; ++index; } } return y; } struct Hessian { template constexpr auto operator()(const Dune::FieldVector& x) const { auto xPowers = std::array,dim>{}; for(auto j : Dune::range(dim)) Impl::computePowers(x[j], xPowers[j]); auto y = std::array,size>{}; std::size_t index = 0; for(auto degree : Dune::range(maxDegree+1)) { for(auto k : Dune::range(degree+1)) { if (degree-k > 1) y[index][0][0] = (degree-k-1)*(degree-k)*xPowers[0][degree-k-2]*xPowers[1][k]; if (k > 0 and degree-k > 0){ auto mixed = k*(degree-k)*xPowers[0][degree-k-1]*xPowers[1][k-1]; y[index][0][1]= mixed; y[index][1][0]= mixed; } if (k > 1) y[index][1][1] = k*(k-1)*xPowers[0][degree-k]*xPowers[1][k-2]; ++index; } } return y; } }; constexpr friend auto derivative(const Derivative & d) { return Hessian{}; } }; constexpr friend auto derivative(const MonomialSet& m) { return Derivative{}; } }; // Specialization for dim = 3 template struct MonomialSet { static constexpr int dim = 3; static constexpr int size = Dune::binomial(maxDegree + dim, dim); static constexpr auto exponents() { auto p = std::array,size>{}; std::size_t index = 0; for(auto degree : Dune::range(maxDegree+1)) { for(auto k : Dune::range(degree+1)) { for (auto l : Dune::range(degree-k+1)) { p[index][0] = degree-k-l; p[index][1] = l; p[index][2] = k; ++index; } } } return p; } template constexpr auto operator()(const Dune::FieldVector& x) const { auto xPowers = std::array,dim>{}; for(auto j : Dune::range(dim)) Impl::computePowers(x[j], xPowers[j]); auto y = Dune::FieldVector{}; std::size_t index = 0; for(auto degree : Dune::range(maxDegree+1)) { for(auto k : Dune::range(degree+1)) { for (auto l : Dune::range(degree-k+1)) { y[index] = xPowers[0][degree-k-l]*xPowers[1][l]*xPowers[2][k]; ++index; } } } return y; } struct Derivative { template constexpr auto operator()(const Dune::FieldVector& x) const { auto xPowers = std::array,dim>{}; for(auto j : Dune::range(dim)) { xPowers[j][0] = 1.0; for(auto k : Dune::range(maxDegree)) xPowers[j][k+1] = xPowers[j][k]*x[j]; } auto y = Dune::FieldMatrix{}; std::size_t index = 0; for(auto degree : Dune::range(maxDegree+1)) { for(auto k : Dune::range(degree+1)) { for (auto l : Dune::range(degree-k+1)) { if (degree-k-l > 0) y[index][0] = (degree-k-l)*xPowers[0][degree-k-l-1]*xPowers[1][l]*xPowers[2][k]; if (l > 0) y[index][1] = l*xPowers[0][degree-k-l]*xPowers[1][l-1]*xPowers[2][k]; if (k > 0) y[index][2] = k*xPowers[0][degree-k-l]*xPowers[1][l]*xPowers[2][k-1]; ++index; } } } return y; } struct Hessian { template constexpr auto operator()(const Dune::FieldVector& x) const { auto xPowers = std::array,dim>{}; for(auto j : Dune::range(dim)) Impl::computePowers(x[j], xPowers[j]); auto y = std::array,size>{}; std::size_t index = 0; for(auto degree : Dune::range(maxDegree+1)) { for(auto k : Dune::range(degree+1)) { for (auto l : Dune::range(degree-k+1)) { // xx if (degree-k-l-1 > 0) y[index][0][0] = (degree-k-l)*(degree-k-l-1)*xPowers[0][degree-k-l-2]*xPowers[1][l]*xPowers[2][k]; // xy and yx if (degree-k-l > 0 and l > 0){ y[index][0][1] = (degree-k-l)*l*xPowers[0][degree-k-l-1]*xPowers[1][l-1]*xPowers[2][k]; y[index][1][0] = y[index][0][1]; } // yy if (l-1 > 0) y[index][1][1] = l*(l-1)*xPowers[0][degree-k-l]*xPowers[1][l-2]*xPowers[2][k]; // xz and zx if (k > 0 and degree-k-l > 0) { y[index][0][2] = (degree-k-l)*k*xPowers[0][degree-k-l-1]*xPowers[1][l]*xPowers[2][k-1]; y[index][2][0] = y[index][0][2]; } // yz if (l > 0 and k > 0) { y[index][1][2] = l*k*xPowers[0][degree-k-l]*xPowers[1][l-1]*xPowers[2][k-1]; y[index][2][1] = y[index][1][2]; } // zz if (k-1 > 0) y[index][2][2] = (k-1)*k*xPowers[0][degree-k-l]*xPowers[1][l]*xPowers[2][k-2]; ++index; } } } return y; } }; constexpr friend auto derivative(const Derivative & d) { return Hessian{}; } }; constexpr friend auto derivative(const MonomialSet& m) { return Derivative{}; } }; #endif } // namespace Dune::Functions #endif // DUNE_FUNCTIONS_ANALYTICFUNCTIONS_MONOMIALSET_HH dune-functions-2.11.0+dfsg/dune/functions/analyticfunctions/polynomial.hh000066400000000000000000000233011513634022200266540ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_ANALYTICFUNCTIONS_POLYNOMIAL_HH #define DUNE_FUNCTIONS_ANALYTICFUNCTIONS_POLYNOMIAL_HH #include #include #include #include namespace Dune { namespace Functions { namespace Impl { // Compute coefficients of derivative of polynomial. // Overload for std::vector template auto polynomialDerivativeCoefficients(const std::vector& coefficients) { if (coefficients.size()==0) return std::vector(); std::vector dpCoefficients(coefficients.size()-1); for (size_t i=1; i auto polynomialDerivativeCoefficients(const std::array& coefficients) { if constexpr (n==0) return coefficients; else { std::array dpCoefficients; for (size_t i=1; i auto polynomialDerivativeCoefficientsHelper(std::integer_sequence, std::integer_sequence) { return std::integer_sequence(); } // Compute coefficients of derivative of polynomial. // Overload for std::integer_sequence template auto polynomialDerivativeCoefficients(std::integer_sequence coefficients) { if constexpr (sizeof...(i)==0) return coefficients; else return polynomialDerivativeCoefficientsHelper(coefficients, std::make_index_sequence()); } // Compute coefficients of derivative of polynomial. // Overload for std::tuple template auto polynomialDerivativeCoefficients(const std::tuple& coefficients) { if constexpr (sizeof...(T)==0) return coefficients; else { // Notice that std::multiplies has issues with signed types. // E.g., `decltype(-2,2ul)` is `long unsigned int`. // Hence the same is deduced as return type in std::multiplies. // To avoid this, we explicitly pass the exponent `i+1` as signed type. // If the coefficient is signed, both types are now signed and // so is the deduced result type of std::multiplies. auto mult = Dune::Hybrid::hybridFunctor(std::multiplies()); return Dune::unpackIntegerSequence([&](auto... i) { return std::tuple(mult(std::get(coefficients), std::integral_constant()) ...); }, std::make_index_sequence()); } } } // namespace Impl in Dune::Functions:: /** * \brief A univariate polynomial implementation * * \ingroup FunctionImplementations * * \tparam K Scalar type. The polynomial will map K to K * \tparam C Coefficient container type (default std::vector) * * This class will store a coefficient container of type C. * Supported containers are std::vector, std::array, std::integer_sequence, std::tuple. * When passing std::tuple, coefficients of type std::integral_constant are promoted * as std::integral_constant when computing derivatives. * * Class template argument deduction is supported for passing std::array, std::vector, * std::integer_sequence, std::initializer_list. When passing such containers * without specifying the template parameters, then the scalar type is deduced to * be the coefficient type. Notice that the deduced coefficient container type when * passing std::initializer_list is std::vector. * * If you want to use different types for scalar and coefficients, you can use the * makePolynomial() function to explicitly specify the scalar type while the * coefficient type is deduced. * * This class exists mainly to demonstrate how to implement * the \ref Concept::DifferentiableFunction concept. */ template> class Polynomial { template struct IsIntegerSequence : public std::false_type {}; template struct IsIntegerSequence> : public std::true_type {}; /** \brief Helper method for the Horner scheme * * This method simply adds c to y, unless c is of type std::integral_constant<0>. * In that case, the method does nothing. The motivation for explicitly handling * this special case is automatic differentiation: While an optimizing compiler * can be expected to eliminate additions by zero in normal code, removal of zeros * is very unlikely when the Horner scheme code is taped for later use in reverse-mode * automatic differentiation. */ template static void add(K& y, const Coefficient &c) { if constexpr (!IsIntegralConstant::value) { if (c!=0) y += c; } else y += c; } public: //! The type of the stored coefficient container using Coefficients = C; //! Default constructor Polynomial() = default; /** * \brief Create from container of coefficients * * Coefficients are ordered in accordance with * the corresponding monomial order. The constructed * Polynomial object will store a copy of the passed * coefficient container. */ Polynomial(Coefficients coefficients) : coefficients_(std::move(coefficients)) {} /** \brief Evaluate polynomial using the Horner scheme * * Coefficients of type `std::integral_constant<0>` are eliminated * at compile time. */ K operator() (const K& x) const { using namespace Dune::Indices; auto n = Dune::Hybrid::size(coefficients_); // Explicitly handling the corner case of an empty coefficient set // allows to save one multiplication. return Hybrid::ifElse(Hybrid::equal_to(n, _0), [&](auto id) { /* then */ // No coefficients at all return K(0); }, [&](auto id) { /* else */ // Do the Horner scheme knowing that there is at least one coefficient K y = Hybrid::elementAt(coefficients_, Hybrid::minus(id(n), _1) ); Dune::Hybrid::forEach(Dune::range(Hybrid::minus(id(n), _1) ), [&](auto i) { y *= x; // Do y+= _next coefficient_, unless that coefficient is std::integral_constant<0>. add(y,Hybrid::elementAt(coefficients_, Hybrid::minus(Hybrid::minus(id(n),_2), i))); }); return y; }); } //! Comparison of coefficients bool operator==(const Polynomial& other) const { if constexpr (IsIntegerSequence::value) return true; else return coefficients()==other.coefficients(); } /** * \brief Obtain derivative of Polynomial function * * \ingroup FunctionImplementations * * The derivative contains its own coefficient * list and is not updated if the original function * is changed. */ friend auto derivative(const Polynomial& p) { auto derivativeCoefficients = Impl::polynomialDerivativeCoefficients(p.coefficients()); using DerivativeCoefficients = decltype(derivativeCoefficients); return Polynomial(std::move(derivativeCoefficients)); } //! Obtain reference to coefficient vector const Coefficients& coefficients() const { return coefficients_; } private: Coefficients coefficients_; }; template Polynomial(std::vector) -> Polynomial>; template Polynomial(std::array) -> Polynomial>; template Polynomial(std::integer_sequence) -> Polynomial>; template Polynomial(std::initializer_list) -> Polynomial>; /** * \brief Create Polynomial * * \tparam K Scalar type. The polynomial will map K to K * \tparam C Coefficient container type * * This helper function allows to specify K, but lets C be deduced from * the argument. If the scalar type K is the same as the coefficient * type (i.e. the entry type of the coefficient container C), then * you can also rely on class template argument deduction and * call Polynomial(coefficients) directly. */ template auto makePolynomial(Coefficients coefficients) { return Polynomial(std::move(coefficients)); } /** * \brief Create Polynomial * * \tparam K Scalar type. The polynomial will map K to K * \tparam C Coefficient type * * The initializer list will be stored as std::vector * in the created object of type Polynomial>. */ template auto makePolynomial(std::initializer_list coefficients) { return Polynomial(std::move(coefficients)); } }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_ANALYTICFUNCTIONS_POLYNOMIAL_HH dune-functions-2.11.0+dfsg/dune/functions/analyticfunctions/test/000077500000000000000000000000001513634022200251305ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/analyticfunctions/test/CMakeLists.txt000066400000000000000000000005601513634022200276710ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # tests that should build and run successfully link_libraries(Dune::Functions) dune_add_test(SOURCES monomialsettest.cc LABELS quick) dune_add_test(SOURCES polynomialtest.cc LABELS quick) dune-functions-2.11.0+dfsg/dune/functions/analyticfunctions/test/monomialsettest.cc000066400000000000000000000117141513634022200306720ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include // Create n^dim uniformly distributed points in [0,1]^dim template std::vector> samplePoints(unsigned int n) { unsigned long size = std::pow(n, dim); auto points = std::vector>(size); if constexpr (dim==1) { for(auto k : Dune::range(n)) points[k] = F(k)/F(n-1); } else { auto points_low = samplePoints(n); auto n_low = points_low.size(); for(auto k : Dune::range(size)) { auto k_0 = k % n_low; auto k_1 = k / n_low; for(auto i : Dune::range(dim-1)) points[k][i] = points_low[k_0][i]; points[k][dim-1] = F(k_1)/F(n-1); } } return points; } template Dune::TestSuite testMonomialSet(unsigned long n, double tol) { Dune::TestSuite suite; auto monomials = Dune::Functions::MonomialSet{}; auto p = monomials.exponents(); auto size = p.size(); auto exponentsToString = [](const auto& exponents) { return std::apply([](const auto&... xi) { return (std::to_string(xi) + ...); }, exponents); }; for(auto x : samplePoints(n)) { auto y = monomials(x); suite.check(y.size() == size); for(auto i : Dune::range(size)) { auto yy = F(1.0); for(auto k : Dune::range(dim)) yy *= std::pow(x[k], p[i][k]); suite.check(std::fabs(y[i] - yy) < tol) << "Monomial(dim=" << dim << ",maxOrder=" << maxOrder << ",index=" << i << ") value incorrect"; } } auto D_monomials = derivative(monomials); for(auto x : samplePoints(n)) { auto y = D_monomials(x); suite.check(y.N() == size); suite.check(y.M() == dim); for(auto i : Dune::range(size)) { for(auto j : Dune::range(dim)) { auto yy = F(1.0); for(auto k : Dune::range(dim)) { if (p[i][k]-(k==j) > 0) yy *= std::pow(x[k], p[i][k]-(k==j)); } yy *= p[i][j]; suite.check(std::fabs(y[i][j] - yy) < tol) << "Monomial(dim=" << dim << ",maxOrder=" << maxOrder << ",index=" << i << ") derivative incorrect"; } } } auto H_monomials = derivative(derivative(monomials)); for(auto x : samplePoints(n)) { auto y = H_monomials(x); suite.check(y.size() == size)<<"Wrong Size"; suite.check(y[0].M() == dim)<<"Wrong M"; suite.check(y[0].N() == dim)<<"Wrong N"; for(auto i : Dune::range(size)) { for(auto j : Dune::range(dim)) { for (auto l: Dune::range(dim)){ auto yy = F(1.0); for(auto k : Dune::range(dim)) { if (p[i][k]-(k==j)-(k==l) > 0) yy *= std::pow(x[k], p[i][k] - int(k == j) - int(k == l)); } if (j == l) yy *= p[i][j] * (int(p[i][j]) - 1.); else yy *= p[i][j] * p[i][l]; suite.check(std::fabs(y[i][j][l] - yy) < tol) << "Monomial(dim=" << dim << ",maxOrder=" << maxOrder << ",index=" << i << ", exponents= "<< exponentsToString(p[i]) <<") hessian component ["<(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); suite.subTest(testMonomialSet(10, 1e-14)); return suite.exit(); } dune-functions-2.11.0+dfsg/dune/functions/analyticfunctions/test/polynomialtest.cc000066400000000000000000000161731513634022200305320ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include // Compare coefficients of different polynomials template bool polynomialsEqual(const P0& p0, const P1& p1) { std::vector c0; Dune::Hybrid::forEach(p0.coefficients(), [&](auto z) { c0.push_back(z); }); std::vector c1; Dune::Hybrid::forEach(p1.coefficients(), [&](auto z) { c1.push_back(z); }); auto n0 = c0.size(); auto n1 = c1.size(); for([[maybe_unused]] auto i : Dune::range(n0,std::max(n0, n1))) c0.push_back(0); for([[maybe_unused]] auto i : Dune::range(n1,std::max(n0, n1))) c1.push_back(0); return (c0==c1); } using Dune::Functions::Polynomial; using Dune::Functions::makePolynomial; template Dune::TestSuite testDynamicPolynomial() { Dune::TestSuite suite; // Check construction from initializer list using P = Polynomial; using Coefficients = typename P::Coefficients; // Check if CTAD works if constexpr (std::is_same_v) { auto p = Polynomial(Coefficients({1, 2, 3, -4})); suite.check(std::is_same_v); } // Check whether a completely empty polynomial can be constructed and evaluated auto pEmpty = Polynomial >(std::array()); suite.check(std::fabs(pEmpty(0)) == 0.0); auto p = P({ 1, 2, 3, -4}); { double x = 42; double y = 1.0 + 2.0*x + 3.0*x*x - 4.0*x*x*x; suite.check(std::fabs(p(x) - y) < 1e-14); } // Check construction from l-value reference using Coeff = typename P::Coefficients; auto c = Coeff({1, 2, 3, -4}); suite.check(polynomialsEqual(p, P(c))); // Check default construction { [[maybe_unused]] P p1; [[maybe_unused]] auto p2 = P(); } // Check copy construction { auto p2 = p; suite.check(polynomialsEqual(p, p2)); P p3(p2); suite.check(polynomialsEqual(p, p3)); } // Check copy assignment { auto p1 = P({2,3,4}); auto p2 = P({3,4,5}); suite.check(not polynomialsEqual(p1, p2)); p1 = p2; suite.check(polynomialsEqual(p1, p2)); } // Check move assignment { auto p1 = P({2,3,4}); auto p2 = P({3,4,5}); p1 = P({3,4,5}); suite.check(polynomialsEqual(p1, p2)); } // Check some value suite.check(p(0) == 1); // Check derivatives { auto dp = P({ 2, 6, -12 }); auto ddp = P({ 6, -24 }); auto dddp = P({ -24 }); auto zero = P(); suite.check(polynomialsEqual(derivative(p), dp)); suite.check(polynomialsEqual(derivative(derivative(p)), ddp)); suite.check(polynomialsEqual(derivative(derivative(derivative(p))), dddp)); suite.check(polynomialsEqual(derivative(derivative(derivative(derivative(p)))), zero)); suite.check(polynomialsEqual(derivative(derivative(derivative(derivative(derivative(p))))), zero)); } return suite; } template Dune::TestSuite testIntegerSequencePolynomial() { Dune::TestSuite suite; // Check if CTAD works { using Scalar = int; using Coefficients = std::integer_sequence; auto p = Polynomial(Coefficients{}); suite.check(std::is_same_v>); } auto p = Polynomial>(); auto dp = Polynomial>(); auto ddp = Polynomial>(); auto dddp = Polynomial>(); auto zero = Polynomial>(); suite.check(derivative(p)==dp); suite.check(derivative(derivative(p))==ddp); suite.check(derivative(derivative(derivative(p)))==dddp); suite.check(derivative(derivative(derivative(derivative(p))))==zero); suite.check(derivative(derivative(derivative(derivative(derivative(p)))))==zero); suite.check(polynomialsEqual(p, Polynomial({1,2,3,-4}))); suite.check(polynomialsEqual(derivative(p), Polynomial({2,6,-12}))); suite.check(polynomialsEqual(derivative(derivative(p)), Polynomial({6,-24}))); suite.check(polynomialsEqual(derivative(derivative(derivative(p))), Polynomial({-24}))); suite.check(polynomialsEqual(derivative(derivative(derivative(derivative(p)))), Polynomial())); suite.check(polynomialsEqual(derivative(derivative(derivative(derivative(derivative(p))))), Polynomial())); { double x = 42; double y = 1.0 + 2.0*x + 3.0*x*x - 4.0*x*x*x; suite.check(std::fabs(p(x) - y) < 1e-14); } return suite; } template Dune::TestSuite testTuplePolynomial() { Dune::TestSuite suite; using namespace Dune::Indices; auto p = makePolynomial(std::tuple(1ul, 2ul, _3, -4l)); auto dp = makePolynomial(std::tuple(2ul, _6, -12l)); auto ddp = makePolynomial(std::tuple(_6, -24l)); auto dddp = makePolynomial(std::tuple(-24l)); auto zero = makePolynomial(std::tuple()); suite.check(derivative(p)==dp); suite.check(derivative(derivative(p))==ddp); suite.check(derivative(derivative(derivative(p)))==dddp); suite.check(derivative(derivative(derivative(derivative(p))))==zero); suite.check(derivative(derivative(derivative(derivative(derivative(p)))))==zero); suite.check(polynomialsEqual(p, Polynomial({1,2,3,-4}))); suite.check(polynomialsEqual(derivative(p), Polynomial({2,6,-12}))); suite.check(polynomialsEqual(derivative(derivative(p)), Polynomial({6,-24}))); suite.check(polynomialsEqual(derivative(derivative(derivative(p))), Polynomial({-24}))); suite.check(polynomialsEqual(derivative(derivative(derivative(derivative(p)))), Polynomial())); suite.check(polynomialsEqual(derivative(derivative(derivative(derivative(derivative(p))))), Polynomial())); { double x = 42; double y = 1.0 + 2.0*x + 3.0*x*x -4.0*x*x*x; suite.check(std::fabs(p(x) - y) < 1e-14); } return suite; } int main(int argc, char* argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite suite; // Check if CTAD works for initializer_list { auto p = Polynomial({1.0, 2.0, 3.0, -4.0}); suite.check(std::is_same_v>>); } suite.subTest(testDynamicPolynomial()); suite.subTest(testDynamicPolynomial()); suite.subTest(testDynamicPolynomial>()); suite.subTest(testDynamicPolynomial>()); suite.subTest(testDynamicPolynomial>()); suite.subTest(testDynamicPolynomial>()); suite.subTest(testIntegerSequencePolynomial()); suite.subTest(testIntegerSequencePolynomial()); suite.subTest(testTuplePolynomial()); suite.subTest(testTuplePolynomial()); return suite.exit(); } dune-functions-2.11.0+dfsg/dune/functions/analyticfunctions/trigonometricfunction.hh000066400000000000000000000027521513634022200311330ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_ANALYTICFUNCTIONS_TRIGONOMETRICFUNCTION_HH #define DUNE_FUNCTIONS_ANALYTICFUNCTIONS_TRIGONOMETRICFUNCTION_HH #include namespace Dune { namespace Functions { /** * \brief A linear combination of trigonomic functions * * \ingroup FunctionImplementations * * \tparam K Scalar type. The polynomial will map K to K * \tparam sinFactor Factor in front of sin * \tparam cosFactor Factor in front of cos * * This class exists mainly to demonstrate how to implement * the \ref Concept::DifferentiableFunction concept. */ template class TrigonometricFunction { public: //! Evaluate function K operator () (const K& x) const { return sinFactor * std::sin(x) + cosFactor * std::cos(x); } }; //! Obtain derivative of TrigonometricFunction function \ingroup FunctionImplementations template TrigonometricFunction derivative(const TrigonometricFunction& f) { return TrigonometricFunction(); } }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_ANALYTICFUNCTIONS_TRIGONOMETRICFUNCTION_HH dune-functions-2.11.0+dfsg/dune/functions/backends/000077500000000000000000000000001513634022200221665ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/backends/CMakeLists.txt000066400000000000000000000006161513634022200247310ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory("test") install(FILES concepts.hh containerfactory.hh istlvectorbackend.hh istlvectorfactory.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/functions/backends) dune-functions-2.11.0+dfsg/dune/functions/backends/concepts.hh000066400000000000000000000023501513634022200243250ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_BACKEND_CONCEPTS_HH #define DUNE_FUNCTIONS_BACKEND_CONCEPTS_HH #include #include namespace Dune { namespace Functions { namespace Concept { using namespace Dune::Concept; // Concept for a ConstVectorBackend template struct ConstVectorBackend { template auto require(const V& v) -> decltype( v[std::declval()] ); }; // Concept for a VectorBackend template struct VectorBackend : Refines> { template auto require(const V& v) -> decltype( const_cast(v).resize(std::declval()), const_cast(v)[std::declval()] = v[std::declval()] ); }; } // namespace Dune::Functions::Concept } // namespace Dune::Functions } // namespace Dune #endif // DUNE_FUNCTIONS_BACKEND_CONCEPTS_HH dune-functions-2.11.0+dfsg/dune/functions/backends/containerfactory.hh000066400000000000000000000111311513634022200260560ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_BACKENDS_CONTAINERFACTORY_HH #define DUNE_FUNCTIONS_BACKENDS_CONTAINERFACTORY_HH #include #include #include #include #include #include #include #include namespace Dune::Functions { namespace ContainerDescriptors { namespace Impl { template struct ContainerFactory { void operator() (const Unknown& descriptor, const T& defaultValue) const { DUNE_THROW(Dune::NotImplemented, "Cannot create a vector. The container descriptor is unknown."); } template auto operator() (const Tuple& descriptor, const T& defaultValue) const { return unpackIntegerSequence([&](auto... ii) { return Dune::TupleVector{(*this)(descriptor[ii], defaultValue)...}; }, std::make_index_sequence()); } template auto operator() (const Array& descriptor, const T& defaultValue) const { using ChildContainer = decltype((*this)(descriptor[0], defaultValue)); return unpackIntegerSequence([&](auto... ii) { return std::array{(*this)(descriptor[ii], defaultValue)...}; }, std::make_index_sequence()); } template auto operator() (const Vector& descriptor, const T& defaultValue) const { using ChildContainer = decltype((*this)(descriptor[0], defaultValue)); auto result = std::vector(); result.reserve(descriptor.size()); for (std::size_t i = 0; i < descriptor.size(); ++i) result.emplace_back((*this)(descriptor[i], defaultValue)); return result; } template auto operator() (const UniformArray& descriptor, const T& defaultValue) const { using ChildContainer = decltype((*this)(descriptor[0], defaultValue)); auto childContainer = (*this)(descriptor[0], defaultValue); return unpackIntegerSequence([&](auto... ii) { return std::array{((void)(ii),childContainer)...}; }, std::make_index_sequence()); } template auto operator() (const UniformVector& descriptor, const T& defaultValue) const { using ChildContainer = decltype((*this)(descriptor[0], defaultValue)); auto childContainer = (*this)(descriptor[0], defaultValue); return std::vector(descriptor.size(), childContainer); } // scalar types auto operator() (const Value& descriptor, const T& defaultValue) const { return T(defaultValue); } }; } // end namespace Impl } // end namespace ContainerDescriptors /** * \brief Construct a nested random access container compatible with the container descriptor. * * \param descriptor A ContainerDescriptor provided by a global basis * \param defaultValue The default value to initialize the container entries. * * The constructed container mimics the nested structure of the container descriptor, * but uses data structures like `std::vector`, `std::array`, and `Dune::TupleVector` * to represent the block levels. The entries in the vector are of type `T` and * initialized with the provided default value. */ template auto makeContainer (const ContainerDescriptor& descriptor, const T& defaultValue) { auto factory = ContainerDescriptors::Impl::ContainerFactory{}; return factory(descriptor, defaultValue); } /** * \brief Construct a nested random access container compatible with the container descriptor. * * \param descriptor A ContainerDescriptor provided by a global basis * * The constructed container mimics the nested structure of the container descriptor, * but uses data structures like `std::vector`, `std::array`, and `Dune::TupleVector` * to represent the block levels. The entries in the vector are of type `T` and * default initialized. */ template auto makeContainer (const ContainerDescriptor& descriptor) { return makeContainer(descriptor, T()); } } // end namespace Dune::Functions #endif // DUNE_FUNCTIONS_BACKENDS_CONTAINERFACTORY_HH dune-functions-2.11.0+dfsg/dune/functions/backends/istlvectorbackend.hh000066400000000000000000000312351513634022200262210ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_ISTLVECTORBACKEND_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_ISTLVECTORBACKEND_HH #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { namespace Impl { template() , int> = 0> auto fieldTypes(V&& /*v*/) { return TypeList{}; } template(), int> = 0> auto fieldTypes(V&& v) { if constexpr (Dune::models, V>()) return fieldTypes(v[std::size_t{0}]); else { auto indexRange = typename decltype(range(Hybrid::size(v)))::integer_sequence(); return unpackIntegerSequence([&](auto... i) { return uniqueTypeList(std::tuple_cat(fieldTypes(v[i])...)); }, indexRange); } } } // namespace Impl /** * \brief Generate list of field types in container * * This generates a Dune::TypeList of the field types * in the given container type. To determine the field * types, operator[] is called as often as passible with * std::size_t or Dune::index_constant arguments. The return * types obtained if no more operator[] call is available * are returned as Dune::TypeList. Notice that possible duplicate * entries are removed. However, const and reference qualifiers * are deliberately preserved. */ template constexpr auto fieldTypes() { return decltype(Impl::fieldTypes(std::declval())){}; } /** * \brief Check if container has a unique field type * * This returns if fieldTypes() has exactly one entry. */ template constexpr bool hasUniqueFieldType() { return std::tuple_size_v())>> ==1; } namespace Impl { /* * \brief A wrapper providing multi-index access to vector entries * * The wrapped vector type should be an istl like random * access container providing operator[] and size() methods. * For classical containers this should support indices * of type std::size_t. For multi-type containers indices * of the form Dune::index_constant should be supported * while size() should be a static constexpr method. * * When resolving multi-indices the backend appends indices * using operator[] as long as the result is not a scalar. * If this exhausts the digits of the multi-index, additional * zeros are appended. * * \tparam V Type of the raw wrapper vector */ template class ISTLVectorBackend { // Template aliases for using detection idiom. template using dynamicIndexAccess_t = decltype(std::declval()[0]); template using staticIndexAccess_t = decltype(std::declval()[Dune::Indices::_0]); template using resizeMethod_t = decltype(std::declval().resize(0)); // Short cuts for feature detection template using hasDynamicIndexAccess = Dune::Std::is_detected>; template using hasStaticIndexAccess = Dune::Std::is_detected>; template using hasResizeMethod = Dune::Std::is_detected>; template using isDynamicVector = Dune::Std::is_detected>; template using isStaticVector = std::bool_constant< Dune::Std::is_detected_v> and not Dune::Std::is_detected_v>>; template using isScalar = std::bool_constant>>; template using isVector = std::bool_constant>>; template static void forwardToResize(Args&&... args) { resize(std::forward(args)...); } template::value, int> = 0> static void resize(C&& c, const SizeProvider& sizeProvider, typename SizeProvider::SizePrefix prefix) { auto size = sizeProvider.size(prefix); if (size==0) { // If size==0 this prefix refers to a single coefficient c. // But being in this overload means that c is not a scalar // because is has a resize() method. Since operator[] deliberately // supports implicit padding of multi-indices by as many // [0]'s as needed to resolve a scalar entry, we should also // except a non-scalar c here. However, this requires that // we silently believe that whatever size c already has is // intended by the user. The only exception is c.size()==0 // which is not acceptable but we also cannot know the desired size. if (c.size()==0) DUNE_THROW(RangeError, "The vector entry v[" << prefix << "] should refer to a " << "scalar coefficient, but is a dynamically sized vector of size==0"); else // Accept non-zero sized coefficients to avoid that resize(basis) // fails for a vector that works with operator[] and already // has the appropriate size. return; } c.resize(size); prefix.push_back(0); for(std::size_t i=0; i::value, int> = 0, std::enable_if_t::value, int> = 0> static void resize(C&& c, const SizeProvider& sizeProvider, typename SizeProvider::SizePrefix prefix) { auto size = sizeProvider.size(prefix); // If size == 0 there's nothing to do: // We can't resize c and it's already // large enough anyway. if (size == 0) return; // If size>0 but c does not have the appropriate // size we throw an exception. // // We could perhaps also allow c.size()>size. // But then looping the loop below gets complicated: // We're not allowed to loop until c.size(). But // we also cannot use size for termination, // because this fails if c is a static vector. if (c.size() != size) DUNE_THROW(RangeError, "Can't resize non-resizable entry v[" << prefix << "] of size " << c.size() << " to size(" << prefix << ")=" << size); // Recursively resize all entries of c now. using namespace Dune::Hybrid; prefix.push_back(0); forEach(integralRange(Hybrid::size(c)), [&](auto&& i) { prefix.back() = i; // Here we'd simply like to call resize(c[i], sizeProvider, prefix); // but even gcc-7 does not except this bus reports // "error: ‘this’ was not captured for this lambda function" // although there's no 'this' because we're in a static method. // Bypassing this by and additional method that does perfect // forwarding allows to workaround this. ISTLVectorBackend::forwardToResize(c[i], sizeProvider, prefix); }); } template::value, int> = 0, std::enable_if_t::value, int> = 0> static void resize(C&&, const SizeProvider& sizeProvider, typename SizeProvider::SizePrefix prefix) { auto size = sizeProvider.size(prefix); if (size != 0) DUNE_THROW(RangeError, "Can't resize scalar vector entry v[" << prefix << "] to size(" << prefix << ")=" << size); } template, int> = 0> void recursiveAssign(C& c, const T& t) { c = t; } template, int> = 0> void recursiveAssign(C& c, const T& t) { Dune::Hybrid::forEach(c, [&](auto&& ci) { recursiveAssign(ci, t); }); } public: using Vector = V; ISTLVectorBackend(Vector& vector) : vector_(&vector) {} template void resize(const SizeProvider& sizeProvider) { auto prefix = typename SizeProvider::SizePrefix(); prefix.resize(0); resize(*vector_, sizeProvider, prefix); } template decltype(auto) operator[](const MultiIndex& index) const { return resolveDynamicMultiIndex(*vector_, index); } template decltype(auto) operator[](const MultiIndex& index) { return resolveDynamicMultiIndex(*vector_, index); } /** * \brief Assign value to wrapped vector * * If the wrapped vector type supports assignment from T, * then this is used. Otherwise assignment is done by recursively * assigning all entries from T. The recursion stops for * the first nested entry type which is assignable from T. */ template void operator= (const T& other) { recursiveAssign(vector(), other); } template void operator= (const ISTLVectorBackend& other) { vector() = other.vector(); } const Vector& vector() const { return *vector_; } Vector& vector() { return *vector_; } private: Vector* vector_; }; } // end namespace Impl /** * \brief Return a vector backend wrapping non-const ISTL like containers * * \ingroup FunctionSpaceBasesUtilities * * The returned object implements the VectorBackend concept and * can be used for all dune-functions * utilities requiring a coefficient vector (e.g. interpolate() * and DiscreteGlobalBasisFunction). It essentially provides * operator[] access using multi-indices and a recursive * resize(GlobalBasis) method for adjusting the size to a * given GlobalBasis. * * Additionally to the VectorBackend interface, provides access * to the wrapped vector using the method vector() and forwards * all assignments to the underlying wrapped vector. * * The wrapped vector type should be a nested ISTL like random * access container providing operator[] and size() methods. * For classical containers this should support indices * of type std::size_t. For multi-type containers indices * of the form Dune::index_constant\ should be supported * while size() should be a static constexpr method. * * When accessing the vector with a multi-index the backend * appends multi-index digits using operator[] as long as the * result is not a scalar. If this exhausts all digits of the * multi-index, additional zeros are appended. * * \tparam Vector Type of the raw wrapper vector */ template auto istlVectorBackend(Vector& v) { static_assert(hasUniqueFieldType(), "Vector type passed to istlVectorBackend() does not have a unique field type."); return Impl::ISTLVectorBackend(v); } /** * \brief Return a vector backend wrapping const ISTL like containers * * \ingroup FunctionSpaceBasesUtilities * * The returned object implements the VectorBackend concept and * can be used for all dune-functions * utilities requiring a coefficient vector (e.g. interpolate() * and DiscreteGlobalBasisFunction. It essentially provides * operator[] access using multi-indices and a recursive * resize(GlobalBasis) method for adjusting the size to a given GlobalBasis. * * Additionally to the VectorBackend interface, provides access * to the wrapped vector using the method vector(). * * The wrapped vector type should be a nested ISTL like random * access container providing operator[] and size() methods. * For classical containers this should support indices * of type std::size_t. For multi-type containers indices * of the form Dune::index_constant\ should be supported * while size() should be a static constexpr method. * * When accessing the vector with a multi-index the backend * appends multi-index digits using operator[] as long as the * result is not a scalar. If this exhausts all digits of the * multi-index, additional zeros are appended. * * \tparam Vector Type of the raw wrapper vector */ template auto istlVectorBackend(const Vector& v) { static_assert(hasUniqueFieldType(), "Vector type passed to istlVectorBackend() does not have a unique field type."); return Impl::ISTLVectorBackend(v); } } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_ISTLVECTORBACKEND_HH dune-functions-2.11.0+dfsg/dune/functions/backends/istlvectorfactory.hh000066400000000000000000000075251513634022200263060ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_BACKENDS_ISTL_VECTORFACTORY_HH #define DUNE_FUNCTIONS_BACKENDS_ISTL_VECTORFACTORY_HH #include #include #include #include #include #include #include namespace Dune::Functions { namespace ContainerDescriptors { namespace Impl { template struct ISTLVectorFactory { void operator() (const Unknown& tree) const { DUNE_THROW(Dune::NotImplemented, "Cannot create a vector. The container descriptor is unknown."); } template auto operator() (const Tuple& tree) const { return unpackIntegerSequence([&](auto... ii) { return Dune::MultiTypeBlockVector{(*this)(tree[ii])...}; }, std::make_index_sequence()); } template auto operator() (const Array& tree) const { return unpackIntegerSequence([&](auto... ii) { return Dune::BlockVector{(*this)(tree[ii])...}; }, std::make_index_sequence()); } template auto operator() (const Vector& tree) const { using W = decltype((*this)(tree[0])); Dune::BlockVector result(tree.size()); for (std::size_t i = 0; i < tree.size(); ++i) result[i] = (*this)(tree[i]); return result; } template auto operator() (const UniformArray& tree) const { auto node = (*this)(tree[0]); return unpackIntegerSequence([&](auto... ii) { return Dune::BlockVector{((void)(ii),node)...}; }, std::make_index_sequence()); } template auto operator() (const UniformVector& tree) const { auto node = (*this)(tree[0]); using W = decltype(node); Dune::BlockVector result(tree.size()); for (std::size_t i = 0; i < tree.size(); ++i) result[i] = node; return result; } // scalar types auto operator() (const Value& tree) const { return T(0); } // flat vectors template auto operator() (const UniformArray& tree) const { return Dune::FieldVector(0); } auto operator() (const UniformVector& tree) const { return Dune::BlockVector(tree.size()); } // block vectors template auto operator() (const UniformVector>& tree) const { return Dune::BlockVector>(tree.size()); } template auto operator() (const Vector>& tree) const { return Dune::BlockVector>(tree.size()); } template auto operator() (const Array,m>& tree) const { return Dune::BlockVector>(m); } }; } // end namespace Impl } // end namespace ContainerDescriptors /** * \brief Construct an istl vector type compatible with the container descriptor. * * The constructed vector mimics the nested structure of the container descriptor, * but uses data structures like `BlockVector` and `FieldVector` to represent the * block levels. The entries in the vector are of type `T` and initialized with * the default value `0`. **/ template auto makeISTLVector (const ContainerDescriptor& tree) { auto factory = ContainerDescriptors::Impl::ISTLVectorFactory{}; return factory(tree); } } // end namespace Dune::Functions #endif // DUNE_FUNCTIONS_BACKENDS_ISTL_VECTORFACTORY_HH dune-functions-2.11.0+dfsg/dune/functions/backends/test/000077500000000000000000000000001513634022200231455ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/backends/test/.gitignore000066400000000000000000000004421513634022200251350ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # temporary files generated by the test system *.log *.trs # individual tests gridviewfunctionspacebasistest dune-functions-2.11.0+dfsg/dune/functions/backends/test/CMakeLists.txt000066400000000000000000000006731513634022200257130ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # tests that should build and run successfully link_libraries(Dune::Functions) dune_add_test(SOURCES containerfactorytest.cc LABELS quick) dune_add_test(SOURCES istlvectorbackendtest.cc LABELS quick) dune_add_test(SOURCES istlvectorfactorytest.cc LABELS quick) dune-functions-2.11.0+dfsg/dune/functions/backends/test/containerfactorytest.cc000066400000000000000000000047441513634022200277370ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include namespace CD = Dune::Functions::ContainerDescriptors; void checkContainerFactory (Dune::TestSuite& test) { using namespace Dune::Indices; auto vec0 = Dune::Functions::makeContainer(CD::Value{}, 42); static_assert(std::is_same_v); test.check(vec0==42, "vec0.value"); auto vec1 = Dune::Functions::makeContainer(CD::FlatArray<2>{}); static_assert(std::is_same_v>); auto vec2 = Dune::Functions::makeContainer(CD::FlatVector{10}); static_assert(std::is_same_v>); test.check(vec2.size() == 10, "vec2.size"); auto vec3 = Dune::Functions::makeContainer(CD::UniformVector>{10}); static_assert(std::is_same_v>>); test.check(vec3.size() == 10, "vec3.size"); // more complicated test CD::Tuple,CD::FlatVector> stokes{ CD::Array{CD::FlatVector{10},CD::FlatVector{10},CD::FlatVector{10}}, CD::FlatVector{5} }; auto vec4 = Dune::Functions::makeContainer(stokes); static_assert(std::is_same_v, 3>, std::vector>>); test.check(vec4.size() == 2, "vec4.size"); test.check(vec4[_0].size() == 3, "vec4[0].size"); test.check(vec4[_1].size() == 5, "vec4[01.size"); test.check(vec4[_0][0].size() == 10, "vec4[0][0].size"); test.check(vec4[_0][1].size() == 10, "vec4[0][1].size"); test.check(vec4[_0][2].size() == 10, "vec4[0][2].size"); auto vec5 = Dune::Functions::makeContainer(stokes[_0]); test.check(vec5.size() == vec4[_0].size(), "vec5 == vec4[0]"); static_assert(std::is_same_v, 3>>); test.check(vec4.size() == 2, "vec4.size"); } int main (int argc, char *argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; checkContainerFactory(test); return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/backends/test/istlvectorbackendtest.cc000066400000000000000000000157161513634022200300740ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; /** * \brief A Dummy global basis * * This is a mock class providing non-uniform size information. * It's non-uniform in the sense that not all multi-indices * have the same length. */ template class GlobalBasisMoc { public: using size_type = std::size_t; using SizePrefix = Dune::ReservedVector; using MultiIndex = Dune::ReservedVector; /** * \brief Construct from basis */ GlobalBasisMoc() {} /** * \brief Return number possible values for next position in multi index * * This shall vanish. It's just here such that this can be used * as size provider n place of the basis. */ size_type size(const SizePrefix& prefix) const { if (prefix.size() == 0) return 2; if (prefix.size() == 1) { if (prefix[0] == 0) return 23; if (prefix[0] == 1) return 42; } if (prefix.size() == 2) { if (prefix[0] == 0) return dim; if (prefix[0] == 1) return 0; } if (prefix.size() == 3) return 0; return 0; } operator size_type () const { return 23*dim+42; } }; template Dune::TestSuite checkISTLVectorBackend(std::string shortName="") { Dune::TestSuite test(shortName); using namespace Dune::Indices; using Basis = GlobalBasisMoc; using SizePrefix = typename Basis::SizePrefix; Basis basis; // Create raw vector Vector x_raw; // Create wrapped vector auto x = Dune::Functions::istlVectorBackend(x_raw); test.require(Dune::models, decltype(x)>(), "VectorBackend concept check") << "Object returned by istlVectorBackend() does not model the VectorBackend concept"; // Resize wrapped vector using basis x.resize(basis); // Derive size information from vector test.require(x_raw.size() == basis.size(SizePrefix{}), "resize check") << "x_raw.size() is " << x_raw.size() << " but should be " << basis.size(SizePrefix{}); test.require(x_raw[_0].size() == basis.size(SizePrefix{0}), "resize check") << "x_raw[_0].size() is " << x_raw[_0].size() << " but should be " << basis.size(SizePrefix{0}); for (std::size_t i=0; i>; using PressureVector = std::vector; using Coefficient = double; using Vector = Dune::TupleVector; using MultiIndex = ReservedVector; test.subTest(checkISTLVectorBackend("TV>, V>")); } { using VelocityVector = std::vector>>; using PressureVector = std::vector>; using Coefficient = double; using Vector = Dune::TupleVector; using MultiIndex = ReservedVector; test.subTest(checkISTLVectorBackend("TV>>, V>>")); } { static const std::size_t dim = 5; using VelocityVector = std::vector,dim>>; using PressureVector = std::vector; using Coefficient = double; using Vector = Dune::TupleVector; using MultiIndex = ReservedVector; test.subTest(checkISTLVectorBackend("TV,5>>, V>")); } { static const std::size_t dim = 5; using VelocityVector = Dune::BlockVector>; using PressureVector = Dune::BlockVector>; using Coefficient = double; using Vector = Dune::MultiTypeBlockVector; using MultiIndex = ReservedVector; test.subTest(checkISTLVectorBackend("MTBV>, BV>>")); } { static const std::size_t dim = 3; using VelocityVector = std::vector, double, Dune::FieldVector>>; using PressureVector = Dune::BlockVector>; using Coefficient = double; using Vector = Dune::MultiTypeBlockVector; using MultiIndex = ReservedVector; test.subTest(checkISTLVectorBackend("MTBV, double, FV>>, BV>")); } return test.exit(); } // Error handling catch (Exception& e) { std::cout << e.what() << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/backends/test/istlvectorfactorytest.cc000066400000000000000000000044021513634022200301420ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include namespace CD = Dune::Functions::ContainerDescriptors; void checkVectorFactory (Dune::TestSuite& test) { using namespace Dune::Indices; auto vec0 = Dune::Functions::makeISTLVector(CD::Value{}); static_assert(std::is_same_v); auto vec1 = Dune::Functions::makeISTLVector(CD::FlatArray<2>{}); static_assert(std::is_same_v>); auto vec2 = Dune::Functions::makeISTLVector(CD::FlatVector{10}); static_assert(std::is_same_v>); test.check(vec2.size() == 10, "vec2.size"); auto vec3 = Dune::Functions::makeISTLVector(CD::UniformVector>{10}); static_assert(std::is_same_v>>); test.check(vec3.size() == 10, "vec3.size"); // more complicated test CD::Tuple,CD::FlatVector> stokes{ CD::Array{CD::FlatVector{10},CD::FlatVector{10},CD::FlatVector{10}}, CD::FlatVector{5} }; auto vec4 = Dune::Functions::makeISTLVector(stokes); test.check(vec4.size() == 2, "vec4.size"); test.check(vec4[_0].size() == 3, "vec4[0].size"); test.check(vec4[_1].size() == 5, "vec4[01.size"); test.check(vec4[_0][0].size() == 10, "vec4[0][0].size"); test.check(vec4[_0][1].size() == 10, "vec4[0][1].size"); test.check(vec4[_0][2].size() == 10, "vec4[0][2].size"); auto vec5 = Dune::Functions::makeISTLVector(stokes[_0]); test.check(vec5.size() == vec4[_0].size(), "vec5 == vec4[0]"); } int main (int argc, char *argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; checkVectorFactory(test); return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/common/000077500000000000000000000000001513634022200217045ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/common/CMakeLists.txt000066400000000000000000000016261513634022200244510ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory("test") install(FILES defaultderivativetraits.hh densevectorview.hh differentiablefunction.hh differentiablefunction_imp.hh differentiablefunctionfromcallables.hh functionconcepts.hh geometryinancestor.hh indexaccess.hh interfaces.hh localfunction.hh localfunction_imp.hh mapperutilities.hh multiindex.hh overflowarray.hh polymorphicsmallobject.hh reserveddeque.hh signature.hh squeezetensor.hh staticforloop.hh subdomain.hh type_traits.hh typeerasure.hh utility.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/functions/common) dune-functions-2.11.0+dfsg/dune/functions/common/defaultderivativetraits.hh000066400000000000000000000050321513634022200271630ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_DEFAULT_DERIVATIVE_TRAITS_HH #define DUNE_FUNCTIONS_COMMON_DEFAULT_DERIVATIVE_TRAITS_HH #include #include #include #include namespace Dune { namespace Functions { /** * \brief Dummy range class to be used if no proper type is available * * \ingroup FunctionUtility */ class InvalidRange {}; /** * \brief Default implementation for derivative traits * * \ingroup FunctionUtility * * This class provides sensible defaults for the range * of derivatives of functions with some common \p Domain * and \p Range types. */ template struct DefaultDerivativeTraits { //! Range of derivative for function with given signature typedef InvalidRange Range; }; /** * \brief Default implementation for derivative traits * * \ingroup FunctionUtility * * Specialization for Signature = double(double) */ template<> struct DefaultDerivativeTraits< double(double) > { //! \copydoc DefaultDerivativeTraits::Range typedef double Range; }; /** * \brief Default implementation for derivative traits * * \ingroup FunctionUtility * * \tparam K Scalar range type * * Specialization for Signature = K(FieldVector) */ template struct DefaultDerivativeTraits)> { //! \copydoc DefaultDerivativeTraits::Range typedef FieldVector Range; }; /** * \brief Default implementation for derivative traits * * \ingroup FunctionUtility * * \tparam K Scalar range type * * Specialization for Signature = FieldVector(FieldVector) */ template struct DefaultDerivativeTraits(FieldVector)> { //! \copydoc DefaultDerivativeTraits::Range typedef FieldMatrix Range; }; /** * \brief Default implementation for derivative traits * * \ingroup FunctionUtility * * \tparam K Scalar range type * * Specialization for Signature = FieldMatrix(FieldVector) */ template struct DefaultDerivativeTraits(FieldVector)> { //! \copydoc DefaultDerivativeTraits::Range typedef FieldMatrix Range; }; }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_COMMON_DEFAULT_DERIVATIVE_TRAITS_HH dune-functions-2.11.0+dfsg/dune/functions/common/densevectorview.hh000066400000000000000000000053641513634022200254510ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_DENSEVECTORVIEW_HH #define DUNE_FUNCTIONS_COMMON_DENSEVECTORVIEW_HH #include #include namespace Dune::Functions::Impl { /** * \brief Wrapper providing the DenseVector interface for a random access container */ template class DenseVectorView : public DenseVector> { R& data_; using mutable_reference = typename std::decay_t::reference; public: //! The type used for array indices and sizes using size_type = typename std::decay_t::size_type; //! The type of values using value_type = typename std::decay_t::value_type; //! The type used for const references to the vector entry using const_reference = typename std::decay_t::const_reference; //! The type used for references to the vector entry using reference = std::conditional_t, const_reference, mutable_reference>; //! Construct from a pointer to a scalar DenseVectorView (R& data) : data_(data) {} //! The copy constructor is deleted DenseVectorView (const DenseVectorView &other) = delete; //! Move constructor DenseVectorView (DenseVectorView &&other) : data_( other.data_ ) {} //! Copy assignment operator DenseVectorView& operator= (const DenseVectorView& other) { data_ = other.data_; return *this; } //! Copy assignment operator template DenseVectorView& operator= (const DenseVectorView& other) { data_ = other.data_; return *this; } //! Container size size_type size () const { return data_.size(); } //! Random access operator reference operator[] (size_type i) { return data_[i]; } //! Random access operator const_reference operator[] (size_type i) const { return data_[i]; } }; // class DenseVectorView } namespace Dune { template< class R> struct DenseMatVecTraits< Dune::Functions::Impl::DenseVectorView > { using derived_type = Dune::Functions::Impl::DenseVectorView; using value_type = typename R::value_type; using size_type = typename R::size_type; }; template< class R > struct FieldTraits< Dune::Functions::Impl::DenseVectorView > : public FieldTraits::value_type>> {}; } #endif // DUNE_FUNCTIONS_COMMON_DENSEVECTORVIEW_HH dune-functions-2.11.0+dfsg/dune/functions/common/differentiablefunction.hh000066400000000000000000000110631513634022200267370ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_DIFFERENTIABLE_FUNCTION_HH #define DUNE_FUNCTIONS_COMMON_DIFFERENTIABLE_FUNCTION_HH #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { /* * Default implementation is empty * The actual implementation is only given if Signature is an type * describing a function signature as Range(Domain). */ template class DerivativeTraits=DefaultDerivativeTraits, size_t bufferSize=56> class DifferentiableFunction {}; namespace Imp { /// Traits class providing type information for DifferentiableFunction template class DerivativeTraits, size_t bufferSize> struct DifferentiableFunctionTraits { /// Signature type using Signature = S; /// Range type using Range = typename SignatureTraits::Range; /// Domain type using Domain = typename SignatureTraits::Domain; /// Signature of the derivative using DerivativeSignature = typename SignatureTraits::template DerivativeSignature; /// Interface type of the derivative using DerivativeInterface = DifferentiableFunction; /// Internal concept type for type erasure using Concept = DifferentiableFunctionWrapperInterface; /// Internal model template for type erasure template using Model = DifferentiableFunctionWrapperImplementation; }; } /** * \brief Class storing differentiable functions using type erasure * * \ingroup FunctionInterface * * \tparam Range Range type * \tparam Domain Domain type * \tparam DerivativeTraits Traits class to determine range of derivative (defaults to DefaultDerivativeTraits) * \tparam bufferSize Size of stack buffer for small object optimization (defaults to 56) * * This models the \ref Concept::DifferentiableFunction concept. * Small object optimization is used to store the given function. * If its size exceed \p bufferSize, memory will be allocated dynamically. */ template class DerivativeTraits, size_t bufferSize> class DifferentiableFunction< Range(Domain), DerivativeTraits, bufferSize> : public TypeErasureBase< typename Imp::DifferentiableFunctionTraits::Concept, Imp::DifferentiableFunctionTraits::template Model> { using Traits = Imp::DifferentiableFunctionTraits; using Base = TypeErasureBase; using DerivativeInterface = typename Traits::DerivativeInterface; public: /** * \brief Construct from function * * \tparam F Function type * * \param f Function of type F * * Calling derivative(DifferentiableFunction) will result in an exception * if the passed function does provide a free derivative() function * found via ADL. */ template = 0 > DifferentiableFunction(F&& f) : Base(std::forward(f)) { static_assert(Dune::Functions::Concept::isFunction(), "Trying to construct a DifferentiableFunction from type that does not model the Function concept"); } //! Default constructor DifferentiableFunction() = default; /** * \brief Evaluation of wrapped function */ Range operator() (const Domain& x) const { return this->asInterface().operator()(x); } /** * \brief Get derivative of wrapped function * * \ingroup FunctionInterface * * This is a free function that will be found by ADL. */ friend DerivativeInterface derivative(const DifferentiableFunction& t) { return t.asInterface().derivative(); } }; }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_COMMON_DIFFERENTIABLE_FUNCTION_HH dune-functions-2.11.0+dfsg/dune/functions/common/differentiablefunction_imp.hh000066400000000000000000000046551513634022200276150ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_DIFFERENTIABLE_FUNCTION_IMP_HH #define DUNE_FUNCTIONS_COMMON_DIFFERENTIABLE_FUNCTION_IMP_HH #include #include #include namespace Dune { namespace Functions { namespace Imp { /** * A concept describing types that have a derivative() method found by ADL */ struct HasFreeDerivative { template auto require(F&& f) -> decltype( derivative(f) ); }; template() , int> = 0> auto derivativeIfImplemented(const F& f) -> decltype(derivative(f)) { return derivative(f); } template()) , int> = 0> Dummy derivativeIfImplemented(const F& f) { DUNE_THROW(Dune::NotImplemented, "Derivative not implemented"); } template class DifferentiableFunctionWrapperInterface {}; // Interface of type erasure wrapper // // Notice that the basic interface of polymorphic classes (destructor, clone, ...) // will be added by the type erasure foundation classes. template class DifferentiableFunctionWrapperInterface { public: virtual Range operator() (const Domain& x) const = 0; virtual DerivativeInterface derivative() const = 0; }; template class DifferentiableFunctionWrapperImplementation {}; // Implementation of type erasure wrapper template class DifferentiableFunctionWrapperImplementation< Range(Domain), DerivativeInterface, B> : public B { public: using B::B; using Wrapped = typename B::Wrapped; virtual Range operator() (const Domain& x) const { return this->get()(x); } virtual DerivativeInterface derivative() const { return derivativeIfImplemented(this->get()); } }; }}} // namespace Dune::Functions::Imp #endif // DUNE_FUNCTIONS_COMMON_DIFFERENTIABLE_FUNCTION_IMP_HH dune-functions-2.11.0+dfsg/dune/functions/common/differentiablefunctionfromcallables.hh000066400000000000000000000126431513634022200314730ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_DIFFEREENTIONABEFUNCTIONFROMCALLABLES_HH #define DUNE_FUNCTIONS_COMMON_DIFFEREENTIONABEFUNCTIONFROMCALLABLES_HH #include #include #include #include #include namespace Dune { namespace Functions { template class DerivativeTraits, class... Callables> class DifferentiableFunctionFromCallables; /** * \brief Wrap a list of callable objects as derivative sequence modelling \ref Concept::DifferentiableFunction * * \ingroup FunctionImplementations * * \tparam Range Range type of function * \tparam Domain Domain type of function * * You can use this to implement a differentiable function including * a variable number of derivatives using callable objects. * * This models the \ref Concept::DifferentiableFunction concept. * * Note that using makeDifferentiableFunction will be less verbose than * creating this wrapper manually. */ template class DerivativeTraits, class F> class DifferentiableFunctionFromCallables { public: //! Signature of function using Signature = Range(Domain); using RawSignature = typename SignatureTraits::RawSignature; //! Signature of derivative using DerivativeSignature = typename DerivativeTraits::Range(Domain); //! Type of derivative using Derivative = DifferentiableFunction; //! Constructor copying the given function template = 0> DifferentiableFunctionFromCallables(FF&& f) : f_(std::forward(f)) {} //! Evaluate function Range operator() (const Domain& x) const { return f_(x); } /** * \brief Get derivative of DifferentiableFunctionFromCallables * * \ingroup FunctionImplementations */ friend Derivative derivative(const DifferentiableFunctionFromCallables& t) { DUNE_THROW(Dune::NotImplemented, "Derivative not implemented"); } private: F f_; }; /** * \brief Wrap a list of callable objects as derivative sequence modelling \ref Concept::DifferentiableFunction * * \ingroup FunctionImplementations * * \tparam Range Range type of function * \tparam Domain Domain type of function * * You can use this to implement a differentiable function including * a variable number of derivatives using callable objects. * * This models the \ref Concept::DifferentiableFunction concept. * * Note that using makeDifferentiableFunction will be less verbose than * creating this wrapper manually. */ template class DerivativeTraits, class F, class DF, class... Derivatives> class DifferentiableFunctionFromCallables { public: using Signature = Range(Domain); using RawSignature = typename SignatureTraits::RawSignature; using DerivativeSignature = typename DerivativeTraits::Range(Domain); using Derivative = DifferentiableFunctionFromCallables; /** * \brief Constructor copying the given functions * * The arguments are used as implementation of the functions itself * and its derivatives with increasing order */ template DifferentiableFunctionFromCallables(FF&& f, DFF&& df, DDFF&&... ddf) : f_(std::forward(f)), df_(std::forward(df), std::forward(ddf)...) {} //! Evaluate function Range operator() (const Domain& x) const { return f_(x); } /** * \brief Get derivative of DifferentiableFunctionFromCallables * * \ingroup FunctionImplementations */ friend Derivative derivative(const DifferentiableFunctionFromCallables& t) { return t.df_; } private: F f_; Derivative df_; }; /** * \brief Create a DifferentiableFunction from callables * * \ingroup FunctionImplementations * * This will return a wrapper modelling the DifferentiableFunction interface * where the evaluation of the function and its derivatives are implemented * by the given callable objects. * * \param signatureTag A dummy parameter to pass the signature and derivative traits * \param f Callable objects implementing the evaluation of the function and its derivatives * * \returns Object modelling DifferentiableFunction interface */ template class DerivativeTraits, class... F> DifferentiableFunctionFromCallables makeDifferentiableFunctionFromCallables(const SignatureTag& signatureTag, F&&... f) { return DifferentiableFunctionFromCallables(f...); } } // namespace Functions } // namespace Dune #endif //DUNE_FUNCTIONS_COMMON_DIFFEREENTIONABEFUNCTIONFROMCALLABLES_HH dune-functions-2.11.0+dfsg/dune/functions/common/functionconcepts.hh000066400000000000000000000331331513634022200256140ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_FUNCTIONCONCEPT_HH #define DUNE_FUNCTIONS_COMMON_FUNCTIONCONCEPT_HH #include #include #include #include #include namespace Dune { namespace Functions { namespace Concept { using namespace Dune::Concept; // Callable concept ############################################################ /** * \brief Concept objects that can be called with given argument list * * \ingroup FunctionConcepts * * \tparam Args Argument list for function call */ template struct Callable { template auto require(F&& f) -> decltype( f(std::declval()...) ); }; /** * \brief Check if f is callable with given argument list * * \ingroup FunctionConcepts * \ingroup Utility */ template static constexpr auto isCallable() { return models, F>(); } /** * \brief Check if f is callable with given argument list * * \ingroup FunctionConcepts * \ingroup Utility */ template static constexpr auto isCallable(F&&, Args&&...) { return models, F>(); } // Function concept ############################################################ template struct Function; /** * \brief Concept for a function mapping \p Domain to \p Range * * \ingroup FunctionConcepts * * \tparam Domain Domain type * \tparam Range Range type */ template struct Function : Refines > { template auto require(F&& f) -> decltype( // F models Function if the result of F(Domain) is implicitly convertible to Range requireConvertible(f(std::declval())) ); }; /// Check if F models the Function concept with given signature \ingroup FunctionConcepts template static constexpr bool isFunction() { return models, F>(); } /// Check if f models the Function concept with given signature \ingroup FunctionConcepts template class DerivativeTraits> static constexpr bool isFunction(F&& f, SignatureTag) { return models, F>(); } // DifferentiableFunction concept ############################################## template class DerivativeTraits = DefaultDerivativeTraits> struct DifferentiableFunction; /** * \brief Concept for a differentiable function mapping \p Domain to \p Range * * \ingroup FunctionConcepts * * The derivative range is derived from the provided \p DerivativeTraits * * \tparam Domain Domain type * \tparam Range Range type * \tparam DerivativeTraits Traits class for computation of derivative range */ template class DerivativeTraits> struct DifferentiableFunction : Refines > { using DerivativeSignature = typename SignatureTraits::template DerivativeSignature; template auto require(F&& f) -> decltype( derivative(f), requireConcept>(derivative(f)) ); }; /// Check if F models the DifferentiableFunction concept with given signature \ingroup FunctionConcepts template class DerivativeTraits = DefaultDerivativeTraits> static constexpr bool isDifferentiableFunction() { return models, F>(); } /// Check if f models the DifferentiableFunction concept with given signature \ingroup FunctionConcepts template class DerivativeTraits> static constexpr bool isDifferentiableFunction(F&& f, SignatureTag) { return models, F>(); } // LocalFunction concept ############################################## template struct LocalFunction; /** * \brief Concept for a local function mapping \p Domain to \p Range * * \ingroup FunctionConcepts * * \tparam Domain Domain type * \tparam Range Range type * \tparam LocalContext The local context this function is defined on */ template struct LocalFunction : Refines > { template auto require(F&& f) -> decltype( f.bind(std::declval()), f.unbind(), requireConvertible(f.bound()), f.localContext(), requireConvertible(f.localContext()) ); }; /// Check if F models the LocalFunction concept with given signature and local context \ingroup FunctionConcepts template static constexpr bool isLocalFunction() { return models, F>(); } // DifferentiableLocalFunction concept ############################################## template class DerivativeTraits = DefaultDerivativeTraits> struct DifferentiableLocalFunction; /** * \brief Concept for a differentiable local function mapping \p Domain to \p Range * * \ingroup FunctionConcepts * * The derivative range is derived from the provided \p DerivativeTraits * * \tparam Domain Domain type * \tparam Range Range type * \tparam LocalContext The local context this function is defined on * \tparam DerivativeTraits Traits class for computation of derivative range */ template class DerivativeTraits> struct DifferentiableLocalFunction : Refines< Dune::Functions::Concept::DifferentiableFunction, Dune::Functions::Concept::LocalFunction > { template auto require(F&& f) -> decltype( f.bind(std::declval()), f.unbind(), f.localContext(), requireConvertible(f.localContext()) ); }; /// Check if F models the DifferentiableLocalFunction concept with given signature and local context \ingroup FunctionConcepts template class DerivativeTraits = DefaultDerivativeTraits> static constexpr bool isDifferentiableLocalFunction() { return models, F>(); } // EntitySet concept ############################################## /** * \brief Concept for an entity set for a \ref Concept::GridFunction * * \ingroup FunctionConcepts * * This describes the set of entities on which a grid function * can be localized. * */ struct EntitySet { template auto require(E&& f) -> decltype( requireType(), requireType(), requireType() ); }; /// Check if F models the GridFunction concept with given signature and entity set \ingroup FunctionConcepts template static constexpr bool isEntitySet() { return models(); } // GridFunction concept ############################################## template struct GridFunction; /** * \brief Concept for a grid function mapping \p Domain to \p Range * * \ingroup FunctionConcepts * * \tparam Domain Domain type * \tparam Range Range type * \tparam EntitySet Set of entities on which the function can be localized */ template struct GridFunction : Refines > { using LocalSignature = Range(typename EntitySet::LocalCoordinate); using LocalContext = typename EntitySet::Element; template auto require(F&& f) -> decltype( localFunction(f), f.entitySet(), requireConcept>(localFunction(f)), requireConcept(), requireConvertible(f.entitySet()), requireConvertible() ); }; /// Check if F models the GridFunction concept with given signature and entity set \ingroup FunctionConcepts template static constexpr bool isGridFunction() { return models, F>(); } // DifferentiableGridFunction concept ############################################## template class DerivativeTraits = DefaultDerivativeTraits> struct DifferentiableGridFunction; /** * \brief Concept for a differentiable grid function mapping \p Domain to \p Range * * \ingroup FunctionConcepts * * The derivative range is derived from the provided \p DerivativeTraits. * * \tparam Domain Domain type * \tparam Range Range type * \tparam EntitySet Set of entities on which the function can be localized * \tparam DerivativeTraits Traits class for computation of derivative range */ template class DerivativeTraits> struct DifferentiableGridFunction : Refines< Dune::Functions::Concept::DifferentiableFunction, Dune::Functions::Concept::GridFunction > { using LocalSignature = Range(typename EntitySet::LocalCoordinate); using LocalContext = typename EntitySet::Element; template using LocalDerivativeTraits = typename Dune::Functions::LocalDerivativeTraits::template Traits; template auto require(F&& f) -> decltype( requireConcept>(localFunction(f)) ); }; /// Check if F models the DifferentiableGridFunction concept with given signature and entity set \ingroup FunctionConcepts template class DerivativeTraits = DefaultDerivativeTraits> static constexpr bool isDifferentiableGridFunction() { return models, F>(); } // GridViewFunction concept ############################################## template struct GridViewFunction; /** * \brief Concept for a grid view function mapping \p Domain to \p Range * * \ingroup FunctionConcepts * * This exactly the \ref Concept::GridFunction * concept with a \ref GridViewEntitySet as \p EntitySet. * * \tparam Domain Domain type * \tparam Range Range type * \tparam GridView GridView on which the function can be localized */ template struct GridViewFunction : Refines>> { template auto require(F&& f) -> decltype( 0 // We don't need to check any further expressions, because a GridViewFunction is just a GridFunction with a special EntitySet ); }; /// Check if F models the GridViewFunction concept with given signature \ingroup FunctionConcepts template static constexpr bool isGridViewFunction() { return models, F>(); } // DifferentiableGridViewFunction concept ############################################## template class DerivativeTraits = DefaultDerivativeTraits> struct DifferentiableGridViewFunction; /** * \brief Concept for a differentiable grid view function mapping \p Domain to \p Range * * \ingroup FunctionConcepts * * This exactly the \ref Concept::GridFunction * concept with a \ref GridViewEntitySet as \p EntitySet. * * \tparam Domain Domain type * \tparam Range Range type * \tparam GridView GridView on which the function can be localized * \tparam DerivativeTraits Traits class for computation of derivative range */ template class DerivativeTraits> struct DifferentiableGridViewFunction : Refines, DerivativeTraits>> { template auto require(F&& f) -> decltype( 0 // We don't need to check any further expressions, because a GridViewFunction is just a GridFunction with a special EntitySet ); }; /// Check if F models the DifferentiableGridViewFunction concept with given signature \ingroup FunctionConcepts template class DerivativeTraits = DefaultDerivativeTraits> static constexpr bool isDifferentiableGridViewFunction() { return models, F>(); } }}} // namespace Dune::Functions::Concept #endif // DUNE_FUNCTIONS_COMMON_FUNCTIONCONCEPT_HH dune-functions-2.11.0+dfsg/dune/functions/common/geometryinancestor.hh000066400000000000000000000152341513634022200261530ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_GEOMETRYINANCESTOR_HH #define DUNE_FUNCTIONS_COMMON_GEOMETRYINANCESTOR_HH #include #include namespace Dune::Functions { /** * \brief A geometry embedding a descendent element into an ancestor * * \ingroup FunctionUtility * * \tparam Element Type of elements considered by this embedding * * This class will store a chain of `geometryInFather` objects * connecting a coarse element with a fine element. The `global()` * and `local()` methods of the `GeometryInAncestor` simply chain * those stored `geometryInFather` objects. * Since this requires that objects of type `GeometryInAncestor` allocate * dynamic memory, they are not intended to be created on the fly * within a hot loop. Instead one reused a single `GeometryInAncestor` * object by binding if to a new element. * * Currently this only provides the `local()` and `global()` methods * of the geometry interface. */ template class GeometryInAncestor { using GeometryInFather = typename Element::LocalGeometry; template static auto reverse_view(V&& v) { return Dune::IteratorRange(v.rbegin(), v.rend()); } public: //! Type of local coordinate (local within fine element) using LocalCoordinate = typename Element::Geometry::LocalCoordinate; // //! Type of global coordinate (local within coarse element) using GlobalCoordinate = typename Element::Geometry::LocalCoordinate; GeometryInAncestor() = default; GeometryInAncestor(GeometryInAncestor&&) = delete; /** * \brief Copy constructor * * If the original GeometryInAncestor holds a pointer * to an external fine Element, the new GeometryInAncestor * points to the same external fine Element. */ GeometryInAncestor(const GeometryInAncestor& other) : storedFineElement_(other.storedFineElement_) , storedCoarseElement_(other.storedCoarseElement_) , fineElementPtr_(&storedFineElement_) , coarseElementPtr_(&storedFineElement_) , geometryInFathersVector_(other.geometryInFathersVector_) { // If other points to external fine element, // we point to the same one. if (other.fineElementPtr_ != & other.storedFineElement_) fineElementPtr_ = other.fineElementPtr_; // If the other coarse element is not the stored one, // then it's the fine one. if (other.coarseElementPtr_ != & other.storedCoarseElement_) coarseElementPtr_ = fineElementPtr_; } /** * \brief Copy construction setting an external fine element * * This copies the original GeometryInAncestor and sets the * pointer to the external fine element passed additionally. */ GeometryInAncestor(const GeometryInAncestor& other, const Element& fineElement) : storedFineElement_(other.storedFineElement_) , storedCoarseElement_(other.storedCoarseElement_) , fineElementPtr_(&fineElement) , coarseElementPtr_(&storedFineElement_) , geometryInFathersVector_(other.geometryInFathersVector_) { // If the other coarse element is not the stored one, // then it's the fine one. if (other.coarseElementPtr_ != & other.storedCoarseElement_) coarseElementPtr_ = fineElementPtr_; } /** * \brief Bind this GeometryInAncestor to a fine element and search coarse element * * \param fineElement The fine element to be bound to * \param includeFather Unary predicate indicating whether father element should be traversed * * Build the geometry information by traversing the fathers starting from * the given `fineElement` as long as `includeFather(element)` returns `true` * to obtain a chain of `geometryInFather` objects connecting the `fineElement` * with a `coarseElement`. The `coarseElement` is the first element where * `includeFather(coarseElement)` returns `false`. * * This overload for an l-value `fineElement` will store * a pointer to `fineElement`. */ template requires (std::is_invocable_r_v) const Element& bind(const Element& fineElement, F&& includeFather) { fineElementPtr_ = &fineElement; geometryInFathersVector_.clear(); coarseElementPtr_ = fineElementPtr_; while (includeFather(*coarseElementPtr_)) { geometryInFathersVector_.push_back(coarseElementPtr_->geometryInFather()); storedCoarseElement_ = coarseElementPtr_->father(); coarseElementPtr_ = &storedCoarseElement_; } return coarseElement(); } /** * \brief Bind this GeometryInAncestor to a fine element and search coarse element * * \param fineElement The fine element to be bound to * \param includeFather Unary predicate indicating whether father element should be traversed * * Build the geometry information by traversing the fathers starting from * the given `fineElement` as long as `includeFather(element)` returns `true` * to obtain a chain of `geometryInFather` objects connecting the `fineElement` * with a `coarseElement`. The `coarseElement` is the first element where * `includeFather(coarseElement)` returns `false`. * * This overload for an r-value `fineElement` will store * a copy of the `fineElement`. */ template requires (std::is_invocable_r_v) const Element& bind(Element&& fineElement, F&& includeFather) { storedFineElement_ = fineElement; bind(storedFineElement_, includeFather); } //! Return the fine element const Element& fineElement() const { return *fineElementPtr_; } //! Return the coarse element const Element& coarseElement() const { return *coarseElementPtr_; } //! Map local coordinate in fine element into coarse element GlobalCoordinate global(LocalCoordinate x) const { for (const auto& g : geometryInFathersVector_) x = g.global(x); return x; } //! Map local coordinate in coarse element into fine element LocalCoordinate local(GlobalCoordinate x) const { // Here we can be instead use std::ranges::reverse_view once // all supported compilers provide this (gcc>=10, clang>=16). for (const auto& g : reverse_view(geometryInFathersVector_)) x = g.local(x); return x; } private: Element storedFineElement_; Element storedCoarseElement_; const Element* fineElementPtr_ = nullptr; const Element* coarseElementPtr_ = nullptr; std::vector geometryInFathersVector_; }; } // namespace Dune::Functions #endif // DUNE_FUNCTIONS_COMMON_GEOMETRYINANCESTOR_HH dune-functions-2.11.0+dfsg/dune/functions/common/indexaccess.hh000066400000000000000000000304171513634022200245230ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_INDEX_ACCESS_HH #define DUNE_FUNCTIONS_COMMON_INDEX_ACCESS_HH #include #include #include #include #include #include namespace Dune { namespace Functions { namespace Imp { namespace Concept { template struct HasDynamicIndexAccess { template auto require(C&& c) -> decltype( c[std::declval()] ); }; struct HasStaticIndexAccess { template auto require(C&& c) -> decltype( c[Dune::Indices::_0] ); }; } // namespace Concept } // namespace Imp /** * \brief Provide operator[] index-access for containers * * \ingroup Utility * * This is the overload for types providing a operator[] * for dynamic std::size_t arguments. * * \param c Container to access * \param i The index to use for accessing the container * \param f A functor to call with the result of operator[] */ template, C>(), int> = 0> auto hybridIndexAccess(C&& c, const I& i, F&& f) -> decltype(f(c[i])) { return f(c[i]); } /** * \brief Provide operator[] index-access for containers * * \ingroup Utility * * This is the overload for types providing a operator[] * only for static arguments of type std::integral_constant. * This does a static linear search until a static index * matching the given dynamic index is found. * Since the result type will in general be different * for different indices the method does not return * the result directly but passes it to a given functor. * * \param c Container to access * \param i The index to use for accessing the container * \param f A functor to call with the result of operator[] */ template, C>(), int> = 0> decltype(auto) hybridIndexAccess(C&& c, const I& i, F&& f) { using Size = decltype(Hybrid::size(c)); return Hybrid::switchCases(std::make_index_sequence(), i, [&](const auto& ii) -> decltype(auto){ return f(c[ii]); }, [&]() -> decltype(auto){ return f(c[Dune::Indices::_0]); }); } namespace Imp { /** * \brief Class representing a shifted multi index * * \tparam Index Type of the base multi index * \tparam offset Number of positions to shift left * * For a given multi index of size n this * represents a multi index with the first * offset entries removed. * * Notice that this does only store a reference to * the passed multi index. */ template class ShiftedDynamicMultiIndex { public: ShiftedDynamicMultiIndex(const Index& index) : index_(index) {} std::size_t operator[](std::size_t position) const { if (position pop() const { return {index_}; } std::size_t size() const { if (offset < index_.size()) return index_.size() - offset; else return 0; } const Index& originalIndex() const { return index_; } private: const Index& index_; }; template class ShiftedStaticMultiIndex { public: ShiftedStaticMultiIndex(const Index& index) : index_(index) {} template auto operator[](Dune::index_constant) const { if constexpr (i{}]; } else { return Dune::index_constant<0>{}; } } /** * \brief Return multi index with one more position truncated */ ShiftedStaticMultiIndex pop() const { return {index_}; } static constexpr std::size_t size() { auto fullSize = decltype(Hybrid::size(std::declval()))::value; if (offset < fullSize) return fullSize - offset; else return 0; } private: const Index& index_; }; /** * \brief Create a ShiftedDynamicMultiIndex * * \tparam offset Number of positions to shift left */ template ShiftedDynamicMultiIndex shiftedDynamicMultiIndex(const Index& index) { return {index}; } /** * \brief Create a ShiftedDynamicMultiIndex * * \tparam offset Number of positions to shift left * * This overload ensures that shifting a multi index twice will not * nest the shifting wrapper but add the offsets. */ template ShiftedDynamicMultiIndex shiftedDynamicMultiIndex(const ShiftedDynamicMultiIndex& index) { return {index.originalIndex()}; } template ShiftedStaticMultiIndex shiftedStaticMultiIndex(const Index& index) { return {index}; } } // namespace Imp namespace Imp { template struct MultiIndexResolver { MultiIndexResolver(const Index& index) : index_(index) {} template, int> = 0> Result operator()(C&& c) { auto&& subIndex = Imp::shiftedDynamicMultiIndex<1>(index_); auto&& subIndexResolver = MultiIndexResolver(subIndex); return (Result)(hybridIndexAccess(c, index_[Dune::Indices::_0], subIndexResolver)); } template, int> = 0> Result operator()(C&& c) { return (Result)(std::forward(c)); } const Index& index_; }; } // namespace Imp /** * \brief Provide multi-index access by chaining operator[] * * \ingroup Utility * * This provides access to a nested container by given * multi-index. Internally this is resolved by recursive * operator[]-calls with static or dynamic indices. * Because this recursion must be terminated using a * compile-time criterion, the result type must explicitly * be provided. The recursion will terminate once the * result can be converted to this result type. * * \tparam Result Type of result * * \param c Container to access * \param index Multi-index */ template Result hybridMultiIndexAccess(C&& c, const MultiIndex& index) { Imp::MultiIndexResolver multiIndexResolver(index); return multiIndexResolver(c); } namespace Imp { template constexpr decltype(auto) resolveDynamicMultiIndex(C&& c, const MultiIndex& multiIndex, const IsFinal& isFinal) { // If c is already considered final simply return it, // else resolve the next multiIndex entry. return Hybrid::ifElse(isFinal(c), [&, c = forwardCapture(std::forward(c))](auto) -> decltype(auto) { assert(multiIndex.size() == 0); return c.forward(); }, [&](auto) -> decltype(auto) { auto hasDynamicAccess = callableCheck([](auto&& cc) -> std::void_t {}); // Split multiIndex into first entry and remaining ones. auto i = multiIndex[0]; auto tail = multiIndex.pop(); // Resolve first multiIndex entry by c[multiIndex[0]] and // continue resolving with the remaining remaining ones. // If c has a dynamic operator[] this is straight forward. // Else the dynamic multiIndex[0] has to be translated into // a static one using hybridIndexAccess. return Hybrid::ifElse(hasDynamicAccess(c), [&](auto id) -> decltype(auto) { return Imp::resolveDynamicMultiIndex(id(c)[i], tail, isFinal); }, [&](auto id) -> decltype(auto) { // auto indexRange = range(Hybrid::size(id(c))); auto indexRange = typename decltype(range(Hybrid::size(id(c))))::integer_sequence(); return Hybrid::switchCases(indexRange, i, [&](auto static_i) -> decltype(auto){ // Do rescursion with static version of i return Imp::resolveDynamicMultiIndex(id(c)[static_i], tail, isFinal); }, [&]() -> decltype(auto){ // As fallback we use c[0] this is needed, because there must be one branch that matches. return Imp::resolveDynamicMultiIndex(id(c)[Dune::Indices::_0], tail, isFinal); }); }); }); } template constexpr decltype(auto) resolveStaticMultiIndex(C&& c, const MultiIndex& multiIndex) { auto isExhausted = Hybrid::equal_to(Hybrid::size(multiIndex), Dune::Indices::_0); return Hybrid::ifElse(isExhausted, [&, c = forwardCapture(std::forward(c))](auto) -> decltype(auto) { return c.forward(); }, [&](auto id) -> decltype(auto) { auto head = multiIndex[Dune::Indices::_0]; auto tail = multiIndex.pop(); return Imp::resolveStaticMultiIndex(id(c)[head], tail); }); } } // namespace Imp /** * \brief Provide multi-index access by chaining operator[] * * \ingroup Utility * * This provides access to a nested container by given dynamically sized * multi-index. Internally this is resolved by recursive * operator[]-calls with static or dynamic indices. * Because this recursion must be terminated, a predicate * is used to determine if a type is considered final for the * multi-index resolution. Hence multi-index resolution is * terminated for values where the criterion matches. * In order to do this statically the predicate has to * return its result as std::true_type or std::false_type. * * If the entries of the multi-index are exhausted, additional * [0] entries are used until the termination criterion is satisfied. * * \param c Container to access * \param multiIndex Multi-index * \param isFinal A predicate function checking if recursion should be terminated. */ template constexpr decltype(auto) resolveDynamicMultiIndex(C&& c, const MultiIndex& multiIndex, const IsFinal& isFinal) { return Imp::resolveDynamicMultiIndex(std::forward(c), Imp::shiftedDynamicMultiIndex<0>(multiIndex), isFinal); } /** * \brief Provide multi-index access by chaining operator[] * * \ingroup Utility * * This provides access to a nested container by given dynamically sized * multi-index. Internally this is resolved by recursive * operator[]-calls with static or dynamic indices. * Because this recursion must be terminated, a predicate * is used to determine if a type is considered final for the * multi-index resolution. This version terminates the recursion * on values that neither have a static nor a dynamic operator[]. * * \param c Container to access * \param multiIndex Multi-index */ template constexpr decltype(auto) resolveDynamicMultiIndex(C&& c, const MultiIndex& multiIndex) { auto hasNoIndexAccess = negatePredicate(callableCheck([](auto&& cc) -> std::void_t {})); return Imp::resolveDynamicMultiIndex(std::forward(c), Imp::shiftedDynamicMultiIndex<0>(multiIndex), hasNoIndexAccess); } /** * \brief Provide multi-index access by chaining operator[] * * \ingroup Utility * * This provides access to a nested container by given statically sized * multi-index. Internally this is resolved by recursive * operator[]-calls with static or dynamic indices. * Since the multi-index must have compile-time known size * it is possible, that values resolved by different multi-indices * have a different size. * * \param c Container to access * \param multiIndex Multi-index */ template constexpr decltype(auto) resolveStaticMultiIndex(C&& c, const MultiIndex& multiIndex) { return Imp::resolveStaticMultiIndex(std::forward(c), Imp::shiftedStaticMultiIndex<0>(multiIndex)); } } // namespace Dune::Functions } // namespace Dune #endif // DUNE_FUNCTIONS_COMMON_INDEX_ACCESS_HH dune-functions-2.11.0+dfsg/dune/functions/common/interfaces.hh000066400000000000000000000041551513634022200243550ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_INTERFACES_HH #define DUNE_FUNCTIONS_COMMON_INTERFACES_HH #include namespace Dune { namespace Functions { /** * \brief Base class with polymorphic type boiler plate code * * \ingroup Utility * * By deriving your polymorphic type from this class you * enforce a default interface for common operations like * clone, clone to buffer, and move. */ template class PolymorphicType { public: /** \brief Destructor */ virtual ~PolymorphicType() {} /** * \brief Clones the object * * clone() needs to be redefined by an implementation class, with the * return type covariantly adapted. This will return a new copy of *this * via a pointer to newly allocated memory. * Remember to delete the resulting pointer. */ virtual Interface* clone() const = 0; /** * \brief Clones the object into buffer * * clone(buffer) needs to be redefined by an implementation class, * with the return type covariantly adapted. This will return a copy * of *this created in the given buffer using placement-new with copy construction. * You must not delete the returned pointer since it points * to the given buffer (however with the proper type instead of void*). */ virtual Interface* clone(void* buffer) const = 0; /** * \brief Move object into buffer * * move(buffer) needs to be redefined by an implementation class, * with the return type covariantly adapted. This will return a copy * of *this created in the given buffer using placement-new with move construction. * You must not delete the returned pointer since it points * to the given buffer (however with the proper type instead of void*). */ virtual Interface* move(void* buffer) = 0; }; }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_COMMON_EXTENDED_CLONEABLE_HH dune-functions-2.11.0+dfsg/dune/functions/common/localfunction.hh000066400000000000000000000125611513634022200250720ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_LOCAL_FUNCTION_HH #define DUNE_FUNCTIONS_COMMON_LOCAL_FUNCTION_HH #include #include #include #include #include #include #include namespace Dune { namespace Functions { /* * Default implementation is empty * The actual implementation is only given if Signature is an type * describing a function signature as Range(Domain). */ template class DerivativeTraits=DefaultDerivativeTraits, size_t bufferSize=56> class LocalFunction {}; namespace Imp { /// Traits class providing type information for DifferentiableFunction template class DerivativeTraits, size_t bufferSize> struct LocalFunctionTraits : DifferentiableFunctionTraits { protected: using Base=DifferentiableFunctionTraits; public: /// LocalContext type using LocalContext = L; /// Signature of the derivative using DerivativeSignature = typename Base::DerivativeSignature; /// Interface type of the derivative using DerivativeInterface = LocalFunction; /// Internal concept type for type erasure using Concept = LocalFunctionWrapperInterface; /// Internal model template for type erasure template using Model = LocalFunctionWrapperImplementation; }; } /** * \brief Class storing local functions using type erasure * * \ingroup FunctionInterface * * \tparam Range Range type * \tparam Domain Domain type * \tparam LocalContext Type of local context where this function is defined on * \tparam DerivativeTraits Traits class to determine range of derivative. * \tparam bufferSize Size of stack buffer for small object optimization (defaults to 56) * * * This models the \ref Concept::LocalFunction concept. * Objects of this type are returned as local functions * by the GridFunction wrapper. Notice that the DerivativeTraits type * used here should normally be LocalDerivativeTraits where GDE * is the DerivativeTraits type of the corresponding global function. * Small object optimization is used to store the given function. * If its size exceed \p bufferSize, memory will be allocated dynamically. */ template class DerivativeTraits, size_t bufferSize> class LocalFunction< Range(Domain), LocalContext, DerivativeTraits, bufferSize> : public TypeErasureBase< typename Imp::LocalFunctionTraits::Concept, Imp::LocalFunctionTraits::template Model> { using Traits = Imp::LocalFunctionTraits; using Base = TypeErasureBase; using DerivativeInterface = typename Traits::DerivativeInterface; public: /** * \brief Construct from function * * \tparam F Function type * * \param f Function of type F * * Calling derivative(DifferentiableFunction) will result in an exception * if the passed function does provide a free derivative() function * found via ADL. */ template = 0 > LocalFunction(F&& f) : Base(std::forward(f)) { static_assert(Dune::Functions::Concept::isLocalFunction(), "Trying to construct a LocalFunction from type that does not model the LocalFunction concept"); } LocalFunction() = default; /** * \brief Evaluation of wrapped function */ Range operator() (const Domain& x) const { return this->asInterface().operator()(x); } /** * \brief Get derivative of wrapped function * * \ingroup FunctionInterface * * This is free function will be found by ADL. */ friend DerivativeInterface derivative(const LocalFunction& t) { return t.asInterface().derivative(); } /** * \brief Bind function to a local context * * You must bind a LocalFunction to a local * context before you can evaluate it. */ void bind(const LocalContext& context) { this->asInterface().bind(context); } /** * \brief Unbind from local context */ void unbind() { this->asInterface().unbind(); } /** \brief Return if the local function is bound to a grid element */ bool bound() const { return this->asInterface().bound(); } /** * \brief Obtain local context this LocalFunction is bound to */ const LocalContext& localContext() const { return this->asInterface().localContext(); } }; }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_COMMON_LOCAL_FUNCTION_HH dune-functions-2.11.0+dfsg/dune/functions/common/localfunction_imp.hh000066400000000000000000000036111513634022200257330ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_LOCALFUNCTION_FUNCTION_IMP_HH #define DUNE_FUNCTIONS_COMMON_LOCALFUNCTION_FUNCTION_IMP_HH #include #include namespace Dune { namespace Functions { namespace Imp { // Interface of type erasure wrapper // // Notice that the basic interface of polymorphic classes (destructor, clone, ...) // will be added by the type erasure foundation classes. template class LocalFunctionWrapperInterface : public DifferentiableFunctionWrapperInterface { public: virtual void bind(const LocalContext&) = 0; virtual void unbind() = 0; virtual bool bound() const = 0; virtual const LocalContext& localContext() const = 0; }; // Implementation of type erasure wrapper template class LocalFunctionWrapperImplementation : public DifferentiableFunctionWrapperImplementation { using Base = DifferentiableFunctionWrapperImplementation; public: using Base::Base; virtual void bind(const LocalContext& context) { this->get().bind(context); } virtual void unbind() { this->get().unbind(); } virtual bool bound() const { return this->get().bound(); } virtual const LocalContext& localContext() const { return this->get().localContext(); } }; }}} // namespace Dune::Functions::Imp #endif // DUNE_FUNCTIONS_COMMON_DIFFERENTIABLE_FUNCTION_IMP_HH dune-functions-2.11.0+dfsg/dune/functions/common/mapperutilities.hh000066400000000000000000000136611513634022200254540ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_MAPPERUTILITIES_HH #define DUNE_FUNCTIONS_COMMON_MAPPERUTILITIES_HH #include #include #include #include #include namespace Dune::Functions { // All utilities are in Impl:: and thus considered implementation // details for now. However, one may want to think about making // them public. Then they could also be put into dune-grid, // since there's nothing dune-function specific about them. namespace Impl { // Helper class providing an unordered range // of global indices associated to the element // within a MultipleCodimMultipleGeomTypeMapper. // This has to be bound to an element, before // it can be used. template class MapperElementSubIndices { using IndexContainer = std::vector::Index>; public: using GridView = GV; using Mapper = Dune::MultipleCodimMultipleGeomTypeMapper; using Index = typename Mapper::Index; using Element = typename GridView::template Codim<0>::Entity; MapperElementSubIndices(const Mapper& mapper) : mapper_(mapper) {} // Bind to given element and precompute all indices. void bind(const Element& element) { constexpr auto dimension = GridView::dimension; indices_.clear(); auto referenceElement = Dune::referenceElement(element.type()); for (auto codim : Dune::range(dimension + 1)) { for (auto subEntity : Dune::range(referenceElement.size(codim))) { std::size_t c = mapper_.layout()(referenceElement.type(subEntity, codim), dimension); if (c > 0) { std::size_t firstIndex = mapper_.subIndex(element, subEntity, codim); for (auto j : Dune::range(firstIndex, firstIndex + c)) { indices_.push_back(j); } } } } } auto begin() const { return indices_.begin(); } auto end() const { return indices_.end(); } private: const Mapper mapper_; IndexContainer indices_; }; // Helper function computing an average mesh size per subentity // by averaging over the adjacent elements. This only considers // the subentities handled by the given mapper and returns a // std::vector of mesh sizes indexed according to the mapper. // // The average is determined by first computing the average volume // of adjacent elements and then taking the d-th root for a d-dimensional // grid. // // This operation has O(n) runtime (with n=mapper.size()), // allocates O(n) memory for the returned vector and additional // O(n) temporary memory. template auto computeAverageSubEntityMeshSize(const Mapper& mapper) { constexpr auto dimension = Mapper::GridView::dimension; std::vector adjacentElements(mapper.size(), 0); std::vector subEntityMeshSize(mapper.size(), 0.0); auto subIndices = Impl::MapperElementSubIndices(mapper); for(const auto& element : Dune::elements(mapper.gridView())) { auto A = element.geometry().volume(); subIndices.bind(element); for(auto i : subIndices) { subEntityMeshSize[i] += A; ++(adjacentElements[i]); } } for(auto i : Dune::range(mapper.size())) subEntityMeshSize[i] = std::pow(subEntityMeshSize[i]/adjacentElements[i], 1./dimension); return subEntityMeshSize; } /** * \brief Computes a consistent orientation of edges. The orientation is chosen, * such that the tangent of an edge points from the vertex with the lower global Id * to the vertex with the larger global Id. This can agree with the reference orientation or not. * \tparam ElementMapper Mappertype providing the gridView and an index method * \param mapper The mapper to compute the storage order of orientations * \returns A vector with a std::bitset<3> for each element in the grid, order according to mapper. * The bitset contains one bit for each edge, which is O iff the global orientation agrees with the * reference orientation. * \note This only works for a two dimensional, purely simplicial, grid. */ template std::vector> computeEdgeOrientations(ElementMapper mapper) { constexpr int dim = 2; static_assert(dim == ElementMapper::GridView::dimension); auto const& gridView = mapper.gridView(); std::vector> orientations; orientations.resize(gridView.size(0)); // compute orientation for all elements auto const& idSet = gridView.grid().globalIdSet(); for (const auto &element : elements(gridView)) { const auto &refElement = referenceElement(element); auto elementIndex = mapper.index(element); std::bitset<3>& orientation = orientations[elementIndex]; for (std::size_t i = 0; i < element.subEntities(dim - 1); i++) { // Local vertex indices within the element are ordered, localV0 < localV1 auto localV0 = refElement.subEntity(i, dim - 1, 0, dim); auto localV1 = refElement.subEntity(i, dim - 1, 1, dim); // Global vertex indices within the grid auto globalV0 = idSet.subId(element, localV0, dim); auto globalV1 = idSet.subId(element, localV1, dim); // The edge is flipped if the local ordering disagrees with global ordering orientation[i] = globalV0 > globalV1; } } return orientations; } } // end namespace Impl } // end namespace Dune::Functions #endif // end namespace DUNE_FUNCTIONS_COMMON_MAPPERUTILITIES_HH dune-functions-2.11.0+dfsg/dune/functions/common/multiindex.hh000066400000000000000000000043511513634022200244120ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_MULTIINDEX_HH #define DUNE_FUNCTIONS_COMMON_MULTIINDEX_HH #include #include #include #include #include namespace Dune::Functions { /** * \brief A statically sized MultiIndex type * * This is basically a std::array. */ template class StaticMultiIndex : public std::array { public: static constexpr std::size_t size() { return n; } static constexpr std::size_t max_size() { return n; } inline friend std::size_t hash_value(const StaticMultiIndex& v) noexcept { return hash_range(v.begin(), v.end()); } }; /** * \brief A statically sized MultiIndex type * * This is basically a std::array. * * This is the specialization for size==1 which * additionally provides const and mutable casts * to a reference to the single contained digit. */ template class StaticMultiIndex : public std::array { public: static constexpr std::size_t size() { return 1; } static constexpr std::size_t max_size() { return 1; } operator const size_type& () const { return (*this)[0]; } inline friend std::size_t hash_value(const StaticMultiIndex& v) noexcept { return hash_range(v.begin(), v.end()); } operator size_type& () { return (*this)[0]; } }; template inline std::ostream& operator<<(std::ostream& stream, const StaticMultiIndex& c) { for (const auto& ci : c) stream << ci << " "; return stream; } } // namespace Dune::Functions template struct std::tuple_size< Dune::Functions::StaticMultiIndex > : std::integral_constant { }; DUNE_DEFINE_HASH(DUNE_HASH_TEMPLATE_ARGS(typename size_type, std::size_t n),DUNE_HASH_TYPE(Dune::Functions::StaticMultiIndex)) #endif // DUNE_FUNCTIONS_COMMON_MULTIINDEX_HH dune-functions-2.11.0+dfsg/dune/functions/common/overflowarray.hh000066400000000000000000000145701513634022200251360ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_OVERFLOWARRAY_HH #define DUNE_FUNCTIONS_COMMON_OVERFLOWARRAY_HH #include #include #include #include #include #include namespace Dune::Functions { /** * \brief A dynamically sized array-like class with overflow * * \tparam BA Type of base array * \tparam maxSize Maximal size of OverflowArray * * This class publicly inherits from a statically sized array-like * base class BA and extends it by an overflow such that * a total capacity of maxSize is available. Within this bound * the size is managed dynamically. * * Potential usecase: If you want to construct a statically * sized array but need dynamic resizing while building it, * you can use an OverflowArray, ...> * and cast the result to the base class type. * * Similar to Dune::ReservedVector this uses a std::array * internally with the following implications: Entries must be * default-constructible. The whole capacity will always be filled * with entries, even if size> class OverflowArray : public BA { static constexpr std::size_t baseSize = std::tuple_size_v; public: using BaseArray = BA; using value_type = typename BaseArray::value_type; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using difference_type = std::ptrdiff_t; using size_type = std::size_t; using iterator = Dune::GenericIterator; using const_iterator = Dune::GenericIterator; private: using OverflowBuffer = std::array; public: OverflowArray() = default; OverflowArray(const std::initializer_list& l) { assert(l.size() <= capacity()); size_ = l.size(); std::copy_n(l.begin(), size_, begin()); } bool operator == (const OverflowArray& other) const { if (size() != other.size()) return false; for (size_type i=0; i 0); if (! empty()) size_--; } /** * \brief Inserts an element to the begin of the OverflowArray, * * The new size must not exceed max_size(). * This is an O(size()) operation. */ void push_front(const value_type& t) { assert(size() < capacity()); for (size_type i=0; i 0); return (*this)[0]; } //! Returns const reference to first element of OverflowArray. const_reference front() const { assert(size() > 0); return (*this)[0]; } //! Returns reference to last element of OverflowArray. reference back() { assert(size() > 0); return (*this)[size()-1]; } //! Returns const reference to last element of OverflowArray. const_reference back() const { assert(size() > 0); return (*this)[size()-1]; } //! Returns number of elements in the OverflowArray. size_type size () const { return size_; } //! Returns true if OverflowArray has no elements. bool empty() const { return size() == 0; } //! Returns the capacity of the OverflowArray. static constexpr size_type capacity() { return maxSize; } //! Returns the maximum length of the OverflowArray. static constexpr size_type max_size() { return maxSize; } //! Compute hash value inline friend std::size_t hash_value(const OverflowArray& v) noexcept { return hash_range(v.begin(), v.end()); } //! Write container to an output stream friend std::ostream& operator<< (std::ostream& s, const OverflowArray& c) { for (const auto& ci : c) s << ci << " "; return s; } private: OverflowBuffer overflow_; size_type size_ = 0; }; } // namespace Dune::Functions #endif // DUNE_FUNCTIONS_COMMON_OVERFLOWARRAY_HH dune-functions-2.11.0+dfsg/dune/functions/common/polymorphicsmallobject.hh000066400000000000000000000124011513634022200270100ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_POLYMORPHICSMALLOBJECT_HH #define DUNE_FUNCTIONS_COMMON_POLYMORPHICSMALLOBJECT_HH #include #include #include #include namespace Dune { namespace Functions { /** * \brief A wrapper providing small object optimization with polymorphic types * * \ingroup Utility * \ingroup TypeErasure * * \tparam Base Base class type of wrapped objects * \tparam bufferSize Size of small object buffer * * This class encapsulates small object optimization for polymorphic types. * The type of objects passed to the constructor must be derived from the * base class type Base. * * If the size of the derived type fits into the static buffer, then the * wrapped object is stored there, otherwise it is allocated dynamically. * * Notice that this class does implement use type erasure for destructors, * copy/move constructors and copy/move assignment. Hence it requires * that Base has a virtual destructor. * * In order to make the copy constructor work for polymorphic types, * Base must provide virtual methods `Base* clone()` and `Base* clone(void*)`. * The former should return a pointer to a dynamically allocated clone, while * the latter should call the appropriate placement-new with the passed pointer. * * Similarly the polymorphic type has to implement a virtual `Base* move(void*)` * method. * This should call placement-new and can std::move all the * data but leave the object in a valid and probably unusable state. */ template class PolymorphicSmallObject { // Actual buffer size must be > 0 static constexpr std::size_t actualBufferSize = std::max(sizeof(std::byte), bufferSize); // Alignment requirement for the buffer. The `Derived` type must have // an alignment requirement that is a divisor of `bufferAlignment` static constexpr std::size_t bufferAlignment = alignof(std::max_align_t); public: //! Default constructor PolymorphicSmallObject() : p_(nullptr) {} /** * \brief Construct from object * * \tparam Derived Type of object to be stored, must be derived from Base * \param derived Object to be stored */ template>>, int> = 0> PolymorphicSmallObject(Derived&& derived) { constexpr bool useBuffer = (sizeof(Derived) <= bufferSize) && (bufferAlignment % alignof(Derived) == 0); if constexpr (useBuffer) { p_ = new (&buffer_) Derived(std::forward(derived)); } else { p_ = new Derived(std::forward(derived)); } } //! Move constructor from other PolymorphicSmallObject PolymorphicSmallObject(PolymorphicSmallObject&& other) noexcept { moveToWrappedObject(std::move(other)); } //! Copy constructor from other PolymorphicSmallObject PolymorphicSmallObject(const PolymorphicSmallObject& other) { copyToWrappedObject(other); } //! Destructor ~PolymorphicSmallObject() { destroyWrappedObject(); } //! Copy assignment from other PolymorphicSmallObject PolymorphicSmallObject& operator=(const PolymorphicSmallObject& other) { if (&other!=this) { destroyWrappedObject(); copyToWrappedObject(other); } return *this; } //! Move assignment from other PolymorphicSmallObject PolymorphicSmallObject& operator=(PolymorphicSmallObject&& other) noexcept { destroyWrappedObject(); moveToWrappedObject(std::move(other)); return *this; } //! Check if *this is not empty explicit operator bool() const { return p_; } //! Check if object is stored in internal stack buffer bool bufferUsed() const { return ((void*) (p_) == (void*)(&buffer_)); } //! Obtain reference to stored object const Base& get() const { return *p_; } //! Obtain mutable reference to stored object Base& get() { return *p_; } private: void destroyWrappedObject() noexcept { if (operator bool()) { if (bufferUsed()) p_->~Base(); else delete p_; } } void moveToWrappedObject(PolymorphicSmallObject&& other) noexcept { if (other.bufferUsed()) p_ = other.p_->move(&buffer_); else { // We don't need to check for &other_!=this, because you can't // have an rvalue to *this and call it's assignment/constructor // at the same time. (Despite trying to shoot yourself in the foot // with std::move explicitly.) // Take ownership of allocated object p_ = other.p_; // Leave pointer in a clean state to avoid double freeing it. other.p_ = 0; } } void copyToWrappedObject(const PolymorphicSmallObject& other) { if (other.bufferUsed()) p_ = other.p_->clone(&buffer_); else p_ = other.p_->clone(); } alignas(bufferAlignment) std::byte buffer_[actualBufferSize]; Base* p_; }; } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_COMMON_POLYMORPHICSMALLOBJECT_HH dune-functions-2.11.0+dfsg/dune/functions/common/reserveddeque.hh000066400000000000000000000134231513634022200250730ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_RESERVEDDEQUE_HH #define DUNE_FUNCTIONS_COMMON_RESERVEDDEQUE_HH /** \file * \brief An stl-compliant double-ended queue which stores everything on the stack */ #include #include #include #include #include #ifdef CHECK_RESERVEDDEQUE #define CHECKSIZE(X) assert(X) #else #define CHECKSIZE(X) {} #endif namespace Dune { namespace Functions { /** \brief A double-ended queue (deque) class with statically reserved memory \ingroup Utility ReservedDeque is something between std::array and std::deque. You have a double ended queue which can be extended and shrunk using methods like push_back and pop_back at the end, or via push_front and pop_front, but reserved memory is predefined. This implies that the deque cannot grow bigger than the predefined maximum size. \tparam T The data type ReservedDeque stores \tparam n The maximum number of objects the ReservedDeque can store */ template class ReservedDeque { public: /** @{ Typedefs */ //! The type of object, T, stored in the vector. typedef T value_type; //! Pointer to T. typedef T* pointer; //! Reference to T typedef T& reference; //! Const reference to T typedef const T& const_reference; //! An unsigned integral type. typedef size_t size_type; //! A signed integral type. typedef std::ptrdiff_t difference_type; //! Iterator used to iterate through a vector. typedef Dune::GenericIterator iterator; //! Const iterator used to iterate through a vector. typedef Dune::GenericIterator const_iterator; /** @} */ /** @{ Constructors */ //! Constructor ReservedDeque() : size_(0), first_(0) {} ReservedDeque(std::initializer_list const &l) { assert(l.size() <= n);// Actually, this is not needed any more! size_ = l.size(); std::copy_n(l.begin(), size_, data_); } /** @} */ /** @{ Data access operations */ //! Erases all elements. void clear() { first_ = 0; size_ = 0; } //! Specifies a new size for the vector. void resize(size_t s) { CHECKSIZE(s<=n); size_ = s; } //! Appends an element to the end of a vector, up to the maximum size n, O(1) time. void push_back(const T& t) { CHECKSIZE(size_i); return data_[(first_ + i) % n]; } //! Returns a const reference to the i'th element. const_reference operator[] (size_type i) const { CHECKSIZE(size_>i); return data_[(first_ + i) % n]; } //! Returns reference to first element of vector. reference front() { CHECKSIZE(size_>0); return data_[first_]; } //! Returns const reference to first element of vector. const_reference front() const { CHECKSIZE(size_>0); return data_[first_]; } //! Returns reference to last element of vector. reference back() { CHECKSIZE(size_>0); return data_[(first_ + size_-1) % n]; } //! Returns const reference to last element of vector. const_reference back() const { CHECKSIZE(size_>0); return data_[(first_ + size_-1) % n]; } /** @} */ /** @{ Informative Methods */ //! Returns number of elements in the vector. size_type size () const { return size_; } //! Returns true if vector has no elements. bool empty() const { return size_==0; } //! Returns current capacity (allocated memory) of the vector. static constexpr size_type capacity() { return n; } //! Returns the maximum length of the vector. static constexpr size_type max_size() { return n; } /** @} */ //! Send ReservedDeque to an output stream friend std::ostream& operator<< (std::ostream& s, const ReservedDeque& v) { for (size_t i=0; i #include #include namespace Dune { namespace Functions { /** * \brief Helper class to check that F is callable * * \ingroup FunctionUtility */ template struct IsCallable; #ifndef DOXYGEN template struct IsCallable { struct yes { std::size_t dummy[2]; }; struct no { std::size_t dummy[1]; }; template static yes test(const decltype(&C::operator()) *); template static no test(...); enum { value = (sizeof(test(0)) == sizeof(yes)) }; }; template struct IsCallable { enum { value = true }; }; template struct IsCallable { enum { value = true }; }; #endif /** * \brief Helper class to deduce the signature of a callable * * \ingroup FunctionUtility */ template::value > struct SignatureTraits {}; #ifndef DOXYGEN /** \brief deduce the signature of the operator() of a class T */ template struct SignatureTraits : public SignatureTraits {}; /** \brief deduce the signature of an arbitrary const member function of class C */ template struct SignatureTraits : public SignatureTraits {}; /** \brief deduce the signature of an arbitrary member function of class C */ template struct SignatureTraits : public SignatureTraits {}; /** \brief extract domain and range from a free functions pointer */ template struct SignatureTraits : public SignatureTraits {}; /** \brief extract domain and range from a signature (works only for free functions) */ template struct SignatureTraits { using Range = R; using Domain = D; using RawRange = std::decay_t; using RawDomain = std::decay_t; using RawSignature = RawRange(RawDomain); template class DerivativeTraits=DefaultDerivativeTraits> using DerivativeSignature = typename DerivativeTraits::Range(Domain); }; #endif template class DerivativeTraits=DefaultDerivativeTraits> struct SignatureTag; /** * \brief Tag-class to encapsulate signature information * * \ingroup FunctionUtility * * \tparam Range range type * \tparam Domain domain type * \tparam DerivativeTraits traits template used to determine derivative traits */ template class DerivativeTraitsT> struct SignatureTag { using Signature = Range(Domain); template using DerivativeTraits = DerivativeTraitsT; }; /** * \brief Construct SignatureTag for derivative * * \ingroup FunctionUtility * * \param tag SignatureTag for a function * \returns SignatureTags of the derivative */ template class DerivativeTraits> auto derivativeSignatureTag(SignatureTag tag) { using DerivativeRange = typename DerivativeTraits::Range; return SignatureTag(); } /** * \brief Construct SignatureTags for derivatives * * \ingroup FunctionUtility * * \tparam maxOrder Maximal order of derivatives * \param tag SignatureTag for a function * * \returns Tuple of SignatureTags * * This constructs an std::tuple of SignatureTags for * all derivatives of order 0 up to maxOrder. */ template class DerivativeTraits> auto derivativeSignatureTags(Dune::Functions::SignatureTag tag) { if constexpr (maxOrder==0) { // If maxOrder== 0 we just need the given SignatureTag return std::make_tuple(tag); } else { // else we first construct the tail tuple with SignatureTags for derivatives // of order 1 to maxOrder auto tailTagsTuple = derivativeSignatureTags(derivativeSignatureTag(tag)); // and prepend this with the given SignatureTag. // This is done by unpacking the tail tuple with apply(). return std::apply([&](auto&&... tailTags){ return std::make_tuple(tag, tailTags...); }, tailTagsTuple); } } } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_COMMON_SIGNATURE_HH dune-functions-2.11.0+dfsg/dune/functions/common/squeezetensor.hh000066400000000000000000000046361513634022200251520ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_SQUEEZETENSOR_HH #define DUNE_FUNCTIONS_COMMON_SQUEEZETENSOR_HH #include #include namespace Dune::Functions { namespace Impl { /** \brief Remove empty axes from tensors * * This default overload returns the unchanged object. */ template constexpr Object& squeezeTensor(Object& o){ return o; } /** \brief Remove empty axes from tensors * * Default overload for `const` objects. It returns the unchanged object. */ template constexpr Object const& squeezeTensor(Object const& o){ return o; } /** \brief Const overload of \ref squeeze for 1-dimensional vectors * * Overload for vectors; returns a scalar */ template constexpr K const& squeezeTensor(Dune::FieldVector const& v){ return v[0]; } /** \brief Mutable overload of \ref squeeze for 1-dimensional vectors */ template constexpr K& squeezeTensor(Dune::FieldVector& v){ return v[0]; } /** \brief Const overload of \ref squeeze for 1 x N matrices */ template constexpr Dune::FieldVector const& squeezeTensor(Dune::FieldMatrix const& m){ return m[0]; } /** \brief Mutable overload of \ref squeeze for 1 x N-dimensional matrices */ template constexpr Dune::FieldVector& squeezeTensor(Dune::FieldMatrix& m){ return m[0]; } /** \brief Const overload of \ref squeeze for 1,N,M-dimensional tensors */ template constexpr Dune::FieldMatrix const& squeezeTensor(std::array, 1> const& m){ return m[0]; } /** \brief Mutable overload of \ref squeeze for 1,N,M-dimensional tensors */ template constexpr Dune::FieldMatrix& squeezeTensor(std::array, 1>& m){ return m[0]; } } // namespace Impl } // namespace Dune::Functions #endif // DUNE_FUNCTIONS_COMMON_SQUEEZETENSOR_HH dune-functions-2.11.0+dfsg/dune/functions/common/staticforloop.hh000066400000000000000000000034141513634022200251170ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_STATICFORLOOP_HH #define DUNE_FUNCTIONS_COMMON_STATICFORLOOP_HH #include #include #include namespace Dune { namespace Functions { namespace Imp { template struct StaticFindInRange { template static void apply(F&& f, Args&&... args) { if (f(std::integral_constant(), std::forward(args)...)) return; StaticFindInRange::apply(std::forward(f), std::forward(args)...); } }; template struct StaticFindInRange { template static void apply(F&& f, Args&&...) {} }; } //end namespace Imp /** * \brief Static find loop * * \ingroup Utility * * Run static for-loop from 'begin' to 'end-1' with functor. * The functor is called with \p Dune::index_constant\ * as first argument. All other arguments of this method * are forwarded to the functor. If the functor returns * true the loop is terminated. * * \todo Should this be just the StaticForLoop? */ template void staticFindInRange(F&& f, Args&&... args) { Imp::StaticFindInRange::apply(std::forward(f), std::forward(args)...); } } // namespace Dune::Functions } // namespace Dune #endif //DUNE_FUNCTIONS_COMMON_STATICFORLOOP_HH dune-functions-2.11.0+dfsg/dune/functions/common/subdomain.hh000066400000000000000000000513611513634022200242140ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_SUBDOMAIN_HH #define DUNE_FUNCTIONS_COMMON_SUBDOMAIN_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Dune::Functions::Experimental { namespace Impl { template using GlobalIntersectionIteratorTraits = Dune::DefaultIteratorTraits< std::forward_iterator_tag, decltype(*std::declval())>; template class GlobalIntersectionIt : public Dune::IteratorFacadeForTraits, GlobalIntersectionIteratorTraits> { using Facade = Dune::IteratorFacadeForTraits, GlobalIntersectionIteratorTraits>; public: using GridView = GV; using Element = typename GridView::template Codim<0>::Entity; using ElementIterator = typename GridView::template Codim<0>::Iterator; using IntersectionIterator = typename GridView::IntersectionIterator; class SentinelIterator {}; GlobalIntersectionIt(const GridView& gridView, const ContainsCallback& contains, ElementIterator elementIt, ElementIterator elementEnd) : gridView_(gridView) , contains_(contains) , elementIt_(std::move(elementIt)) , elementEnd_(std::move(elementEnd)) { if (elementIt_ != elementEnd_) { element_ = *elementIt_; iIt_ = gridView_.ibegin(element_); iEnd_ = gridView_.iend(element_); if (not contains_(*iIt_)) ++(*this); } } using reference = typename Facade::reference; reference operator*() const { return *iIt_; } GlobalIntersectionIt& operator++() { while(true) { ++iIt_; if (iIt_ == iEnd_) { ++elementIt_; if (elementIt_ == elementEnd_) return *this; element_ = *elementIt_; iIt_ = gridView_.ibegin(element_); iIt_ = gridView_.ibegin(element_); iEnd_ = gridView_.iend(element_); } if (contains_(*iIt_)) return *this; } return *this; } friend bool operator==(const GlobalIntersectionIt& it1, const GlobalIntersectionIt& it2) { if (it1.elementIt_ != it2.elementIt_) return false; if (it1.elementIt_ == it1.elementEnd_) return true; return (it1.iIt_ == it2.iIt_); } friend bool operator==(const GlobalIntersectionIt& it1, const SentinelIterator& it2) { return it1.elementIt_ == it1.elementEnd_; } private: GridView gridView_; ContainsCallback contains_; ElementIterator elementIt_; ElementIterator elementEnd_; Element element_; IntersectionIterator iIt_; IntersectionIterator iEnd_; }; } /** * \brief An IndexSet for a sub-domain * * \ingroup Utility * * A SubDomainIndexSet provides indices for a subset of the * entities of a grid view. Once created, new entities can be * inserted using `subDomainIndexSet.insertElement(element)` * which will insert the grid `element` of codim 0 and all * its sub entities. This will increment the internally stored * size information accordingly. Querying the index of an * entity or sub-entity that has not been inserted before * will lead to an exception. * * Internally this uses a `std::vector` which stores * the sub-domain index for each entity in the underlying grid view * using a magic value for entities not contained in the sub-domain. * * \tparam HGV The underlying host grid view type. */ template class SubDomainIndexSet { using HostGridView = HGV; public: using Grid = typename HostGridView::Grid; using Types = std::vector; using IndexType = std::size_t; //! Codim specific typedefs template struct Codim { using Entity = typename Grid::template Codim::Entity; using EntitySeed = typename Grid::template Codim::EntitySeed; using Geometry = typename Grid::template Codim::Geometry; using LocalGeometry = typename Grid::template Codim::LocalGeometry; }; enum {dimension = Grid::dimension}; private: using AllEntityMapper = Dune::MultipleCodimMultipleGeomTypeMapper; static auto allCodimLayout() { return [](Dune::GeometryType, int) { return true; }; } static constexpr auto typeIndexSize = Dune::GlobalGeometryTypeIndex::size(dimension); static constexpr auto unusesIndex = std::numeric_limits::max(); public: //! Construct SubDomainIndexSet for underlying host grid view SubDomainIndexSet(const HostGridView& hostGridView) : hostGridView_(hostGridView) , allEntityMapper_(hostGridView_, allCodimLayout()) { clear(); } // ********************************* // IndexSet interface methods // ********************************* IndexType size(Dune::GeometryType gt) const { return sizePerGT_[Dune::GlobalGeometryTypeIndex::index(gt)]; } IndexType size(int codim) const { return sizePerCodim_[codim]; } template IndexType index(const Entity& entity) const { auto index = indices_[allEntityMapper_.index(entity)]; if (index==unusesIndex) DUNE_THROW(Dune::InvalidStateException, "Accessing nonexisting entry using SubDomainIndexSet::index()!"); return index; } template IndexType index(const typename Codim::Entity& entity) const { return index::Entity>(entity); } template IndexType subIndex(const Entity& entity, int subEntity, unsigned int codim) const { auto index = indices_[allEntityMapper_.subIndex(entity, subEntity, codim)]; if (index==unusesIndex) DUNE_THROW(Dune::InvalidStateException, "Accessing nonexisting entry using SubDomainIndexSet::subIndex()!"); return index; } template IndexType subIndex(const typename Codim::Entity& entity, int subEntity, unsigned int codim) const { return subIndex::Entity>(entity, subEntity, codim); } template bool contains(const Entity& entity) const { return (indices_[allEntityMapper_.index(entity)] != unusesIndex); } Types types(int codim) const { return typesPerCodim_[codim]; } // ********************************* // Extended methods // ********************************* //! Access underlying host grid view const HostGridView& hostGridView() const { return hostGridView_; } //! Insert element and all its sub-entities into SubDomainIndexSet void insertElement(const typename Codim<0>::Entity& element) { const auto& re = referenceElement(element); for (auto codim : Dune::range(0, dimension+1)) { for (auto subEntity : Dune::range(re.size(codim))) { auto& index = indices_[allEntityMapper_.subIndex(element, subEntity, codim)]; if (index==unusesIndex) { const auto& type = re.type(subEntity, codim); const auto typeIndex = Dune::GlobalGeometryTypeIndex::index(type); index = sizePerGT_[typeIndex]; if (sizePerGT_[typeIndex]==0) typesPerCodim_[codim].push_back(type); sizePerGT_[typeIndex]++; sizePerCodim_[codim]++; } } } } protected: // Clear all data void clear() { for(auto& size : sizePerGT_) size = 0; for(auto& size : sizePerCodim_) size = 0; for(auto& types : typesPerCodim_) types.clear(); indices_.clear(); indices_.resize(allEntityMapper_.size(), unusesIndex); } HostGridView hostGridView_; // Global size information std::array sizePerGT_; std::array sizePerCodim_; std::array typesPerCodim_; AllEntityMapper allEntityMapper_; // Index map std::vector indices_; }; /** * \brief A GridView for a sub-domain * * \ingroup Utility * * A SubDomainGridView provides a reduces GridView interface * for a subset of the entities of a grid view. Objects of * this class store a pointer to a SubDomainIndexSet and * can be copied cheaply. * * \tparam HGV The underlying host grid view type. */ template class SubDomainGridView { template class NonImplementedIterator { public: NonImplementedIterator() { static_assert(codim==0, "SubDomainGridView::Codim::Iterator is only implemented for codim=0"); } }; template class ElementIterator { using Element = typename HGV::template Codim<0>::Entity; public: using HostElementIterator = typename HGV::template Codim<0>::template Partition::Iterator; ElementIterator(const SubDomainIndexSet& indexSet, HostElementIterator&& it, HostElementIterator&& endIt) : indexSet_(&indexSet) , hostIt_(std::move(it)) , hostEndIt_(std::move(endIt)) { while ((hostIt_!= hostEndIt_) and (not indexSet_->contains(*hostIt_))) ++hostIt_; } ElementIterator& operator++() { ++hostIt_; while ((hostIt_!= hostEndIt_) and (not indexSet_->contains(*hostIt_))) ++hostIt_; return *this; } const Element& operator*() const { return *hostIt_; } friend bool operator==(const ElementIterator& a, const ElementIterator& b) { return a.hostIt_==b.hostIt_; } private: HostElementIterator hostIt_; HostElementIterator hostEndIt_; const SubDomainIndexSet* indexSet_; }; public: using HostGridView = HGV; using Grid = typename HostGridView::Grid; using ctype = typename Grid::ctype; using IndexSet = SubDomainIndexSet; using Intersection = typename HostGridView::Intersection; using IntersectionIterator = typename HostGridView::IntersectionIterator; //! Codim specific typedefs template struct Codim { using Entity = typename Grid::template Codim::Entity; using EntitySeed = typename Grid::template Codim::EntitySeed; using Geometry = typename Grid::template Codim::Geometry; using LocalGeometry = typename Grid::template Codim::LocalGeometry; using Iterator = std::conditional_t, NonImplementedIterator>; template struct Partition { using Iterator = std::conditional_t, NonImplementedIterator>; }; }; enum {dimension = Grid::dimension}; enum {dimensionworld = Grid::dimensionworld}; SubDomainGridView(const IndexSet& indexSet) : indexSet_(&indexSet) {} SubDomainGridView(const SubDomainGridView& other) = default; const Grid& grid() const { return indexSet_->hostGridView().grid(); } const IndexSet& indexSet() const { return *indexSet_; } int size(int codim) const { return indexSet_->size(codim); } int size(Dune::GeometryType gt) const { return indexSet_->size(gt); } template bool contains(const Entity& entity) const { return indexSet_->contains(entity); } //! Create an iterator pointing to the begin of the range. template typename Codim::template Partition::Iterator begin() const { static_assert(codim==0, "SubDomainGridView::begin is only implemented for codim=0"); return {indexSet(), hostGridView().template begin(), hostGridView().template end()}; } //! Create an iterator pointing to the end of the range. template typename Codim::template Partition::Iterator end() const { static_assert(codim==0, "SubDomainGridView::end is only implemented for codim=0"); return {indexSet(), hostGridView().template end(), hostGridView().template end()}; } decltype(auto) comm() const { return hostGridView().comm(); } decltype(auto) ibegin(const typename Codim<0>::Entity& element) const { return hostGridView().ibegin(element); } decltype(auto) iend(const typename Codim<0>::Entity& element) const { return hostGridView().iend(element); } //! Access underlying host grid view const HostGridView& hostGridView() const { return indexSet_->hostGridView(); } protected: const IndexSet* indexSet_; }; /** * \brief ADL findable access to element range for a SubDomainGridView * * \ingroup Utility */ template auto elements(const SubDomainGridView& subDomainGridView) { return Dune::IteratorRange(subDomainGridView.template begin<0>(), subDomainGridView.template end<0>()); } /** * \brief ADL findable access to element range for a SubDomainGridView * * \ingroup Utility */ template auto elements(const SubDomainGridView& subDomainGridView, Dune::PartitionSet partitionSet) { constexpr auto pit = partitionSet.partitionIterator(); return Dune::IteratorRange(subDomainGridView.template begin<0, pit>(), subDomainGridView.template end<0, pit>()); } /** * \brief ADL findable access to intersection range for an element of a SubDomainGridView * * \ingroup Utility */ template auto intersections(const SubDomainGridView& subDomainGridView, const Element& element) { return Dune::IteratorRange(subDomainGridView.ibegin(element), subDomainGridView.iend(element)); } /** * \brief Class representing a sub-domain of a GridView * * \ingroup Utility * * A SubDomain is a subset of grid elements from a given * underlying grid view together with their sub-entities. * It allows to create a `SubDomainGridView` which implements * a reasonable subset of the grid view interface defined * in dune-grid. In particular the `SubDomainGridView` * implements an index set, a `contains()` methods, an * element iterator and intersection iterators. * * \tparam HGV The underlying host grid view type. */ template class SubDomain { public: using HostGridView = HGV; using Grid = typename HostGridView::Grid; using IndexSet = SubDomainIndexSet; using GridView = SubDomainGridView; //! Codim specific typedefs template struct Codim { using Entity = typename Grid::template Codim::Entity; using EntitySeed = typename Grid::template Codim::EntitySeed; using Geometry = typename Grid::template Codim::Geometry; using LocalGeometry = typename Grid::template Codim::LocalGeometry; }; enum {dimension = Grid::dimension}; //! Construct SubDomain for underlying host grid view SubDomain(const HostGridView& hostGridView) : indexSet_(hostGridView) {} const IndexSet& indexSet() const { return indexSet_; } //! Create grid view representing the SubDomain GridView gridView() const { return GridView(indexSet_); } //! Access underlying host grid view HostGridView hostGridView() const { return indexSet_.hostGridView(); } //! Insert element and all its sub-entities into SubDomain void insertElement(const typename Codim<0>::Entity& element) { indexSet_.insertElement(element); } //! Check if element is contained in SubDomain bool contains(const typename Codim<0>::Entity& element) const { return indexSet_.contains(element); } private: IndexSet indexSet_; }; /** * \brief Class representing the intersection between two subdomains * * Conceptually this represents a range of intersections. */ template class SubDomainInterface { static_assert( std::is_same_v, "SubDomainInterface requires that both SubDomain types have the same Intersection type"); public: using Intersection = typename SubDomainA::GridView::Intersection; /** * \brief Create interface between two subdomains * * Notice that the order of the passed subdomains does matter, * because the intersections visited by the iterator will * always be oriented such that A is inside and B outside. */ SubDomainInterface(const SubDomainA& subDomainA, const SubDomainB& subDomainB) : subDomainA_(subDomainA) , subDomainB_(subDomainB) {} /** * \brief Check if intersection is contained in the interface between the subdomains * * This evaluates to true for all intersections having element from subdomain A and B * as inside and outside or outside and inside, respectively. * * Notice that this also evaluates to true for intersection with B as inside and * A as outside, despite the fact that these are not visited by the iterator. */ bool contains(const Intersection& is) const { if (is.boundary() or not(is.neighbor())) return false; return (subDomainA_.contains(is.inside()) && subDomainB_.contains(is.outside())) || (subDomainA_.contains(is.outside()) && subDomainB_.contains(is.inside())); } /** * \brief Check if intersection is oriented * * The intersection is called oriented if its inside and outside * element are contained in subDomainA and subDomainB, respectively. * The result of this method is undefined if the intersection is not * contained in the intersection. */ bool isOriented(const Intersection& is) const { if (is.boundary() or not(is.neighbor())) return false; return (subDomainA_.contains(is.inside()) && subDomainB_.contains(is.outside())); } /** * \brief Begin iterator over all intersection between the subdomains * * The iterator will always have the elements from sub domain A and B * as inside and outside, respectively. */ const auto begin() const { return Impl::GlobalIntersectionIt(subDomainA_.gridView(), [&](const auto& is) { if (is.boundary() or not(is.neighbor())) return false; return subDomainB_.indexSet().contains(is.outside()); }, subDomainA_.gridView().template begin<0>(), subDomainA_.gridView().template end<0>()); } //! End iterator (sentinel) const auto end() const { return typename decltype(begin())::SentinelIterator(); } private: const SubDomainA& subDomainA_; const SubDomainB& subDomainB_; }; /** * \brief Class representing the skeleton of a subdomain * * Conceptually this represents a range of intersections. */ template class SubDomainSkeleton { public: using Intersection = typename SubDomain::GridView::Intersection; //! Create skeleton of a subdomain SubDomainSkeleton(const SubDomain& subDomain) : subDomain_(subDomain) {} //! Check if intersection is contained in the skeleton of the subdomain bool contains(const Intersection& is) const { if (is.boundary() or not(is.neighbor())) return false; return subDomain_.contains(is.inside()) and subDomain_.contains(is.outside()); } private: const SubDomain& subDomain_; }; } // namespace Dune::Functions::Experimental #endif// DUNE_FUNCTIONS_COMMON_SUBDOMAIN_HH dune-functions-2.11.0+dfsg/dune/functions/common/test/000077500000000000000000000000001513634022200226635ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/common/test/.gitignore000066400000000000000000000003651513634022200246570ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later *.log *.trs differentiablefunctiontest functionfromcallabletest dune-functions-2.11.0+dfsg/dune/functions/common/test/CMakeLists.txt000066400000000000000000000006101513634022200254200ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # tests that should build and run successfully link_libraries(Dune::Functions) dune_add_test(SOURCES differentiablefunctiontest.cc LABELS quick) dune_add_test(SOURCES polymorphicsmallobjecttest.cc LABELS quick) dune-functions-2.11.0+dfsg/dune/functions/common/test/differentiablefunctiontest.cc000066400000000000000000000215141513634022200306060ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include //#include "derivativecheck.hh" bool checkTrue(bool value, std::string error) { if (not(value)) std::cout << "TEST FAILURE:" << error << std::endl; return value; } // Check if interface compiles and is implementable by a simple dummy struct DifferentiableFunctionImplementableTest { template static bool checkWithFunction(F&& f) { bool passed = true; using Dune::Functions::Concept::isFunction; using Dune::Functions::Concept::isDifferentiableFunction; using Dune::Functions::SignatureTag; { std::cout << "--------------" << std::endl; // passed = passed and DerivativeCheck::checkAllImplementedTrulyDerived(testFunction, 10); // Test whether I can evaluate the function somewhere std::cout << "Function value at x=5: " << f(5) << std::endl; passed = checkTrue(isDifferentiableFunction(f, SignatureTag()), "Raw function f does not satisfy DifferentiableFunction concept"); std::cout << std::endl << "Check calling derivatives through function object" << std::endl; // Test whether I can evaluate the first derivative auto df = derivative(f); passed = checkTrue(isDifferentiableFunction(df, SignatureTag()), "Raw function df does not satisfy DifferentiableFunction concept"); std::cout << "Derivative at x=5: " << df(5) << std::endl; // Test whether I can evaluate the second derivative through FunctionHandle auto ddf = derivative(df); passed = checkTrue(isDifferentiableFunction(ddf, SignatureTag()), "Raw function ddf does not satisfy DifferentiableFunction concept"); std::cout << "Second derivative at x=5: " << ddf(5) << std::endl; // Test whether I can evaluate the third derivative through FunctionHandle auto dddf = derivative(ddf); passed = checkTrue(isFunction(dddf, SignatureTag()), "Raw function dddf does not satisfy Function concept"); std::cout << "Third derivative at x=5: " << dddf(5) << std::endl; std::cout << std::endl << "Check calling derivatives through DifferentiableFunction object" << std::endl; Dune::Functions::DifferentiableFunction fi = f; passed = checkTrue(isDifferentiableFunction(fi, SignatureTag()), "Wrapped function fi does not satisfy DifferentiableFunction concept"); // Try to reassign wrapper fi = f; // Try assigning a default constructed wrapper Dune::Functions::DifferentiableFunction fii; passed = checkTrue(isDifferentiableFunction(fii, SignatureTag()), "Wrapped function fii does not satisfy DifferentiableFunction concept"); fii = fi; // Try to copy wrapper auto fiii = fii; passed = checkTrue(isDifferentiableFunction(fiii, SignatureTag()), "Wrapped function fiii does not satisfy DifferentiableFunction concept"); std::cout << "Function value at x=5: " << fiii(5) << std::endl; // Test whether I can evaluate the first derivative auto dfiii = derivative(fiii); passed = checkTrue(isDifferentiableFunction(dfiii, SignatureTag()), "Wrapped function dfiii does not satisfy DifferentiableFunction concept"); std::cout << "Derivative at x=5: " << dfiii(5) << std::endl; // Test whether I can evaluate the second derivative through FunctionHandle auto ddfiii = derivative(dfiii); passed = checkTrue(isDifferentiableFunction(ddfiii, SignatureTag()), "Wrapped function ddfiii does not satisfy DifferentiableFunction concept"); std::cout << "Second derivative at x=5: " << ddfiii(5) << std::endl; // Test whether I can evaluate the third derivative through FunctionHandle auto dddfiii = derivative(ddfiii); passed = checkTrue(isFunction(dddfiii, SignatureTag()), "Wrapped function dddfiii does not satisfy Function concept"); std::cout << "Third derivative at x=5: " << dddfiii(5) << std::endl; // Wrap as non-differentiable function auto g = [=] (const double& x) {return f(x);}; passed = checkTrue(isFunction(g, SignatureTag()), "Lambda function g does not satisfy Function concept"); Dune::Functions::DifferentiableFunction gg = [=] (const double& x) {return f(x);}; passed = checkTrue(isFunction(gg, SignatureTag()), "Wrapped function gg does not satisfy DifferentiableFunction concept"); std::cout << "Function value at x=5: " << gg(5) << std::endl; try { auto dg = derivative(gg); passed = false; } catch (Dune::NotImplemented& e) { std::cout << "Obtaining derivative from nondifferentiable function failed expectedly" << std::endl; passed = checkTrue(not(isDifferentiableFunction(g, SignatureTag())), "But unwrapped function g does satisfy DifferentiableFunction concept"); } } return passed; } static bool check() { bool passed = true; passed = passed and checkWithFunction(Dune::Functions::Polynomial({1, 2, 3})); passed = passed and checkWithFunction(Dune::Functions::TrigonometricFunction()); auto f = [](double x){ return std::sin(x);}; auto df = [](double x){ return std::cos(x);}; auto ddf = [](double x){ return -std::sin(x);}; auto dddf = [](double x){ return -std::cos(x);}; auto F = makeDifferentiableFunctionFromCallables(Dune::Functions::SignatureTag(), f, df, ddf, dddf); passed = passed and checkWithFunction(F); return passed; } }; #if 0 // Check if recursive DerivativeRange definition terminates // after at most maxRecursionLevel=k derivatives, i.e. if // the type of the k-th and (k+1)-nd derivative is the same. template struct DerivativeRangeTerminationTest { template struct TerminationTest { static int level() { std::cout << "Type of " << recursionLevel << "-th derivative is " << Dune::className() << std::endl; typedef typename Dune::Functions::DerivativeTraits::DerivativeRange DDR; int upperBound = TerminationTest::level(); if (std::is_same_v) return recursionLevel; else return upperBound; } }; template struct TerminationTest { static int level() { std::cout << "Type of " << maxRecursionLevel << "-th derivative is " << Dune::className() << std::endl; typedef typename Dune::Functions::DerivativeTraits::DerivativeRange DDR; if (std::is_same_v) return maxRecursionLevel; else return maxRecursionLevel+1; } }; static bool check() { std::cout << "Checking recursion for Domain=" << Dune::className() << " and Range=" << Dune::className() << std::endl; int terminationLevel = TerminationTest::level(); if (terminationLevel <= maxRecursionLevel) { std::cout << "Recursion terminated after " << terminationLevel << "-th derivative" << std::endl; return true; } else { std::cout << "Recursion did not terminated after given maxRecursionLevel " << maxRecursionLevel; return false; } } }; #endif int main ( int argc, char **argv ) try { Dune::MPIHelper::instance(argc, argv); bool passed = true; passed = passed and DifferentiableFunctionImplementableTest::check(); #if 0 passed = passed and DerivativeRangeTerminationTest::check(); passed = passed and DerivativeRangeTerminationTest , Dune::FieldVector, 5 >::check(); passed = passed and DerivativeRangeTerminationTest , Dune::FieldVector, 5 >::check(); #endif if (passed) std::cout << "All tests passed" << std::endl; return passed ? 0: 1; } catch( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/common/test/polymorphicsmallobjecttest.cc000066400000000000000000000063361513634022200306670ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include class Base { public: virtual ~Base() = default; virtual Base* clone() = 0; virtual Base* clone(void*) = 0; virtual Base* move(void*) = 0; virtual int foo() = 0; virtual bool checkMemberAlignment() = 0; }; class Derived : public virtual Base { long double dummy_; int i_; public: virtual ~Derived() = default; Derived(int i = 0) : dummy_(0), i_(i) {} Base* clone() final { return new Derived{i_}; } Base* clone(void* ptr) final { return new (ptr) Derived{this->i_}; } Base* move(void* ptr) final { return new (ptr) Derived{std::move(this->i_)}; } int foo() final { return i_; } bool checkMemberAlignment() final { return (reinterpret_cast(&dummy_) % alignof(long double)) == 0; } }; bool checkTrue(bool value, std::string error) { if (not(value)) std::cout << "TEST FAILURE:" << error << std::endl; return value; } template bool test() { bool success = true; static_assert(std::is_nothrow_move_constructible_v); static_assert(std::is_nothrow_move_assignable_v); std::array v{Derived{2}, Derived{2}}; success &= checkTrue(v[0].get().checkMemberAlignment(), "Invalid alignment of member!"); success &= checkTrue(v[1].get().checkMemberAlignment(), "Invalid alignment of member!"); Obj obj(Derived{2}); success &= checkTrue(obj.get().foo() == 2, "Invalid state using constructor with Derived argument!"); Obj objCopy(obj); success &= checkTrue(objCopy.get().foo() == 2, "Invalid state using copy constructor!"); Obj objMove(std::move(objCopy)); success &= checkTrue(objMove.get().foo() == 2, "Invalid state using move constructor!"); Obj objCopyAssign = obj; success &= checkTrue(objCopyAssign.get().foo() == 2, "Invalid state using copy assignment!"); Obj objMoveAssign = std::move(objCopyAssign); success &= checkTrue(objMoveAssign.get().foo() == 2, "Invalid state using move assignment!"); obj = obj; success &= checkTrue(obj.get().foo() == 2, "Invalid state using self assignment!"); return success; } int main ( int argc, char **argv ) try { Dune::MPIHelper::instance(argc, argv); bool passed = true; std::cout << "Testing PolymorphicSmallObject with no buffer" << std::endl; passed &= test>(); std::cout << "Testing PolymorphicSmallObject with buffer" << std::endl; constexpr std::size_t OBJSIZE = sizeof(Derived); passed &= test>(); if (passed) std::cout << "All tests passed" << std::endl; return passed ? 0: 1; } catch( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/common/type_traits.hh000066400000000000000000000040121513634022200245710ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_TYPE_TRAITS_HH #define DUNE_FUNCTIONS_COMMON_TYPE_TRAITS_HH #include #include #include namespace Dune { namespace Functions { /** * \brief Helper to constrain forwarding constructors * * \ingroup Utility * * Helper typedef to remove constructor with forwarding reference from * overload set for type is not constructible from argument list. * This is useful to avoid failing forwarding constructors * for base classes or members. */ template using enableIfConstructible = std::enable_if_t< std::is_constructible_v, int>; /** * \brief Check if type is a statically sized container * * \ingroup Utility * * Derives from std::true_type or std::false_type */ template struct HasStaticSize : public IsIntegralConstant()))> {}; //! A variable template representing the value of \ref HasStaticSize template inline constexpr bool HasStaticSize_v = HasStaticSize::value; /** * \brief Obtain size of statically sized container, or 0 if dynamic size * * \ingroup Utility * * Derives from std::integral_constant */ template struct StaticSizeOrZero : public std::conditional_t, decltype(Dune::Hybrid::size(std::declval())), std::integral_constant> {}; /** * \brief Obtain size of statically sized container as integral_constant, or fail. * \ingroup Utility */ template using StaticSize = std::enable_if_t, decltype(Dune::Hybrid::size(std::declval()))>; }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_COMMON_TYPE_TRAITS_HH dune-functions-2.11.0+dfsg/dune/functions/common/typeerasure.hh000066400000000000000000000132661513634022200246050ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_TYPEERASURE_HH #define DUNE_FUNCTIONS_COMMON_TYPEERASURE_HH #include #include #include #include #include namespace Dune { namespace Functions { namespace Imp { /** * \brief The internal wrapper interface for type erasure * * \ingroup TypeErasure * * This class adds some foundation interfaces needed * for proper dynamic polymorphism and type erasure. * * The actual application interface has to be provided * by the user. * * \tparam Interface Class defininig the internal abstract virtual interface */ template class TypeErasureWrapperInterface : public Interface, public PolymorphicType> { public: virtual const std::type_info& target_type() const = 0; }; /** * \brief Base implementation of the internal wrapper interface * * \ingroup TypeErasure * * This class derives from the foundation interfaces * and the user defined interfaces provided by the interface * parameter. It will store any suitable type T to do * the type erasure. * * The implementation of the foundation and user interface * is provided by classed derived of this one. * * \tparam Interface Class defininig the internal abstract virtual interface * \tparam T A type modelleding the desired interface */ template class TypeErasureWrapperBase : public TypeErasureWrapperInterface { public: template = 0> TypeErasureWrapperBase(TT&& t) : wrapped_(std::forward(t)) {} //! Get mutable reference stored object T& get() { return wrapped_; } //! Get reference stored object const T& get() const { return wrapped_; } protected: using Wrapped = T; Wrapped wrapped_; }; /** * \brief Implementation of the internal wrapper interface * * \ingroup TypeErasure * * This class implements the foundation and user interfaces * of the internal type erasure wrapper. * * The foundation interface of TypeErasureWrapperInterface is directly * implemented here whereas the user interface is implemented * by deriving from the user-provides Implementation template. * * The Implementation is a template taking one class template * parameter. It should directly or indirectly derive from this * class and inherit its constructors. * In order to forward the implemented methods to the erased * type it can use the wrapper_ member of this base class being * of this type. * * \tparam Interface Class defining the internal abstract virtual interface * \tparam Implementation Class defining the implementation of the abstract methods of Interface * \tparam T A type modeling the desired interface */ template class Implementation, class T> class TypeErasureWrapperImplementation : public Implementation > { public: //! Construct wrapper from object template = 0> TypeErasureWrapperImplementation(TT&& t) : Implementation >(std::forward(t)) {} //! Implementation of PolymorphicType::clone() virtual TypeErasureWrapperImplementation* clone() const { return new TypeErasureWrapperImplementation(*this); } //! Implementation of PolymorphicType::clone(void* buffer) virtual TypeErasureWrapperImplementation* clone(void* buffer) const { return new (buffer) TypeErasureWrapperImplementation(*this); } //! Implementation of PolymorphicType::move(void* buffer) virtual TypeErasureWrapperImplementation* move(void* buffer) { return new (buffer) TypeErasureWrapperImplementation(std::move(*this)); } //! Get type of stored object virtual const std::type_info& target_type() const { return typeid(T); } }; } // namespace Dune::Functions::Imp /** * \brief Base class for type-erased interface wrapper * * \ingroup TypeErasure * * This is meant as a base class for the type-erased interface * wrapper that is actually visible to the user. By deriving * from this you get small object optimization for the internal * polymorphic wrapper. */ template class Implementation, size_t bufferSize = 56> class TypeErasureBase { public: //! Construct wrapper from object template = 0 > TypeErasureBase(T&& t) : wrapped_(Imp::TypeErasureWrapperImplementation>(std::forward(t))) {} //! Default constructor TypeErasureBase() = default; //! Get mutable reference to wrapped object Interface& asInterface() { return wrapped_.get(); } //! Get reference to wrapped object const Interface& asInterface() const { return wrapped_.get(); } //! Query whether there is a stored object explicit operator bool() const noexcept { // Forward the query to the wrapped object return static_cast(wrapped_); } //! Get type of stored object const std::type_info& target_type() const { return wrapped_.get().target_type(); } protected: PolymorphicSmallObject, bufferSize > wrapped_; }; }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_COMMON_TYPEERASURE_HH dune-functions-2.11.0+dfsg/dune/functions/common/utility.hh000066400000000000000000000262151513634022200237360ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_COMMON_UTILITY_HH #define DUNE_FUNCTIONS_COMMON_UTILITY_HH #include #include #include #include #include namespace Dune { namespace Functions { template [[deprecated("This function will be removed after Dune 2.11")]] auto forwardAsStaticInteger(std::integer_sequence values, const size_type& i, F&& f, Args&&... args) ->decltype(f(std::integral_constant(), std::forward(args)...)) { return f(std::integral_constant(), std::forward(args)...); } template [[deprecated("This function will be removed after Dune 2.11")]] auto forwardAsStaticInteger(std::integer_sequence values, const size_type i, F&& f, Args&&... args) ->decltype(f(std::integral_constant(), std::forward(args)...)) { if (i==firstValue) return f(std::integral_constant(), std::forward(args)...); return forwardAsStaticInteger(std::integer_sequence(), i, std::forward(f), std::forward(args)...); } /** * \brief Transform dynamic index to static index_constant * * \ingroup Utility * * This will call the given function with index_constant\ * where i is the dynamically provided index. * * To achieve this the condition i==ii is checked subsequently * for al static indices ii in the range 0,...,(end-1). In order * to be able to compile this we require for all ii in this range * that f(index_constant()) is well-formed and that the result * type of it can be converted to the result type of f(index_constant<0>()). * If i is not in this range, the returned value is f(index_constant()) * * \param i Dynamic index * \param f Function to call (e.g., a generic lambda) * \param args Additional arguments for f * * \returns f(index_constant\(), args...) * * \deprecated This will be removed in 2.11. use Dune::Hybrid::switchCases instead. */ template [[deprecated("This function will be removed after Dune 2.11, use Dune::Hybrid::switchCases.")]] auto forwardAsStaticIndex(const size_type& i, F&& f, Args&&... args) ->decltype(f(Dune::Indices::_0, std::forward(args)...)) { return forwardAsStaticInteger(std::make_index_sequence{}, i, std::forward(f), std::forward(args)...); } namespace Imp { template class T, class List> struct ExpandTupleHelper {}; template class T, template class ListType, class... Args> struct ExpandTupleHelper> { using Type = T; }; } // end namespace Imp /** * \brief Expand tuple arguments as template arguments * * \ingroup Utility * * This template alias refers to T if * ArgTuple is a std::tuple. * * \tparam T A variadic template * \tparam ArgTuple A tuple of types */ template class T, class ArgTuple> using ExpandTuple = typename Imp::ExpandTupleHelper::Type; namespace Imp { template class T, class... Tuple> struct TransformTupleHelper {}; template class T, class... Args1> struct TransformTupleHelper> { using Type = std::tuple...>; }; template class T, class... Args1, class... Args2> struct TransformTupleHelper, typename std::tuple> { using Type = std::tuple...>; }; } // end namespace Imp /** * \brief Transform tuple types argument using type-functor * * \ingroup Utility * * This is a template alias for a tuple whose i-th type * is given by F where T1i,...,TMi are the * i-th types of the 1,...,M-th tuple of the given tuple * list Tuples. Currently only M=1,2 are supported. * \tparam F A template alias mapping 1,...,sizeof...(ArgTuple) types to a new one * \tparam Tuples A list of tuples */ template class F, class... Tuples> using TransformTuple = typename Imp::TransformTupleHelper::Type; namespace Imp { template auto transformTupleHelper(F&& f, const std::tuple& tuple, std::index_sequence) -> decltype(std::make_tuple(f(std::get(tuple))...)) { return std::make_tuple(f(std::get(tuple))...); } template auto transformTupleHelper(F&& f, const std::tuple& tuple1, const std::tuple& tuple2, std::index_sequence) -> decltype(std::make_tuple(f(std::get(tuple1), std::get(tuple2))...)) { return std::make_tuple(f(std::get(tuple1), std::get(tuple2))...); } } // end namespace Imp /** * \brief Transform tuple value using a functor * * \ingroup Utility * * This will apply the given functor to all values in * given tuple and return the results in a new tuple. * * \param f A functor defined for all tuple entries * \param tuple The tuple to transform */ template auto transformTuple(F&& f, const std::tuple& tuple) -> decltype(Imp::transformTupleHelper(std::forward(f), tuple, std::index_sequence_for{})) { return Imp::transformTupleHelper(std::forward(f), tuple, std::index_sequence_for{}); } /** * \brief Transform tuple value using a binary functor * * \ingroup Utility * * This will apply the given functor to the each corresponding * pair of values in the given tuples and return the results * in a new tuple. * * \param f A functor defined for all tuple entries * \param tuple1 The tuple containing values for the first parameter * \param tuple2 The tuple containing values for the second parameter */ template auto transformTuple(F&& f, const std::tuple& tuple1, const std::tuple& tuple2) -> decltype(Imp::transformTupleHelper(std::forward(f), tuple1, tuple2, std::index_sequence_for{})) { return Imp::transformTupleHelper(std::forward(f), tuple1, tuple2, std::index_sequence_for{}); } namespace Imp { template struct IntegerSequenceTupleHelper {}; template struct IntegerSequenceTupleHelper> { using Type = std::tuple...>; }; } // end namespace Imp /** * \brief Transform integer_sequence to tuple...> */ template using IntegerSequenceTuple= typename Imp::IntegerSequenceTupleHelper::Type; /** * \brief Get last entry of type list * * \ingroup Utility */ template struct LastType { using type = std::tuple_element_t>; }; namespace Imp { template struct RotateHelper; template struct RotateHelper, std::index_sequence > { using type = typename std::tuple::type, std::tuple_element_t>...>; }; } // end namespace Imp /** * \brief Rotate type list by one, such that last entry is moved to first position * * \ingroup Utility * * The rotated type list is exported as tuple */ template struct RotateTuple { using type = typename Imp::RotateHelper, std::make_index_sequence>::type; }; /** * \brief Create a predicate for checking validity of expressions * * \param f A function involving the expression to check. * * This returns a function object that allows to check if the * expression encoded in f is valid for the given arguments. * To be precise it checks if f can be called using the given arguments. * This can be used in the following way: To generate a check if the * expression x(a,b) is valid for some a and b use: * \code{.cpp} auto xIsValid = callableCheck([](auto&& a, auto&& b) -> std::void_t {}); if (xIsValid(a,b)) ... \endcode * * Notice that the given function f is stored by value. * * \ingroup Utility */ template auto callableCheck(Expression f) { return [f](auto&&... args){ return Functions::Concept::isCallable(f, std::forward(args)...); }; } /** * \brief Negate given predicate * * \param check A predicate function to negate * * This returns a function having the same parameters as * f, but negating the result. Negation here means that * std::true_type is converted to std::false_type are * vice verse, while other return values are converted to * bool values and then the negated value is returned as bool, too. * * Notice that the given function f is stored by value. * * \ingroup Utility */ template auto negatePredicate(Check check) { return [check](auto&&... args){ auto negate = overload( [](std::true_type) { return std::false_type{};}, [](std::false_type) { return std::true_type{};}, [](bool v) { return not v;}); return negate(check(std::forward(args)...)); }; } namespace Impl { // Wrapper to capture values in a lambda for perfect forwarding. // This captures value types by value and reference types by reference. template struct ForwardCaptureWrapper; template struct ForwardCaptureWrapper { template ForwardCaptureWrapper(TT&& t) : t_{std::forward(t)} {} auto forward() const { return std::move(t_); } T t_; }; template struct ForwardCaptureWrapper { ForwardCaptureWrapper(T& t) : t_{t} {} T& forward() const { return t_; }; T& t_; }; template struct ForwardCaptureWrapper { ForwardCaptureWrapper(const T& t) : t_{t} {} const T& forward() const { return t_; }; const T& t_; }; } // end namespace Dune::Functions::Impl /** * \brief Create a capture object for perfect forwarding. * * The returned object will capture the passed argument t. * If t is passed as r-value, then it is captured by value, * otherwise by reference. The captured value is accessible * once using the forward() method which either returns the * catured reference or moves the captured value. * * This allows to capture values for perfect forwarding * in lambda functions using * [t=forwardCapture(std::forward(t))]() -> decltype(auto) { return t.forward(); } */ template auto forwardCapture(T&& t) { return Impl::ForwardCaptureWrapper(std::forward(t)); } } // namespace Dune::Functions } // namespace Dune #endif // DUNE_FUNCTIONS_COMMON_UTILITY_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/000077500000000000000000000000001513634022200242735ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/CMakeLists.txt000066400000000000000000000026301513634022200270340ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory("test") install(FILES basistags.hh boundarydofs.hh brezzidouglasmarinibasis.hh bsplinebasis.hh compositebasis.hh concepts.hh containerdescriptors.hh cubichermitebasis.hh defaultglobalbasis.hh defaultlocalview.hh dynamicpowerbasis.hh flatmultiindex.hh flatvectorview.hh functionaldescriptor.hh globalvaluedlocalfiniteelement.hh hierarchicallagrangebasis.hh hierarchicallagrangewithelementbubblebasis.hh hierarchicnodetorangemap.hh interpolate.hh lagrangebasis.hh lagrangedgbasis.hh leafprebasismappermixin.hh leafprebasismixin.hh lfeprebasismixin.hh morleybasis.hh nedelecbasis.hh periodicbasis.hh powerbasis.hh rannacherturekbasis.hh raviartthomasbasis.hh refinedlagrangebasis.hh restrictedbasis.hh nodes.hh subentitydofs.hh subspacebasis.hh subspacelocalview.hh taylorhoodbasis.hh transformedfiniteelementmixin.hh transformedindexbasis.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/functions/functionspacebases) dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/argyrisbasis.hh000066400000000000000000001057141513634022200273260ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_ARGYRISBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_ARGYRISBASIS_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * \file Argyrisbasis.hh * \brief This file provides an implementation of the cubic Argyris finite element. * * For reference, see[Ciarlet, The Finite Element Method for Elliptic Problems, 2002]. * It contains in the following order: * - A GlobalBasis typedef ArgyrisBasis * - A template ArgyrisLocalFiniteElement providing an implementation * of the LocalFiniteElement interface, along with its subparts (Impl namespace) * - A template ArgyrisNode * - A template ArgyrisPreBasis * - A Factory argyris() in the BasisFactory namespace */ namespace Dune::Functions { template class ArgyrisPreBasis; /** \brief Nodal basis of a scalar cubic Argyris finite element space * * \ingroup FunctionSpaceBasesImplementations * * \note This only works for simplex grids. The Argyris basis is only implemented for degree 5. * \note The Argyris Finite element has the following properties: * - Its global space is in \f$ C^1 \f$. * - Its interpolation evaluates derivatives up to order 2, i.e. you cannot interpolate into a lambda function. * - Strongly enforcing boundary conditions is not as simple as with langrange bases * - It global space is not nested, i.e. the space on a refined grid is not a subspace of the * space on the coarser grid. * All arguments passed to the constructor will be forwarded to the constructor * of ArgyrisPreBasis. * * \tparam GV The GridView that the space is defined on * \tparam R The range type of the local basis */ template using ArgyrisBasis = DefaultGlobalBasis >; namespace Impl { /** \brief Associations of the Argyris degrees of freedom to subentities of the triangle */ class ArgyrisLocalCoefficients { public: using size_type = std::size_t; static constexpr int dim = 2; ArgyrisLocalCoefficients() { for (unsigned int i = 0; i < 3; i++) // subentities: three vertices for (unsigned int k = 0; k < 6; k++) // 6 basis functions per vertex localKeys_[6 * i + k] = LocalKey(i, dim, k); //(subentity, codim, number of dof for this subentity) for (unsigned int i = 0; i < 3; ++i) // subentities: three edges localKeys_[18 + i] = LocalKey(i, dim - 1, 0); // one node per edge } /** \brief number of coefficients */ static constexpr size_type size() { return 21; } /** \brief get i'th index */ LocalKey const &localKey(size_type i) const { return localKeys_[i]; } private: std::array localKeys_; }; /** \brief Implementation of Argyris Polynomials * \tparam D Type to represent the field in the domain * \tparam R Type to represent the field in the range * \tparam dim Dimension of the domain simplex */ template class ArgyrisReferenceLocalBasis { static constexpr int dim = 2; public: using Traits = H2LocalBasisTraits, R, 1, FieldVector, FieldMatrix, FieldMatrix >; private: /** * \brief Get the Argyris Coefficients Matrix * \return FieldMatrix * where size is the dimension of the quintic polynomial space (21) * * This returns the basis transformation matrix from a monomial * basis to the Argyris basis on the reference domain. * I.e. the i-th row of the returned matrix contains the * coefficients of the i-th Argyris basis function with respect to a * monomial basis. The monomials are enumerated as in the * MonomialSet of the corresponding dimension and order. * The values are taken (and reordered) from https://defelement.com/elements/argyris.html. */ static constexpr auto getArgyrisCoefficients() { // Define std::sqrt(2.) manually in double precision, // because std::sqrt is not constexpr before C++26. D sqrt2 = -8. * 1.414213562373095; return Dune::FieldMatrix{ // vertex functionals // l_0 {1, /*0th order*/ 0, 0, /*1th order*/ 0, 0, 0, /*2th order*/ -10, 0, 0, -10, /*3th order*/ 15, 0, -30, 0, 15, /*4th order*/ -6, 0, 30, 30, 0, -6}, /*5th order*/ // l_1 {0, 1, 0, 0, 0, 0, -6, 0, -11, 0, 8, 0, 10, 18, 0, -3, 0, 1, -10, -8, 0}, // l_2, l_1 mirrored { 0, 0, 1, 0, 0, 0, 0, -11, 0, -6, 0, 18, 10, 0, 8, 0, -8, -10, 1, 0, -3}, // l_3 { 0, 0, 0, 0.5, 0, 0, -1.5, 0, 0, 0, 1.5, 0, -1.5, 0, 0, -0.5, 0, 1.5, 1, 0, 0}, // l_4 { 0, 0, 0, 0, 1, 0, 0, -4, -4, 0, 0, 5, 10, 5, 0, 0, -2, -6, -6, -2, 0}, // l_5, l_3 mirrored { 0, 0, 0, 0, 0, 0.5, 0, 0, 0, -1.5, 0, 0, -1.5, 0, 1.5, 0, 0, 1, 1.5, 0, -0.5}, // l_6 {0, 0, 0, 0, 0, 0, 10, 0, 0, 0, -15, 0, 15, 0, 0, 6, 0, -15, -15, 0, 0}, // l_7 {0, 0, 0, 0, 0, 0, -4, 0, 0, 0, 7, 0, -3.5, 0, 0, -3, 0, 3.5, 3.5, 0, 0}, // l_8 {0, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0, 14, 18.5, 0, 0, 0, -8, -18.5, -13.5, 0, 0}, // l_9 { 0, 0, 0, 0, 0, 0, 0.5, 0, 0, 0, -1, 0, 0.25, 0, 0, 0.5, 0, -0.25, -0.25, 0, 0}, // l_10 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -3, -3.5, 0, 0, 0, 2, 3.5, 2.5, 0, 0}, // l_11 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.25, 0, 0, 0, 0, -0.75, -1.25, 0, 0}, // l_12 mirrors l_6 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 15, 0, -15, 0, 0, -15, -15, 0, 6}, // l_13 mirrors l_8 { 0, 0, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0, 18.5, 14, 0, 0, 0, -13.5, -18.5, -8, 0}, // l_14 mirrors l_7 { 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, 0, -3.5, 0, 7, 0, 0, 3.5, 3.5, 0, -3}, // l_15 mirrors l_11 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.25, 0, 0, 0, 0, -1.25, -0.75, 0, 0}, // l_16 mirrors l_10 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -3.5, -3, 0, 0, 0, 2.5, 3.5, 2, 0}, // l_17 mirrors l_9 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5, 0, 0, 0.25, 0, -1, 0, 0, -0.25, -0.25, 0, 0.5}, // edge functionals // l_18 { 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, -32, -32, 0, 0, 0, 16, 32, 16, 0, 0}, // l_19 { 0, 0, 0, 0, 0, 0, 0, 0, -16, 0, 0, 0, 32, 32, 0, 0, 0, -16, -32, -16, 0}, // l_20 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1. * sqrt2, 0, 0, 0, 0, sqrt2, sqrt2, 0, 0}}; } static constexpr auto referenceBasisCoefficients = getArgyrisCoefficients(); static constexpr MonomialSet monomials = {}; public: /** The number of basis functions in the basis */ static constexpr unsigned int size() { return ArgyrisLocalCoefficients::size(); } /** The polynomial order of the basis */ static constexpr unsigned int order() { return 5; } /** \brief Evaluate function values of all shape functions at a given point * * \param[in] in The evaluation point * \param[out] out Values of all shape functions at that point */ void evaluateFunction(const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto monomialValues = monomials(in); multiplyWithCoefficentMatrix(referenceBasisCoefficients, monomialValues, out); } /** \brief Evaluate Jacobians of all shape functions at a given point * * \param[in] in The evaluation point * \param[out] out Jacobians of all shape functions at that point */ void evaluateJacobian(const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto monomialValues = derivative(monomials)(in); multiplyWithCoefficentMatrix(referenceBasisCoefficients, monomialValues, out); } /** \brief Evaluate Hessians of all shape functions at a given point * * \param[in] in The evaluation point * \param[out] out Hessians of all shape functions at that point */ void evaluateHessian(const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto monomialValues = derivative(derivative(monomials))(in); multiplyWithCoefficentMatrix(referenceBasisCoefficients, monomialValues, out); } /** \brief Evaluate partial derivatives of all shape functions at a given point * * \param[in] order The partial derivative to be computed, as a multi-index * \param[in] in The evaluation point * \param[out] out Jacobians of all shape functions at that point */ void partial(std::array order, const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto totalOrder = std::accumulate(order.begin(), order.end(), 0); if (totalOrder == 0) evaluateFunction(in, out); else if (totalOrder == 1) { evaluateJacobian(in,jacobiansBuffer_); std::size_t which = std::max_element(order.begin(), order.end()) - order.begin(); for (auto i : Dune::range(size())) out[i] = jacobiansBuffer_[i][0][which]; } else if (totalOrder == 2) { evaluateHessian(in, hessianBuffer_); std::size_t first, second; first = std::max_element(order.begin(), order.end()) - order.begin(); if (order[first] == 2) second = first; else { order[first] = 0; second = std::max_element(order.begin(), order.end()) - order.begin(); } for (auto i : Dune::range(size())) out[i] = hessianBuffer_[i][first][second]; } else DUNE_THROW(RangeError, "partial() not implemented for given order"); } private: mutable std::vector jacobiansBuffer_; mutable std::vector hessianBuffer_; }; /** \brief Class that evaluates the push forwards of the global nodes of a * LocalFunction. It stretches the LocalInterpolation interface, because we * evaluate the derivatives of f. * */ template class ArgyrisLocalInterpolation { using size_type = std::size_t; static constexpr int dim = 2; static constexpr unsigned int size() { return ArgyrisLocalCoefficients::size(); } using FunctionalDescriptor = Dune::Functions::Impl::FunctionalDescriptor; public: ArgyrisLocalInterpolation() { // first vertex // function evaluation descriptors_[0] = FunctionalDescriptor(); // gradient evaluations descriptors_[1] = FunctionalDescriptor({1,0}); descriptors_[2] = FunctionalDescriptor({0,1}); // hessian evaluations descriptors_[3] = FunctionalDescriptor({2,0}); descriptors_[4] = FunctionalDescriptor({1,1}); descriptors_[5] = FunctionalDescriptor({0,2}); // second vertex descriptors_[6] = FunctionalDescriptor(); descriptors_[7] = FunctionalDescriptor({1,0}); descriptors_[8] = FunctionalDescriptor({0,1}); descriptors_[9] = FunctionalDescriptor({2,0}); descriptors_[10] = FunctionalDescriptor({1,1}); descriptors_[11] = FunctionalDescriptor({0,2}); // third vertex descriptors_[12] = FunctionalDescriptor(); descriptors_[13] = FunctionalDescriptor({1,0}); descriptors_[14] = FunctionalDescriptor({0,1}); descriptors_[15] = FunctionalDescriptor({2,0}); descriptors_[16] = FunctionalDescriptor({1,1}); descriptors_[17] = FunctionalDescriptor({0,2}); // normal derivatives at edge midpoints descriptors_[18] = FunctionalDescriptor(1); descriptors_[19] = FunctionalDescriptor(1); descriptors_[20] = FunctionalDescriptor(1); } /** \brief bind the Interpolation to an element and a local state. */ template void bind( Element const& element, std::arrayconst& averageVertexMeshSize, std::bitset<3>const& edgeOrientation) { averageVertexMeshSize_ = &averageVertexMeshSize; auto geometry = element.geometry(); // get global Normals and midpoints auto refElement = Dune::referenceElement(geometry.type()); for (std::size_t i = 0; i < 3; ++i) { localVertices_[i] = refElement.position(i, 2); localMidpoints_[i] = refElement.position(i, 1); std::size_t lower = (i == 2) ? 1 : 0; std::size_t upper = (i == 0) ? 1 : 2; auto edge = geometry.global(refElement.position(upper, 2)) - geometry.global(refElement.position(lower, 2)); // normalize and orient edge /= edge.two_norm() * (edgeOrientation[i] ? -1. : 1.); // Rotation by pi/2. Note that Kirby rotates by 3*pi/2 globalNormals_[i] = {-edge[1], edge[0]}; } } /** \brief Evaluate a given function and its derivatives at the nodes * * \tparam F Type of function to evaluate * \tparam C Type used for the values of the function * \param[in] f Function to evaluate * \param[out] out Array of function values */ template void interpolate(F const& f, std::vector& out) const { out.resize(size()); auto&& df = derivative(f); auto&& Hf = derivative(df); int offset = 0; // iterate over vertices for (int i = 0; i < 3; i++, offset += 6) { auto&& x = localVertices_[i]; auto derivativeValue = squeezeTensor(df(x)); auto hessianValue = squeezeTensor(Hf(x)); auto && h = (*averageVertexMeshSize_)[i]; out[offset ] = f(x); out[offset + 1] = derivativeValue[0] * h; out[offset + 2] = derivativeValue[1] * h; out[offset + 3] = hessianValue[0][0] * h*h; out[offset + 4] = hessianValue[0][1] * h*h; out[offset + 5] = hessianValue[1][1] * h*h; } // iterate over edges for (int i = 0; i < 3; i++) out[18 + i] = squeezeTensor(df(localMidpoints_[i])).dot(globalNormals_[i]); } /** * \brief Get the object that describes which type of evaluations is performed by the dual basis */ const FunctionalDescriptor& functionalDescriptor(size_type i) const { return descriptors_[i]; } protected: std::array, 3> globalNormals_; std::array, 3> localMidpoints_; std::array, 3> localVertices_; std::array const* averageVertexMeshSize_; std::array descriptors_; }; // TODO: There is a copy of this further up template struct ArgyrisLocalBasisTraits : public H2LocalBasisTraits, R, 1, Dune::FieldVector, Dune::FieldMatrix, Dune::FieldMatrix > {}; /** \brief Argyris finite element for triangles, as defined on the reference Element * * Note that this is a non affine-equivalent finite element. * It requires an additional transformation to the relate reference basis * with the pullbacks of global basis. * * \tparam D Type used for domain coordinates * \tparam R Type used for function values */ template class ArgyrisLocalFiniteElement : public Impl::TransformedFiniteElementMixin, ArgyrisLocalBasisTraits > { using Base = Impl::TransformedFiniteElementMixin< ArgyrisLocalFiniteElement, ArgyrisLocalBasisTraits >; friend class Impl::TransformedLocalBasis, ArgyrisLocalBasisTraits >; static constexpr int dim = 2; public: /** \brief Export number types, dimensions, etc. */ using size_type = std::size_t; using Traits = LocalFiniteElementTraits< Impl::TransformedLocalBasis, ArgyrisLocalBasisTraits >, Impl::ArgyrisLocalCoefficients, Impl::ArgyrisLocalInterpolation >; /** \brief Returns the assignment of the degrees of freedom to the element * subentities */ const typename Traits::LocalCoefficientsType &localCoefficients() const { return coefficients_; } /** \brief Returns object that evaluates degrees of freedom */ const typename Traits::LocalInterpolationType &localInterpolation() const { return interpolation_; } /** \brief The reference element that the local finite element is defined on */ static constexpr GeometryType type() { return GeometryTypes::simplex(dim); } /** The size of the transformed finite element. */ static constexpr size_type size() { return Impl::ArgyrisLocalCoefficients::size(); } /** Binds the Finite Element to an element. */ template void bind(VertexMapper const& vertexMapper, std::vector const& globalAverageVertexMeshSize, ElementMapper const& elementMapper, std::vector >const& edgeOrientations, Element const &e) { // Cache average mesh size for each vertex for (auto i : range(dim+1)) averageVertexMeshSize_[i] = globalAverageVertexMeshSize[vertexMapper.subIndex(e, i, dim)]; // Cache orientation for each mesh edgeOrientation_ = edgeOrientations[elementMapper.index(e)]; // Bind LocalInterpolation to updated local state interpolation_.bind(e, averageVertexMeshSize_, edgeOrientation_); // Compute local transformation matrices for each vertex const auto& geometry = e.geometry(); const auto& refElement = ReferenceElements::simplex(); for (auto i : range(dim+1)) { vertexJacobians_[i] = geometry.jacobian(refElement.position(i, dim)); } // get geometrical information std::array, 3> referenceTangents; // get local and global Tangents for (std::size_t i = 0; i < 3; ++i) { std::size_t lower = (i == 2) ? 1 : 0; std::size_t upper = (i == 0) ? 1 : 2; auto edge = refElement.position(upper, 2) - refElement.position(lower, 2); // store normalized reference Tangent vectors referenceTangents[i] = edge / edge.two_norm(); auto globalEdge = geometry.global(refElement.position(upper, 2)) - geometry.global(refElement.position(lower, 2)); // store length of global tangents and normalized global tangent vectors l[i] = globalEdge.two_norm(); globalTangents[i] = globalEdge / l[i]; tau[i] = FieldVector{ globalTangents[i][0] * globalTangents[i][0], 2. * globalTangents[i][0] * globalTangents[i][1], globalTangents[i][1] * globalTangents[i][1] }; // The following variables are named as in Kirby's paper // two g matrices auto&& referenceG = FieldMatrix{ {-referenceTangents[i][1], referenceTangents[i][0]}, {referenceTangents[i][0], referenceTangents[i][1]} }; auto&& globalG = FieldMatrix{ {-globalTangents[i][1], globalTangents[i][0]}, {globalTangents[i][0], globalTangents[i][1]} }; // b[i] = globalG * geometry.jacobian(refElement.position(i, 2)) * referenceG; theta[i][0][0] = vertexJacobians_[i][0][0] * vertexJacobians_[i][0][0]; theta[i][0][1] = vertexJacobians_[i][0][0] * vertexJacobians_[i][0][1]; theta[i][0][2] = vertexJacobians_[i][0][1] * vertexJacobians_[i][0][1]; theta[i][1][0] = 2. * vertexJacobians_[i][0][0] * vertexJacobians_[i][1][0]; theta[i][1][1] = vertexJacobians_[i][0][0] * vertexJacobians_[i][1][1] + vertexJacobians_[i][0][1] * vertexJacobians_[i][1][0]; theta[i][1][2] = 2. * vertexJacobians_[i][0][1] * vertexJacobians_[i][1][1]; theta[i][2][0] = vertexJacobians_[i][1][0] * vertexJacobians_[i][1][0]; theta[i][2][1] = vertexJacobians_[i][1][0] * vertexJacobians_[i][1][1]; theta[i][2][2] = vertexJacobians_[i][1][1] * vertexJacobians_[i][1][1]; } } protected: /** \brief Returns the local basis, i.e., the set of shape functions */ Impl::ArgyrisReferenceLocalBasis const& referenceLocalBasis() const { return basis_; } /** Applies the transformation. Note that we do not distinguish for * Scalar/Vector/Matrix Type, but only assume the Values to be Elements of a Vectorspace. * We assume containers with random access iterators. */ template void transform(InputValues const &inValues, OutputValues &outValues) const { using std::pow; assert(inValues.size() == size()); assert(outValues.size() == inValues.size()); // compatibility with sympy code below auto &[b_0, b_1, b_2] = b; auto &[J_0, J_1, J_2] = vertexJacobians_; auto &[theta_0, theta_1, theta_2] = theta; auto & h = averageVertexMeshSize_; auto & o = edgeOrientation_; std::array, 3> const &t = globalTangents; // This code is generated with sympy. // It a matrix free implementation of the matrix V in Kirbys paper. outValues[0] = -15.0/8.0*b_0[1][0]*inValues[18]/l[0] - 15.0/8.0*b_1[1][0]*inValues[19]/l[1] + inValues[0]; outValues[1] = (J_0[0][0]*inValues[1] + J_0[0][1]*inValues[2] - 0.4375*b_0[1][0]*inValues[18]*t[0][0] - 0.4375*b_1[1][0]*inValues[19]*t[1][0])/h[0]; outValues[2] = (J_0[1][0]*inValues[1] + J_0[1][1]*inValues[2] - 0.4375*b_0[1][0]*inValues[18]*t[0][1] - 0.4375*b_1[1][0]*inValues[19]*t[1][1])/h[0]; outValues[3] = (-1.0/32.0*b_0[1][0]*inValues[18]*l[0]*tau[0][0] - 1.0/32.0*b_1[1][0]*inValues[19]*l[1]*tau[1][0] + inValues[3]*theta_0[0][0] + inValues[4]*theta_0[0][1] + inValues[5]*theta_0[0][2])/h[0] /h[0]; outValues[4] = (-1.0/32.0*b_0[1][0]*inValues[18]*l[0]*tau[0][1] - 1.0/32.0*b_1[1][0]*inValues[19]*l[1]*tau[1][1] + inValues[3]*theta_0[1][0] + inValues[4]*theta_0[1][1] + inValues[5]*theta_0[1][2])/h[0] /h[0]; outValues[5] = (-1.0/32.0*b_0[1][0]*inValues[18]*l[0]*tau[0][2] - 1.0/32.0*b_1[1][0]*inValues[19]*l[1]*tau[1][2] + inValues[3]*theta_0[2][0] + inValues[4]*theta_0[2][1] + inValues[5]*theta_0[2][2])/h[0] /h[0]; outValues[6] = (15.0/8.0)*b_0[1][0]*inValues[18]/l[0] - 15.0/8.0*b_2[1][0]*inValues[20]/l[2] + inValues[6]; outValues[7] = (J_1[0][0]*inValues[7] + J_1[0][1]*inValues[8] - 0.4375*b_0[1][0]*inValues[18]*t[0][0] - 0.4375*b_2[1][0]*inValues[20]*t[2][0])/h[1]; outValues[8] = (J_1[1][0]*inValues[7] + J_1[1][1]*inValues[8] - 0.4375*b_0[1][0]*inValues[18]*t[0][1] - 0.4375*b_2[1][0]*inValues[20]*t[2][1])/h[1]; outValues[9] = ((1.0/32.0)*b_0[1][0]*inValues[18]*l[0]*tau[0][0] - 1.0/32.0*b_2[1][0]*inValues[20]*l[2]*tau[2][0] + inValues[9]*theta_1[0][0] + inValues[10]*theta_1[0][1] + inValues[11]*theta_1[0][2])/h[1] /h[1]; outValues[10] = ((1.0/32.0)*b_0[1][0]*inValues[18]*l[0]*tau[0][1] - 1.0/32.0*b_2[1][0]*inValues[20]*l[2]*tau[2][1] + inValues[9]*theta_1[1][0] + inValues[10]*theta_1[1][1] + inValues[11]*theta_1[1][2])/h[1] /h[1]; outValues[11] = ((1.0/32.0)*b_0[1][0]*inValues[18]*l[0]*tau[0][2] - 1.0/32.0*b_2[1][0]*inValues[20]*l[2]*tau[2][2] + inValues[9]*theta_1[2][0] + inValues[10]*theta_1[2][1] + inValues[11]*theta_1[2][2])/h[1] /h[1]; outValues[12] = (15.0/8.0)*b_1[1][0]*inValues[19]/l[1] + (15.0/8.0)*b_2[1][0]*inValues[20]/l[2] + inValues[12]; outValues[13] = (J_2[0][0]*inValues[13] + J_2[0][1]*inValues[14] - 0.4375*b_1[1][0]*inValues[19]*t[1][0] - 0.4375*b_2[1][0]*inValues[20]*t[2][0])/h[2]; outValues[14] = (J_2[1][0]*inValues[13] + J_2[1][1]*inValues[14] - 0.4375*b_1[1][0]*inValues[19]*t[1][1] - 0.4375*b_2[1][0]*inValues[20]*t[2][1])/h[2]; outValues[15] = ((1.0/32.0)*b_1[1][0]*inValues[19]*l[1]*tau[1][0] + (1.0/32.0)*b_2[1][0]*inValues[20]*l[2]*tau[2][0] + inValues[15]*theta_2[0][0] + inValues[16]*theta_2[0][1] + inValues[17]*theta_2[0][2])/h[2] /h[2]; outValues[16] = ((1.0/32.0)*b_1[1][0]*inValues[19]*l[1]*tau[1][1] + (1.0/32.0)*b_2[1][0]*inValues[20]*l[2]*tau[2][1] + inValues[15]*theta_2[1][0] + inValues[16]*theta_2[1][1] + inValues[17]*theta_2[1][2])/h[2] /h[2]; outValues[17] = ((1.0/32.0)*b_1[1][0]*inValues[19]*l[1]*tau[1][2] + (1.0/32.0)*b_2[1][0]*inValues[20]*l[2]*tau[2][2] + inValues[15]*theta_2[2][0] + inValues[16]*theta_2[2][1] + inValues[17]*theta_2[2][2])/h[2] /h[2]; outValues[18] = b_0[0][0]*inValues[18]*(o[0] ? -1 : 1); outValues[19] = b_1[0][0]*inValues[19]*(o[1] ? -1 : 1); outValues[20] = b_2[0][0]*inValues[20]*(o[2] ? -1 : 1); } private: typename Impl::ArgyrisReferenceLocalBasis basis_; typename Traits::LocalCoefficientsType coefficients_; typename Traits::LocalInterpolationType interpolation_; // the transformation to correct the lack of affine equivalence boils down to // matrix without blockstructure, because the normal derivative dofs interact nontrivially // with the others, for details see Kirby's paper. // The following geometric information is needed // the jacobians per vertex std::array, dim+1> vertexJacobians_; // a three by three matrix per vertex formalizing the rotation of the hessian in voigt notation std::array, 3> theta; // the edge lengths FieldVector l; // the global tangent vectors (normalized) std::array, 3> globalTangents; // Geometric quantities without trivial meaning std::array, 3> tau; std::array, 3> b; // Additionally, we collect some global information // the local state, i.e. a collection of global information restricted to this element std::array averageVertexMeshSize_; std::bitset<3> edgeOrientation_; }; } // end namespace Impl // ***************************************************************************** // This is the reusable part of the basis. It contains // // ArgyrisPreBasis // ArgyrisNode // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template class ArgyrisNode : public LeafBasisNode { using Mapper = Dune::MultipleCodimMultipleGeomTypeMapper; public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; using FiniteElement = typename Impl::ArgyrisLocalFiniteElement; ArgyrisNode(Mapper const& vertexMapper, Mapper const& elementMapper, std::vector const& averageVertexMeshSize, std::vector > const& edgeOrientation) : element_(nullptr) , vertexMapper_(&vertexMapper) , elementMapper_(&elementMapper) , averageVertexMeshSize_(&averageVertexMeshSize) , edgeOrientation_(&edgeOrientation) {} //! Return current element, throw if unbound Element const &element() const { return *element_; } /** \brief Return the LocalFiniteElement for the element we are bound to * * The LocalFiniteElement implements the corresponding interfaces of the * dune-localfunctions module */ FiniteElement const &finiteElement() const { return finiteElement_; } //! Bind to element. void bind(Element const &e) { element_ = &e; finiteElement_.bind(*vertexMapper_, *averageVertexMeshSize_, *elementMapper_,* edgeOrientation_, *element_ ); this->setSize(finiteElement_.size()); } //! The order of the local basis. unsigned int order() const { return finiteElement_.localBasis().order(); } protected: FiniteElement finiteElement_; Element const* element_; Mapper const* vertexMapper_; Mapper const* elementMapper_; std::vector const* averageVertexMeshSize_; std::vector > const* edgeOrientation_; }; /** * \brief A pre-basis for a Argyrisbasis * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The grid view that the FE basis is defined on * \tparam R Range type used for shape function values * \note This only works for simplex grids */ template class ArgyrisPreBasis : public LeafPreBasisMapperMixin { using Base = LeafPreBasisMapperMixin; using SubEntityMapper = Dune::MultipleCodimMultipleGeomTypeMapper; using Element = typename GV::template Codim<0>::Entity; using D = typename GV::ctype; static const std::size_t dim = GV::dimension; // helper methods to assign each subentity the number of dofs. Used by the LeafPreBasisMapperMixin. static constexpr auto ArgyrisMapperLayout(Dune::GeometryType type, int gridDim) { assert(gridDim == 2); if (type.isVertex()) return 6; // one evaluation dof and two derivative dofs and three hessian dofs per vertex else if (type.isLine()) return 1; else if (type.isTriangle()) return 0; else DUNE_THROW(Dune::Exception, "Invalid Geometry type for Argyris Element!"); } public: //! The grid view that the FE basis is defined on using GridView = GV; //! Type used for indices and size information using size_type = std::size_t; //! Template mapping root tree path to type of created tree node using Node = ArgyrisNode; public: //! Constructor for a given grid view object ArgyrisPreBasis(const GV &gv) : Base(gv, ArgyrisMapperLayout) , vertexMapper_({gv, mcmgVertexLayout()}) , elementMapper_({gv, mcmgElementLayout()}) { averageVertexMeshSize_ = Impl::computeAverageSubEntityMeshSize(vertexMapper_); edgeOrientations_ = Impl::computeEdgeOrientations(elementMapper_); } //! Update the stored grid view, to be called if the grid has changed void update(GridView const &gv) { Base::update(gv); vertexMapper_.update(this->gridView()); elementMapper_.update(this->gridView()); averageVertexMeshSize_ = Impl::computeAverageSubEntityMeshSize(vertexMapper_); edgeOrientations_ = Impl::computeEdgeOrientations(elementMapper_); } //! Create tree node Node makeNode() const { return Node{vertexMapper_, elementMapper_, averageVertexMeshSize_, edgeOrientations_}; } protected: SubEntityMapper vertexMapper_; std::vector averageVertexMeshSize_; SubEntityMapper elementMapper_; std::vector > edgeOrientations_; }; // class ArgyrisPreBasis namespace BasisFactory { /** * \brief construct a PreBasisFactory for the quintic Argyris Finite Element * * \tparam R RangeFieldType * \return the PreBasisFactory * \relates ArgyrisBasis */ template auto argyris() { return [=](auto const &gridView) { return ArgyrisPreBasis, R>(gridView); }; } } // end namespace BasisFactory } // end namespace Dune::Functions #endif dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/basistags.hh000066400000000000000000000135721513634022200266040ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BASISTAGS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BASISTAGS_HH #include #include namespace Dune { namespace Functions { namespace Concept { struct IndexMergingStrategy { template auto require(T&& t) -> decltype( registerIndexMergingStrategy(t) ); }; template static constexpr bool isIndexMergingStrategy() { return models(); } template static constexpr bool isIndexMergingStrategy(T&& t) { return models>(); } } // namespace Concept namespace BasisFactory { /** * \brief Base class for index merging strategies to simplify detection * * \ingroup FunctionSpaceBasesUtilities */ struct IndexMergingStrategy {}; void registerIndexMergingStrategy(IndexMergingStrategy); /** * \brief Lexicographic merging of direct children without blocking. * * \ingroup FunctionSpaceBasesUtilities * * Example: For two children {f} and {g} with multi-indices (all i*,k* can be multi-indices, themselves) * * function in {f}| index * ---------------|------ * f_0 |(0,i0) * f_1 |(0,i1) * f_2 |(1,i2) * * function in {g}| index * ---------------|------ * g_0 | (0,k0) * g_1 | (1,k1) * g_2 | (2,k2) * * the merged indices will be * * function in {f,g}| index * -----------------|------ * f_0 | (0,i0) * f_1 | (0,i1) * f_2 | (1,i2) * g_0 | (2,k0) * g_1 | (3,k1) * g_2 | (4,k2) */ struct FlatLexicographic : public IndexMergingStrategy {}; /** * \brief Interleaved merging of direct children without blocking. * * \ingroup FunctionSpaceBasesUtilities * * Example: For two children {f} and {g} with multi-indices (all i* can be multi-indices, themselves) * * function in {f}| index * ---------------|------ * f_0 |(0,i0) * f_1 |(1,i1) * f_2 |(2,i2) * * function in {g}| index * ---------------|------ * g_0 | (0,i0) * g_1 | (1,i1) * g_2 | (2,i2) * * the merged indices will be * * function in {f,g}| index * -----------------|------ * f_0 | (0,i0) * g_0 | (1,i0) * f_1 | (2,i1) * g_1 | (3,i1) * f_2 | (4,i2) * g_2 | (5,i2) */ struct FlatInterleaved : public IndexMergingStrategy {}; /** * \brief Lexicographic merging of direct children with blocking (i.e. creating one block per direct child). * * \ingroup FunctionSpaceBasesUtilities * * Example: For two children {f} and {g} with multi-indices (all i*,k* can be multi-indices, themselves) * * function in {f}| index * ---------------|------ * f_0 |(i0) * f_1 |(i1) * f_2 |(i2) * * function in {g}| index * ---------------|------ * g_0 | (k0) * g_1 | (k1) * g_2 | (k2) * * the merged indices will be * * function in {f,g}| index * -----------------|------ * f_0 | (0,i0) * f_1 | (0,i1) * f_2 | (0,i2) * g_0 | (1,k0) * g_1 | (1,k1) * g_2 | (1,k2) */ struct BlockedLexicographic : public IndexMergingStrategy {}; /** * \brief Interleaved merging of direct children with blocking (i.e. creating blocks at the leaves containing one leaf per child each). * * \ingroup FunctionSpaceBasesUtilities * * Example: For two children {f} and {g} with multi-indices (all i* can be multi-indices, themselves) * * function in {f}| index * ---------------|------ * f_0 |(i0) * f_1 |(i1) * f_2 |(i2) * * function in {g}| index * ---------------|------ * g_0 | (i0) * g_1 | (i1) * g_2 | (i2) * * the merged indices will be * * function in {f,g}| index * -----------------|------ * f_0 | (i0,0) * g_0 | (i0,1) * f_1 | (i1,0) * g_1 | (i1,1) * f_2 | (i2,0) * g_2 | (i2,1) */ struct BlockedInterleaved : public IndexMergingStrategy {}; /** * \brief Creates a lexicographic merging of direct children without blocking. * * \ingroup FunctionSpaceBasesUtilities */ constexpr FlatLexicographic flatLexicographic() { return {}; } /** * \brief Creates an interleaved merging of direct children without blocking. * * \ingroup FunctionSpaceBasesUtilities */ constexpr FlatInterleaved flatInterleaved() { return {}; } /** * \brief Creates a lexicographic merging of direct children with blocking (i.e. creating one block per direct child). * * \ingroup FunctionSpaceBasesUtilities */ constexpr BlockedLexicographic blockedLexicographic() { return {}; } /** * \brief Creates an interleaved merging of direct children with blocking (i.e. creating blocks at the leaves containing one leaf per child each). * * \ingroup FunctionSpaceBasesUtilities */ constexpr BlockedInterleaved blockedInterleaved() { return {}; } } // end namespace BasisFactory } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BASISTAGS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/boundarydofs.hh000066400000000000000000000112241513634022200273130ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BOUNDARYDOFS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BOUNDARYDOFS_HH #include #include namespace Dune { namespace Functions { /** * \brief Loop over all DOFs on the boundary * * \ingroup FunctionSpaceBasesUtilities * * This loops over all DOFs of a basis associated to sub-entities * on the boundary. This overload will pass three arguments to the * given loop callback: The local index of the boundary DOF, * a bound local view this local index belongs to, * and a boundary intersection associated to a sub-entity such that * the DOF is associated to a sub-sub-entity of this sub-entity. * Notice that this may visit the same DOF multiple times. * * If this callback signature is not suitable you can use one of * the another variants of forEachBoundaryDOF. * * \param basis A function space basis * \param f A callback that will be called with a local index, a bound local view, and an intersection of the visited boundary DOF */ template>()(0, std::declval(),std::declval()), 0) = 0> void forEachBoundaryDOF(const Basis& basis, F&& f) { auto localView = basis.localView(); auto seDOFs = subEntityDOFs(basis); const auto& gridView = basis.gridView(); for(auto&& element : elements(gridView)) if (element.hasBoundaryIntersections()) { localView.bind(element); for(const auto& intersection: intersections(gridView, element)) if (intersection.boundary()) for(auto localIndex: seDOFs.bind(localView,intersection)) f(localIndex, localView, intersection); } } /** * \brief Loop over all DOFs on the boundary * * \ingroup FunctionSpaceBasesUtilities * * This loops over all DOFs of a basis associated to sub-entities * on the boundary. This overload will pass two arguments to the * given loop callback: The local index of the boundary DOF and * a bound local view this local index belongs to. * Notice that this may visit the same DOF multiple times. * * If this callback signature is not suitable you can use one of * the another variants of forEachBoundaryDOF. * * \param basis A function space basis * \param f A callback that will be called with a local index and a bound local view of the visited boundary DOF */ template>()(0, std::declval()),0) = 0> void forEachBoundaryDOF(const Basis& basis, F&& f) { auto localView = basis.localView(); auto seDOFs = subEntityDOFs(basis); const auto& gridView = basis.gridView(); for(auto&& element : elements(gridView)) if (element.hasBoundaryIntersections()) { localView.bind(element); for(const auto& intersection: intersections(gridView, element)) if (intersection.boundary()) for(auto localIndex: seDOFs.bind(localView,intersection)) f(localIndex, localView); } } /** * \brief Loop over all DOFs on the boundary * * \ingroup FunctionSpaceBasesUtilities * * This loops over all DOFs of a basis associated to sub-entities * on the boundary. This overload will pass a single argument to the * given loop callback: The global (multi-)index of the boundary DOF. * Notice that this may visit the same DOF multiple times. * * If this callback signature is not suitable you can use one of * the another variants of forEachBoundaryDOF. * * \param basis A function space basis * \param f A callback that will be called with the global index of the visited boundary DOF */ template>()(std::declval()),0) = 0> void forEachBoundaryDOF(const Basis& basis, F&& f) { auto localView = basis.localView(); auto seDOFs = subEntityDOFs(basis); const auto& gridView = basis.gridView(); for(auto&& element : elements(gridView)) if (element.hasBoundaryIntersections()) { localView.bind(element); for(const auto& intersection: intersections(gridView, element)) if (intersection.boundary()) for(auto localIndex: seDOFs.bind(localView,intersection)) f(localView.index(localIndex)); } } } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BOUNDARYDOFS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/brezzidouglasmarinibasis.hh000066400000000000000000000231321513634022200317230ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BREZZIDOUGLASMARINIBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BREZZIDOUGLASMARINIBASIS_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { namespace Impl { template struct BDMSimplexLocalInfo { static_assert((AlwaysFalse::value),"The requested type of BDM element is not implemented, sorry!"); }; template struct BDMSimplexLocalInfo<2,D,R,1> { using FiniteElement = BDM1Simplex2DLocalFiniteElement; static const std::size_t Variants = 8; }; template struct BDMSimplexLocalInfo<2,D,R,2> { using FiniteElement = BDM2Simplex2DLocalFiniteElement; static const std::size_t Variants = 8; }; template struct BDMCubeLocalInfo { static_assert((AlwaysFalse::value),"The requested type of BDM element is not implemented, sorry!"); }; template struct BDMCubeLocalInfo<2,D,R,1> { using FiniteElement = BDM1Cube2DLocalFiniteElement; static const std::size_t Variants = 16; }; template struct BDMCubeLocalInfo<2,D,R,2> { using FiniteElement = BDM2Cube2DLocalFiniteElement; static const std::size_t Variants = 16; }; template struct BDMCubeLocalInfo<3,D,R,1> { using FiniteElement = BDM1Cube3DLocalFiniteElement; static const std::size_t Variants = 64; }; template class BDMLocalFiniteElementMap { using D = typename GV::ctype; constexpr static auto dim = GV::dimension; using CubeFiniteElement = typename BDMCubeLocalInfo::FiniteElement; using SimplexFiniteElement = typename BDMSimplexLocalInfo::FiniteElement; public: using T = LocalBasisTraits, R, dim, FieldVector, FieldMatrix >; using FiniteElement = LocalFiniteElementVirtualInterface; BDMLocalFiniteElementMap(const GV& gv) : is_(&(gv.indexSet())), orient_(gv.size(0)) { update(gv); } void update(const GV& gv) { is_ = &(gv.indexSet()); orient_.resize(gv.size(0)); cubeVariant_.resize(BDMCubeLocalInfo::Variants); simplexVariant_.resize(BDMSimplexLocalInfo::Variants); // create all variants for (size_t i = 0; i < cubeVariant_.size(); i++) cubeVariant_[i] = std::make_shared >(CubeFiniteElement(i)); for (size_t i = 0; i < simplexVariant_.size(); i++) simplexVariant_[i] = std::make_shared >(SimplexFiniteElement(i)); // compute orientation for all elements // loop once over the grid for(const auto& cell : elements(gv)) { unsigned int myId = is_->index(cell); orient_[myId] = 0; for (const auto& intersection : intersections(gv,cell)) { if (intersection.neighbor() && (is_->index(intersection.outside()) > myId)) orient_[myId] |= (1 << intersection.indexInInside()); } } } //! \brief get local basis functions for entity template const FiniteElement& find(const EntityType& e) const { if (e.type().isCube()) return *cubeVariant_[orient_[is_->index(e)]]; else return *simplexVariant_[orient_[is_->index(e)]]; } private: std::vector > > cubeVariant_; std::vector > > simplexVariant_; const typename GV::IndexSet* is_; std::vector orient_; }; } // namespace Impl // ***************************************************************************** // This is the reusable part of the basis. It contains // // BrezziDouglasMariniPreBasis // BrezziDouglasMariniNode // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template class BrezziDouglasMariniNode; template class BrezziDouglasMariniPreBasis : public LeafPreBasisMapperMixin { using Base = LeafPreBasisMapperMixin; static const int dim = GV::dimension; // Degrees of freedom per subentity static MCMGLayout dofLayout() { return [](GeometryType gt, size_t gridDim) -> size_t { if (gt.dim() == gridDim) return dim*(k-1)*3; if (gt.dim() == gridDim-1) return dim+(k-1); return 0; }; } using FiniteElementMap = typename Impl::BDMLocalFiniteElementMap; public: /** \brief The grid view that the FE space is defined on */ using GridView = GV; using size_type = std::size_t; using Node = BrezziDouglasMariniNode; /** \brief Constructor for a given grid view object */ BrezziDouglasMariniPreBasis(const GridView& gv) : Base(gv, dofLayout()), finiteElementMap_(gv) { // There is no inherent reason why the basis shouldn't work for grids with more than one // element types. Somebody simply has to sit down and implement the missing bits. if (gv.indexSet().types(0).size() > 1) DUNE_THROW(Dune::NotImplemented, "Brezzi-Douglas-Marini basis is only implemented for grids with a single element type"); } /* \brief Update the stored grid view, to be called if the grid has changed */ void update (const GridView& gv) { Base::update(gv); finiteElementMap_.update(gv); } /** * \brief Create tree node */ Node makeNode() const { return Node{&finiteElementMap_}; } protected: FiniteElementMap finiteElementMap_; }; template class BrezziDouglasMariniNode : public LeafBasisNode { public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; using FiniteElementMap = typename Impl::BDMLocalFiniteElementMap; using FiniteElement = Impl::GlobalValuedLocalFiniteElement; BrezziDouglasMariniNode(const FiniteElementMap* finiteElementMap) : element_(nullptr), finiteElementMap_(finiteElementMap) {} //! Return current element, throw if unbound const Element& element() const { return *element_; } /** \brief Return the LocalFiniteElement for the element we are bound to * * The LocalFiniteElement implements the corresponding interfaces of the dune-localfunctions module */ const FiniteElement& finiteElement() const { return finiteElement_; } //! Bind to element. void bind(const Element& e) { element_ = &e; finiteElement_.bind((finiteElementMap_->find(*element_)), e); this->setSize(finiteElement_.size()); } protected: FiniteElement finiteElement_; const Element* element_; const FiniteElementMap* finiteElementMap_; }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can create a Brezzi-Douglas-Marini pre-basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam k Order of the Brezzi-Douglas-Marini element */ template auto brezziDouglasMarini() { return [](const auto& gridView) { return BrezziDouglasMariniPreBasis, k>(gridView); }; } } // end namespace BasisFactory // ***************************************************************************** // This is the actual global basis implementation based on the reusable parts. // ***************************************************************************** /** \brief Basis of a scalar k-th-order BDM finite element space on simplex and cube grids * * TODO: Fix this for grids with more than one element type * * \tparam GV The GridView that the space is defined on * \tparam k The order of the basis */ template using BrezziDouglasMariniBasis = DefaultGlobalBasis >; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BREZZIDOUGLASMARINIBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/bsplinebasis.hh000066400000000000000000001312331513634022200272750ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BSPLINEBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BSPLINEBASIS_HH /** \file * \brief The B-spline global function space basis */ #include #include /** \todo Don't use this matrix */ #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { // A maze of dependencies between the different parts of this. We need a few forward declarations template class BSplineLocalFiniteElement; template class BSplinePreBasis; /** \brief LocalBasis class in the sense of dune-localfunctions, presenting the restriction * of a B-spline patch to a knot span * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV Grid view that the basis is defined on * \tparam R Number type used for spline function values */ template class BSplineLocalBasis { friend class BSplineLocalFiniteElement; typedef typename GV::ctype D; enum {dim = GV::dimension}; public: //! \brief export type traits for function signature typedef LocalBasisTraits,R,1,FieldVector, FieldMatrix > Traits; /** \brief Constructor with a given B-spline patch * * The patch object does all the work. */ BSplineLocalBasis(const BSplinePreBasis& preBasis, const BSplineLocalFiniteElement& lFE) : preBasis_(preBasis), lFE_(lFE) {} /** \brief Evaluate all shape functions * \param in Coordinates where to evaluate the functions, in local coordinates of the current knot span * \param[out] out Result of shape functions evaluation */ void evaluateFunction (const FieldVector& in, std::vector >& out) const { FieldVector globalIn = offset_; scaling_.umv(in,globalIn); preBasis_.evaluateFunction(globalIn, out, lFE_.currentKnotSpan_); } /** \brief Evaluate Jacobian of all shape functions * \param in Coordinates where to evaluate the Jacobian, in local coordinates of the current knot span * \param[out] out Result of Jacobian evaluation */ void evaluateJacobian (const FieldVector& in, std::vector >& out) const { FieldVector globalIn = offset_; scaling_.umv(in,globalIn); preBasis_.evaluateJacobian(globalIn, out, lFE_.currentKnotSpan_); for (size_t i=0; i inline void evaluate (const typename std::array& directions, const typename Traits::DomainType& in, std::vector& out) const { switch(k) { case 0: evaluateFunction(in, out); break; case 1: { FieldVector globalIn = offset_; scaling_.umv(in,globalIn); preBasis_.evaluate(directions, globalIn, out, lFE_.currentKnotSpan_); for (size_t i=0; i globalIn = offset_; scaling_.umv(in,globalIn); preBasis_.evaluate(directions, globalIn, out, lFE_.currentKnotSpan_); for (size_t i=0; i& preBasis_; const BSplineLocalFiniteElement& lFE_; // Coordinates in a single knot span differ from coordinates on the B-spline patch // by an affine transformation. This transformation is stored in offset_ and scaling_. FieldVector offset_; DiagonalMatrix scaling_; }; /** \brief Attaches a shape function to an entity * * \ingroup FunctionSpaceBasesImplementations * * The attachment uses the same order as for Qk elements. This does *not* provide sufficient information * to compute global indices for the shape functions. However, it does allow to find all degrees of freedom * that belong to the grid boundary, if the knot vector is open. * * \note Currently only implemented for 1d and 2d grids. For higher dimensions you can still use * the BSplineBasis, but you won't be able to find the degrees of freedom on the grid boundary. * * \tparam dim Dimension of the reference cube */ template class BSplineLocalCoefficients { // Return i as a d-digit number in the (k+1)-nary system std::array multiindex (unsigned int i) const { std::array alpha; for (int j=0; j& subEntity) { if (sizes_[0]==1) { subEntity[0] = 0; return; } /* edge and vertex numbering 0----0----1 */ unsigned lastIndex=0; subEntity[lastIndex++] = 0; // corner 0 for (unsigned i = 0; i < sizes_[0] - 2; ++i) subEntity[lastIndex++] = 0; // inner dofs of element (0) subEntity[lastIndex++] = 1; // corner 1 assert(size()==lastIndex); } void setup2d(std::vector& subEntity) { unsigned lastIndex=0; // LocalKey: entity number , entity codim, dof indices within each entity /* edge and vertex numbering 2----3----3 | | | | 0 1 | | | | 0----2----1 */ // lower edge (2) subEntity[lastIndex++] = 0; // corner 0 for (unsigned i = 0; i < sizes_[0]-2; ++i) subEntity[lastIndex++] = 2; // inner dofs of lower edge (2) subEntity[lastIndex++] = 1; // corner 1 // iterate from bottom to top over inner edge dofs for (unsigned e = 0; e < sizes_[1]-2; ++e) { subEntity[lastIndex++] = 0; // left edge (0) for (unsigned i = 0; i < sizes_[0]-2; ++i) subEntity[lastIndex++] = 0; // face dofs subEntity[lastIndex++] = 1; // right edge (1) } // upper edge (3) subEntity[lastIndex++] = 2; // corner 2 for (unsigned i = 0; i < sizes_[0]-2; ++i) subEntity[lastIndex++] = 3; // inner dofs of upper edge (3) subEntity[lastIndex++] = 3; // corner 3 assert(size()==lastIndex); } public: void init(const std::array& sizes) { sizes_ = sizes; li_.resize(size()); // Set up array of codimension-per-dof-number std::vector codim(li_.size()); for (std::size_t i=0; i mIdx = multiindex(i); for (int j=0; j index(size()); for (std::size_t i=0; i mIdx = multiindex(i); for (int j=dim-1; j>=0; j--) if (mIdx[j]>0 and mIdx[j] subEntity(li_.size()); if (subEntity.size() > 0) { if (dim==1) { setup1d(subEntity); } else if (dim==2 and sizes_[0]>1 and sizes_[1]>1) { setup2d(subEntity); } } for (size_t i=0; i()); } //! get i'th index const LocalKey& localKey (std::size_t i) const { return li_[i]; } private: // Number of shape functions on this element per coordinate direction std::array sizes_; std::vector li_; }; /** \brief Local interpolation in the sense of dune-localfunctions, for the B-spline basis on tensor-product grids * * \ingroup FunctionSpaceBasesImplementations */ template class BSplineLocalInterpolation { public: //! \brief Local interpolation of a function template void interpolate (const F& f, std::vector& out) const { DUNE_THROW(NotImplemented, "BSplineLocalInterpolation::interpolate"); } }; /** \brief LocalFiniteElement in the sense of dune-localfunctions, for the B-spline basis on tensor-product grids * * \ingroup FunctionSpaceBasesImplementations * * This class ties together the implementation classes BSplineLocalBasis, BSplineLocalCoefficients, and BSplineLocalInterpolation * * \tparam D Number type used for domain coordinates * \tparam R Number type used for spline function values */ template class BSplineLocalFiniteElement { typedef typename GV::ctype D; enum {dim = GV::dimension}; friend class BSplineLocalBasis; public: /** \brief Export various types related to this LocalFiniteElement */ typedef LocalFiniteElementTraits, BSplineLocalCoefficients, BSplineLocalInterpolation > > Traits; /** \brief Constructor with a given B-spline basis */ BSplineLocalFiniteElement(const BSplinePreBasis& preBasis) : preBasis_(preBasis), localBasis_(preBasis,*this) {} /** \brief Copy constructor */ BSplineLocalFiniteElement(const BSplineLocalFiniteElement& other) : preBasis_(other.preBasis_), localBasis_(preBasis_,*this) {} /** \brief Bind LocalFiniteElement to a specific knot span of the spline patch * * Elements are the non-empty knot spans, here we do the renumbering * * \param elementIdx Integer coordinates in the tensor product patch */ void bind(const std::array& elementIdx) { /* \todo In the long run we need to precompute a table for this */ for (size_t i=0; i sizes; for (size_t i=0; i& localBasis() const { return localBasis_; } /** \brief Hand out a LocalCoefficients object */ const BSplineLocalCoefficients& localCoefficients() const { return localCoefficients_; } /** \brief Hand out a LocalInterpolation object */ const BSplineLocalInterpolation >& localInterpolation() const { return localInterpolation_; } /** \brief Number of shape functions in this finite element */ unsigned size () const { std::size_t r = 1; for (int i=0; i (preBasis_.knotVectors_[i].size() - currentKnotSpan_[i] - 2) ) r -= order[i] - (preBasis_.knotVectors_[i].size() - currentKnotSpan_[i] - 2); return r; } const BSplinePreBasis& preBasis_; BSplineLocalBasis localBasis_; BSplineLocalCoefficients localCoefficients_; BSplineLocalInterpolation > localInterpolation_; // The knot span we are bound to std::array currentKnotSpan_; }; template class BSplineNode; /** \brief Pre-basis for B-spline basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The GridView that the space is defined on * * The BSplinePreBasis can be used to embed a BSplineBasis * in a larger basis for the construction of product spaces. */ template class BSplinePreBasis : public LeafPreBasisMixin< BSplinePreBasis > { using Base = LeafPreBasisMixin< BSplinePreBasis >; static const int dim = GV::dimension; /** \brief Simple dim-dimensional multi-index class */ class MultiDigitCounter { public: /** \brief Constructs a new multi-index, and sets all digits to zero * \param limits Number of different digit values for each digit, i.e., digit i counts from 0 to limits[i]-1 */ MultiDigitCounter(const std::array& limits) : limits_(limits) { std::fill(counter_.begin(), counter_.end(), 0); } /** \brief Increment the multi-index */ MultiDigitCounter& operator++() { for (int i=0; i limits_; /** \brief The values of the multi-index. Each array entry is one digit */ std::array counter_; }; public: /** \brief The grid view that the FE space is defined on */ using GridView = GV; using size_type = std::size_t; using Node = BSplineNode; // Type used for function values using R = double; /** \brief Construct a B-spline basis for a given grid view and set of knot vectors * * The grid *must* match the knot vectors, i.e.: * - The grid must be structured and Cartesian, and have cube elements only * - The number of elements in each direction must match the number of knot spans in that direction * - In fact, the element spacing in any direction must match the knot spacing in that direction * (disregarding knot multiplicities) * - When ordering the grid elements according to their indices, the resulting order must * be lexicographical, with the x-index increasing fastest. * * Unfortunately, not all of these conditions can be checked for automatically. * * \param gridView The grid we are defining the basis on. * \param knotVector A single knot vector, which will be used for all coordinate directions * \param order B-spline order, will be used for all coordinate directions * \param makeOpen If this is true, then knots are prepended and appended to the knot vector to make the knot vector 'open'. * i.e., start and end with 'order+1' identical knots. Basis functions from such knot vectors are interpolatory at * the end of the parameter interval. */ BSplinePreBasis(const GridView& gridView, const std::vector& knotVector, unsigned int order, bool makeOpen = true) : gridView_(gridView) { // \todo Detection of duplicate knots std::fill(elements_.begin(), elements_.end(), knotVector.size()-1); // Mediocre sanity check: we don't know the number of grid elements in each direction. // but at least we know the total number of elements. assert( std::accumulate(elements_.begin(), elements_.end(), 1, std::multiplies()) == gridView_.size(0) ); for (int i=0; i& lowerLeft, const FieldVector& upperRight, const std::array& elements, unsigned int order, bool makeOpen = true) : elements_(elements), gridView_(gridView) { // Mediocre sanity check: we don't know the number of grid elements in each direction. // but at least we know the total number of elements. assert( std::accumulate(elements_.begin(), elements_.end(), 1, std::multiplies()) == gridView_.size(0) ); for (int i=0; i It indices(const Node& node, It it) const { // Local degrees of freedom are arranged in a lattice. // We need the lattice dimensions to be able to compute lattice coordinates from a local index std::array localSizes; for (int i=0; i localIJK = getIJK(i, localSizes); const auto currentKnotSpan = node.finiteElement().currentKnotSpan_; const auto order = order_; std::array globalIJK; for (int i=0; i=0; i--) globalIdx = globalIdx * size(i) + globalIJK[i]; *it = {{globalIdx}}; } return it; } //! \brief Total number of B-spline basis functions unsigned int dimension () const { unsigned int result = 1; for (size_t i=0; i& in, std::vector >& out, const std::array& currentKnotSpan) const { // Evaluate std::array, dim> oneDValues; for (size_t i=0; i limits; for (int i=0; i& in, std::vector >& out, const std::array& currentKnotSpan) const { // How many shape functions to we have in each coordinate direction? std::array limits; for (int i=0; i (knotVectors_[i].size() - currentKnotSpan[i] - 2) ) limits[i] -= order_[i] - (knotVectors_[i].size() - currentKnotSpan[i] - 2); } // The lowest knot spans that we need values from std::array offset; for (int i=0; i, dim> oneDValues; // Evaluate 1d function values of one order lower (needed for the derivative formula) std::array, dim> lowOrderOneDValues; std::array, dim> values; for (size_t i=0; i, dim> oneDDerivatives; for (size_t i=0; i, dim> oneDValuesShort; for (int i=0; i void evaluate(const typename std::array& directions, const FieldVector& in, std::vector >& out, const std::array& currentKnotSpan) const { if (k != 1 && k != 2) DUNE_THROW(RangeError, "Differentiation order greater than 2 is not supported!"); // Evaluate 1d function values (needed for the product rule) std::array, dim> oneDValues; std::array, dim> oneDDerivatives; std::array, dim> oneDSecondDerivatives; // Evaluate 1d function derivatives if (k==1) for (size_t i=0; i offset; for (int i=0; i limits; for (int i=0; i (knotVectors_[i].size() - currentKnotSpan[i] - 2) ) limits[i] -= order_[i] - (knotVectors_[i].size() - currentKnotSpan[i] - 2); } // Working towards computing only the parts that we really need: // Let's copy them out into a separate array std::array, dim> oneDValuesShort; for (int i=0; i getIJK(typename GridView::IndexSet::IndexType idx, std::array elements) { std::array result; for (int i=0; i& out, const std::vector& knotVector, unsigned int order, unsigned int currentKnotSpan) { std::size_t outSize = order+1; // The 'standard' value away from the boundaries of the knot vector if (currentKnotSpan (knotVector.size() - currentKnotSpan - 2) ) outSize -= order - (knotVector.size() - currentKnotSpan - 2); out.resize(outSize); // It's not really a matrix that is needed here, a plain 2d array would do DynamicMatrix N(order+1, knotVector.size()-1); // The text books on splines use the following geometric condition here to fill the array N // (see for example Cottrell, Hughes, Bazilevs, Formula (2.1). However, this condition // only works if splines are never evaluated exactly on the knots. // // for (size_t i=0; i 1e-10) ? (in - knotVector[i]) / (knotVector[i+r] - knotVector[i]) : 0; R factor2 = ((knotVector[i+r+1] - knotVector[i+1]) > 1e-10) ? (knotVector[i+r+1] - in) / (knotVector[i+r+1] - knotVector[i+1]) : 0; N[r][i] = factor1 * N[r-1][i] + factor2 * N[r-1][i+1]; } /** \todo We only hand out function values for those basis functions whose support overlaps * the current knot span. However, in the preceding loop we still computed _all_ values_. * This won't scale. */ for (size_t i=0; i& out, const std::vector& knotVector, unsigned int order, unsigned int currentKnotSpan) { // It's not really a matrix that is needed here, a plain 2d array would do DynamicMatrix& N = out; N.resize(order+1, knotVector.size()-1); // The text books on splines use the following geometric condition here to fill the array N // (see for example Cottrell, Hughes, Bazilevs, Formula (2.1). However, this condition // only works if splines are never evaluated exactly on the knots. // // for (size_t i=0; i 1e-10) ? (in - knotVector[i]) / (knotVector[i+r] - knotVector[i]) : 0; R factor2 = ((knotVector[i+r+1] - knotVector[i+1]) > 1e-10) ? (knotVector[i+r+1] - in) / (knotVector[i+r+1] - knotVector[i+1]) : 0; N[r][i] = factor1 * N[r-1][i] + factor2 * N[r-1][i+1]; } } /** \brief Evaluate the second derivatives of all one-dimensional B-spline functions for a given coordinate direction * * \param in Scalar(!) coordinate where to evaluate the functions * \param [out] out Vector containing the values of all B-spline derivatives at 'in' * \param [out] outJac Vector containing the first derivatives of all B-spline derivatives at 'in' (only if calculation was switched on by enableEvaluations) * \param [out] outHess Vector containing the second derivatives of all B-spline derivatives at 'in' (only if calculation was switched on by enableEvaluations) */ static void evaluateAll(const typename GV::ctype& in, std::vector& out, bool evaluateJacobian, std::vector& outJac, bool evaluateHessian, std::vector& outHess, const std::vector& knotVector, unsigned int order, unsigned int currentKnotSpan) { // How many shape functions to we have in each coordinate direction? unsigned int limit; limit = order+1; // The 'standard' value away from the boundaries of the knot vector if (currentKnotSpan (knotVector.size() - currentKnotSpan - 2) ) limit -= order - (knotVector.size() - currentKnotSpan - 2); // The lowest knot spans that we need values from unsigned int offset; offset = std::max((int)(currentKnotSpan - order),0); // Evaluate 1d function values (needed for the product rule) DynamicMatrix values; evaluateFunctionFull(in, values, knotVector, order, currentKnotSpan); out.resize(knotVector.size()-order-1); for (size_t j=0; j lowOrderOneDValues; if (order!=0) { lowOrderOneDValues.resize(knotVector.size()-(order-1)-1); for (size_t j=0; j lowOrderTwoDValues; if (order>1 && evaluateHessian) { lowOrderTwoDValues.resize(knotVector.size()-(order-2)-1); for (size_t j=0; j order_; /** \brief The knot vectors, one for each space dimension */ std::array, dim> knotVectors_; /** \brief Number of grid elements in the different coordinate directions */ std::array elements_; GridView gridView_; }; template class BSplineNode : public LeafBasisNode { static const int dim = GV::dimension; public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; using FiniteElement = BSplineLocalFiniteElement; BSplineNode(const BSplinePreBasis* preBasis) : preBasis_(preBasis), finiteElement_(*preBasis) {} //! Return current element, throw if unbound const Element& element() const { return element_; } /** \brief Return the LocalFiniteElement for the element we are bound to * * The LocalFiniteElement implements the corresponding interfaces of the dune-localfunctions module */ const FiniteElement& finiteElement() const { return finiteElement_; } //! Bind to element. void bind(const Element& e) { element_ = e; auto elementIndex = preBasis_->gridView().indexSet().index(e); finiteElement_.bind(preBasis_->getIJK(elementIndex,preBasis_->elements_)); this->setSize(finiteElement_.size()); } protected: const BSplinePreBasis* preBasis_; FiniteElement finiteElement_; Element element_; }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can create a B-spline pre-basis * * \ingroup FunctionSpaceBasesImplementations * */ inline auto bSpline(const std::vector& knotVector, unsigned int order, bool makeOpen = true) { return [&knotVector, order, makeOpen](const auto& gridView) { return BSplinePreBasis>(gridView, knotVector, order, makeOpen); }; } } // end namespace BasisFactory // ***************************************************************************** // This is the actual global basis implementation based on the reusable parts. // ***************************************************************************** /** \brief A global B-spline basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The GridView that the space is defined on */ template using BSplineBasis = DefaultGlobalBasis >; } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BSPLINEBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/compositebasis.hh000066400000000000000000000352071513634022200276470ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_COMPOSITEBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_COMPOSITEBASIS_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { // ***************************************************************************** // This is the reusable part of the composite bases. It contains // // CompositePreBasis // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** /** * \brief A pre-basis for composite bases * * \ingroup FunctionSpaceBasesImplementations * * This pre-basis represents a composition of several given pre-bases. * Its node type is a CompositeBasisNodes for the given subnodes. * * \tparam IMS An IndexMergingStrategy used to merge the global indices of the child pre-bases * \tparam SPB The child pre-bases */ template class CompositePreBasis { static const bool isBlocked = std::is_same_v or std::is_same_v; public: //! Tuple of child pre-bases using SubPreBases = std::tuple; //! Export individual child pre-bases by index template using SubPreBasis = std::tuple_element_t; //! The grid view that the FE basis is defined on using GridView = typename std::tuple_element_t<0, SubPreBases>::GridView; //! Type used for indices and size information using size_type = std::size_t; //! Strategy used to merge the global indices of the child pre-bases using IndexMergingStrategy = IMS; protected: static const std::size_t children = sizeof...(SPB); using ChildIndices = std::make_index_sequence; public: //! Template mapping root tree path to type of created tree node using Node = CompositeBasisNode; static constexpr size_type maxMultiIndexSize = std::max({SPB::maxMultiIndexSize...}) + isBlocked; static constexpr size_type minMultiIndexSize = std::min({SPB::minMultiIndexSize...}) + isBlocked; static constexpr size_type multiIndexBufferSize = std::max({SPB::multiIndexBufferSize...}) + isBlocked; /** * \brief Constructor for given child pre-basis objects * * The child pre-basis will be stored as copies */ template = 0, enableIfConstructible, SFArgs...> = 0> CompositePreBasis(SFArgs&&... sfArgs) : subPreBases_(std::forward(sfArgs)...) { Hybrid::forEach(subPreBases_, [&](const auto& subPreBasis){ static_assert(models, std::decay_t>(), "Subprebases passed to CompositePreBasis does not model the PreBasis concept."); }); } /** * \brief Constructor for given GridView * * This constructor is only available if all child pre-bases are constructible * from the grid view. */ template 1)>, // Avoid ambiguous constructor if there's only one child std::is_same, std::is_constructible... >, int> = 0> CompositePreBasis(const GV& gv) : subPreBases_(SPB(gv)...) { Hybrid::forEach(subPreBases_, [&](const auto& subPreBasis){ static_assert(models, std::decay_t>(), "Subprebases passed to CompositePreBasis does not model the PreBasis concept."); }); } //! Initialize the global indices void initializeIndices() { Hybrid::forEach(ChildIndices(), [&](auto i) { this->subPreBasis(i).initializeIndices(); }); } //! Obtain the grid view that the basis is defined on const GridView& gridView() const { return std::get<0>(subPreBases_).gridView(); } //! Update the stored grid view, to be called if the grid has changed void update(const GridView& gv) { Hybrid::forEach(ChildIndices(), [&](auto i) { this->subPreBasis(i).update(gv); }); } /** * \brief Create tree node */ Node makeNode() const { auto node = Node{}; Hybrid::forEach(ChildIndices(), [&](auto i) { node.setChild(this->subPreBasis(i).makeNode(), i); }); return node; } //! Same as size(prefix) with empty prefix size_type size() const { return size(Dune::ReservedVector{}); } //! Return number of possible values for next position in multi index template size_type size(const SizePrefix& prefix) const { return size(prefix, IndexMergingStrategy{}); } private: template static void multiIndexPopFront(MultiIndex& M) { for(std::size_t i=0; i size_type size(SizePrefix prefix, BasisFactory::BlockedLexicographic) const { if (prefix.size() == 0) return children; auto front = prefix.front(); multiIndexPopFront(prefix); return Hybrid::switchCases(ChildIndices(), front, [&] (auto i) { return this->subPreBasis(i).size(prefix); }, []() { return size_type(0); }); } template size_type size(SizePrefix prefix, BasisFactory::FlatLexicographic) const { size_type result = 0; if (prefix.size() == 0) Hybrid::forEach(ChildIndices(), [&](auto i) { result += this->subPreBasis(i).size(); }); else { staticFindInRange<0, children>([&](auto i) { auto firstDigitSize = this->subPreBasis(i).size(); if (prefix[0] < firstDigitSize) { result = this->subPreBasis(i).size(prefix); return true; } prefix[0] -= firstDigitSize; return false; }); } return result; } public: //! Get the total dimension of the space spanned by this basis size_type dimension() const { size_type r=0; // Accumulate dimension() for all subprebases Hybrid::forEach(ChildIndices(), [&](auto i) { r += this->subPreBasis(i).dimension(); }); return r; } //! Get the maximal number of DOFs associated to node for any element size_type maxNodeSize() const { size_type r=0; // Accumulate maxNodeSize() for all subprebases Hybrid::forEach(ChildIndices(), [&](auto i) { r += this->subPreBasis(i).maxNodeSize(); }); return r; } //! Const access to the stored prebasis of the factor in the power space template const SubPreBasis& subPreBasis(Dune::index_constant = {}) const { return std::get(subPreBases_); } //! Mutable access to the stored prebasis of the factor in the power space template SubPreBasis& subPreBasis(Dune::index_constant = {}) { return std::get(subPreBases_); } //! Const access to the stored prebases tuple const auto& subPreBases() const { return subPreBases_; } //! Maps from subtree index set [0..size-1] to a globally unique multi index in global basis template It indices(const Node& node, It it) const { return indices(node, it, IndexMergingStrategy{}); } //! Return the associated container descriptor auto containerDescriptor() const { namespace CD = Dune::Functions::ContainerDescriptors; if constexpr(std::is_same_v) { return std::apply([&](auto const&... spb) { return CD::makeDescriptor(Dune::Functions::containerDescriptor(spb)...); }, subPreBases_); } else if constexpr(std::is_same_v) { return std::apply([&](auto const&... spb) { return CD::Impl::flatLexicographic(Dune::Functions::containerDescriptor(spb)...); }, subPreBases_); } else return CD::Unknown{}; } private: template It indices(const Node& node, It multiIndices, BasisFactory::FlatLexicographic) const { size_type firstComponentOffset = 0; // Loop over all children Hybrid::forEach(ChildIndices(), [&](auto child){ size_type subTreeSize = node.child(child).size(); // Fill indices for current child into index buffer starting from current // buffer position and shift first index component of any index for current // child by suitable offset to get lexicographic indices. subPreBasis(child).indices(node.child(child), multiIndices); for (std::size_t i = 0; i static void multiIndexPushFront(MultiIndex& M, size_type M0) { M.resize(M.size()+1); for(std::size_t i=M.size()-1; i>0; --i) M[i] = M[i-1]; M[0] = M0; } template It indices(const Node& node, It multiIndices, BasisFactory::BlockedLexicographic) const { // Loop over all children Hybrid::forEach(ChildIndices(), [&](auto child){ size_type subTreeSize = node.child(child).size(); // Fill indices for current child into index buffer starting from current position subPreBasis(child).indices(node.child(child), multiIndices); // Insert child index before first component of all indices of current child. for (std::size_t i = 0; imultiIndexPushFront(multiIndices[i], child); // Increment buffer iterator by the number of indices processed for current child multiIndices += subTreeSize; }); return multiIndices; } std::tuple subPreBases_; }; namespace BasisFactory { namespace Imp { template class CompositePreBasisFactory { template auto makePreBasisFromChildPreBases(const GridView&, ChildPreBasis&&... childPreBasis) const { return CompositePreBasis...>(std::forward(childPreBasis)...); } public: CompositePreBasisFactory(const ChildPreBasisFactory&... childPreBasisFactory) : childPreBasisFactories_(childPreBasisFactory...) {} CompositePreBasisFactory(ChildPreBasisFactory&&... childPreBasisFactory) : childPreBasisFactories_(std::move(childPreBasisFactory)...) {} template auto operator()(const GridView& gridView) const { // Use std::apply to unpack the tuple childPreBasisFactories_ return std::apply([&](const auto&... childPreBasisFactory) { return this->makePreBasisFromChildPreBases(gridView, childPreBasisFactory(gridView)...); }, childPreBasisFactories_); } private: std::tuple childPreBasisFactories_; }; } // end namespace BasisFactory::Imp /** * \brief Create a factory builder that can build a CompositePreBasis * * \ingroup FunctionSpaceBasesImplementations * * \tparam Args Types of child factory builders and IndexMergingStrategy type * \param args Child factory builder objects and an IndexMergingStrategy * * This is the overload used if the last argument is an IndexMergingStrategy. */ template< typename... Args, std::enable_if_t::type>(),int> = 0> auto composite(Args&&... args) { // We have to separate the last entry which is the IndexMergingStrategy // and the preceding ones, which are the ChildPreBasisFactories using ArgTuple = std::tuple...>; // Compute number of children and index of the IndexMergingStrategy argument constexpr std::size_t children = sizeof...(Args) - 1; // Use last type as IndexMergingStrategy using IndexMergingStrategy = std::tuple_element_t; // Index sequence for all but the last entry for partial tuple unpacking auto childIndices = std::make_index_sequence{}; // Unpack tuple only for those entries related to children return applyPartial([](auto&&... childPreBasisFactory){ return Imp::CompositePreBasisFactory...>(std::forward(childPreBasisFactory)...); }, std::forward_as_tuple(std::forward(args)...), childIndices); } /** * \brief Create a factory builder that can build a CompositePreBasis * * \ingroup FunctionSpaceBasesImplementations * * \tparam Args Types of child factory builders * \param args Child factory builder objects * * This is the overload used if no IndexMergingStrategy is supplied. * In this case the BasisFactory::BlockedLexicographic strategy is used. */ template< typename... Args, std::enable_if_t::type>(),int> = 0> [[deprecated("Using the method `composite` without an explicit index merging strategy" " will change its meaning after the release of dune-functions 2.11." " Previously, the default merging strategy was `BlockedLexicographic`," " but this will change to `FlatLexicographic`." " Change the call to `composite(..., blockedLexicographic())` to retain the old behavior.")]] auto composite(Args&&... args) { return Imp::CompositePreBasisFactory...>(std::forward(args)...); } } // end namespace BasisFactory } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_COMPOSITEBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/concepts.hh000066400000000000000000000225571513634022200264450ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_CONCEPTS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_CONCEPTS_HH #include #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { namespace ContainerDescriptors { /* forward declaration */ struct Unknown; } namespace Concept { using namespace Dune::Concept; struct HasResize { template auto require(C&& c) -> decltype( c.resize(0) ); }; struct HasSizeMethod { template auto require(C&& c) -> decltype( c.size() ); }; struct HasIndexAccess { template auto require(C&& c, I&& i) -> decltype( c[i] ); }; // Concept for a container descriptor describing the structure of an index tree struct ContainerDescriptor { private: template auto expandChilds(const CD& cd, std::index_sequence) -> decltype( (requireConcept(cd[std::integral_constant{}]),...) ); // static size overload template auto children(const CD& cd, Dune::PriorityTag<1>) -> decltype( expandChilds(cd, std::make_index_sequence{}) ); // dynamic size overload template auto children(const CD& cd, Dune::PriorityTag<0>) -> decltype( requireConcept(cd[0]) ); public: // specialization for the fallback type void require(const Dune::Functions::ContainerDescriptors::Unknown&); template auto require(const CD& cd) -> decltype( requireConvertible(cd.size()), children(cd, Dune::PriorityTag<2>{}) ); }; // Concept for a BasisNode in a local ansatz tree struct BasisNode { template auto require(const N& node) -> decltype( requireType(), requireConvertible(node.size()), requireConvertible(node.localIndex(std::declval())), requireConvertible(node.treeIndex()), requireBaseOf() ); }; // Concept for a LeafBasisNode in a local ansatz tree template struct LeafBasisNode : Refines { template auto require(const N& node) -> decltype( requireType(), requireType(), requireConvertible(node.element()), requireConvertible(node.finiteElement()), requireSameType::Entity>(), requireBaseOf() ); }; template struct BasisTree; // Concept for a PowerBasisNode in a local ansatz tree template struct PowerBasisNode : Refines { template auto require(const N& node) -> decltype( requireBaseOf, N::degree()>, N>(), requireConcept, Dune::TypeTree::Child>() ); }; // Concept for a DynamicPowerBasisNode in a local ansatz tree template struct DynamicPowerBasisNode : Refines { template auto require(const N& node) -> decltype( requireBaseOf>, N>(), requireConcept, Dune::TypeTree::Child>() ); }; // Concept for a CompositeBasisNode in a local ansatz tree template struct CompositeBasisNode : Refines { template auto require(const N& node) -> decltype( requireBaseOf>, N>(), requireConceptForTupleEntries, Dune::TypeTree::Impl::Children>() ); }; // Concept for a full local BasisTree template struct BasisTree : Refines { template static constexpr bool hasStaticDegree() { return requires() { N::degree(); }; } template auto require(const N& node) -> decltype( requireConcept, LeafBasisNode, BasisNode>, N>(), requireConcept and hasStaticDegree(), PowerBasisNode, BasisNode>, N>(), requireConcept and not hasStaticDegree(), DynamicPowerBasisNode, BasisNode>, N>(), requireConcept and (not Dune::TypeTree::Concept::UniformInnerTreeNode), CompositeBasisNode, BasisNode>, N>() ); }; // Concept for a PreBasis template struct PreBasis { private: template using MultiIndex = Dune::ReservedVector; public: template auto require(const PB& preBasis) -> decltype( requireType(), requireType(), requireType(), requireConvertible(), requireConvertible(), requireConvertible(), requireTrue(), requireTrue(), requireSameType(), const_cast(preBasis).initializeIndices(), requireConvertible(preBasis.gridView()), requireConvertible(preBasis.makeNode()), requireConvertible(preBasis.size()), requireConvertible(preBasis.size(std::declval>())), requireConvertible(preBasis.dimension()), requireConvertible(preBasis.maxNodeSize()), requireSameType(preBasis).update(preBasis.gridView())),void>(), requireConcept>(preBasis.makeNode()), requireConvertible>::iterator>( preBasis.indices( preBasis.makeNode(), std::declval>::iterator>())) ); }; // Concept for a LocalView template struct LocalView { template auto require(const V& localView) -> decltype( requireType(), requireType(), requireType(), requireType(), requireType(), requireType(), requireSameType(), requireSameType(), requireSameType(), requireSameType::Entity>(), const_cast(localView).bind(std::declval()), const_cast(localView).unbind(), requireConvertible(localView.bound()), requireConvertible(localView.tree()), requireConvertible(localView.size()), requireConvertible(localView.index(std::declval())), requireConvertible(localView.maxSize()), requireConvertible(localView.globalBasis()), requireConcept>(localView.tree()), 0 ); }; // Concept for a GlobalBasis template struct GlobalBasis { template auto require(const B& basis) -> decltype( requireType(), requireType(), requireType(), requireType(), requireType(), requireSameType(), requireConvertible(basis.gridView()), requireConvertible(basis.localView()), requireConvertible(basis.size()), requireConvertible(basis.size(std::declval())), requireConvertible(basis.dimension()), requireSameType(basis).update(basis.gridView())),void>(), requireConcept>(basis.localView()), requireConcept(basis.containerDescriptor()) ); }; } // namespace Dune::Functions::Concept } // namespace Dune::Functions } // namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_CONCEPTS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/containerdescriptors.hh000066400000000000000000000316041513634022200310640ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_CONTAINERDESCRIPTORS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_CONTAINERDESCRIPTORS_HH #include #include #include #include #include #include #include #include #include #include #include #include /** * \file * \brief Lightweight representation of (hierarchical) size and block structure extracted * from a basis to describe data structures like containers that can be accessed by * multi-indices provided by the basis. * * The structure of a container-descriptor is a reduced container interface: * \code struct [Container]Descriptor { template [SubContainerDescriptor] operator[](Index i) const; // return the i-th sub-container-descriptor [static constexpr] std::size_t size() [const]; // return the number of children }; * \endcode * * With the `operator[]` you can access the children. The `Index` type is either * an integral value or an `integral_constant` for tuple nodes. * * Size is either a static property, or a runtime value. **/ namespace Dune::Functions { namespace ContainerDescriptors { //! Fallback container descriptor if nothing else fits struct Unknown {}; } // end namespace ContainerDescriptors //! Return the container descriptor of the pre-basis, if defined, otherwise ContainerDescriptor::Unknown template auto containerDescriptor(const PreBasis& preBasis) { if constexpr (requires{ preBasis.containerDescriptor(); }) return preBasis.containerDescriptor(); else return ContainerDescriptors::Unknown{}; } namespace ContainerDescriptors { //! The node in the descriptor tree representing a value placeholder struct Value { //! The child access method is only available for the interface, but should not be called. template Value operator[] (const Index&) const { return {}; } //! A value placeholder does not have any sub-descriptors, thus its size is zero. static constexpr std::size_t size () { return 0; } }; //! Descriptor with all children of possibly different type template using Tuple = Dune::TupleVector; //! Generate a descriptor in case the children are not all of the same type. //! \relates Dune::TupleVector template 0), int> = 0, std::enable_if_t<(...|| (not std::is_same_v)), int> = 0> auto makeDescriptor (Child0 child0, Children... children) { using Descriptor = Tuple; return Descriptor{std::move(child0),std::move(children)...}; } //! Descriptor for arrays with all children of the same type and static size. template using Array = std::array; //! Generate a descriptor in case the children are all of the same type. template &&...), int> = 0> auto makeDescriptor (Child0 child, Children... children) { using Descriptor = Array; return Descriptor{std::move(child),std::move(children)...}; } //! Descriptor for vectors with all children of the same type and dynamic size. template using Vector = std::vector; //! Descriptor for arrays with all children identical and the number of children a static size. template struct UniformArray { //! Default constructor. Is enable if the child-type is default constructible. template, int> = 0> UniformArray () : child_{} {} //! Constructor that stores a single child only. explicit UniformArray (Child child) : child_{std::move(child)} {} //! Access the i'th child that is always the same, i.e., `child_`. template const Child& operator[] (const Index& /*i*/) const { return child_; } //! The static size information, i.e., number of children. static constexpr std::size_t size () { return n; } private: Child child_; }; //! Alias for a uniform array storing value placeholders template using FlatArray = UniformArray; //! Generate a uniform descriptor in case the size is a static constant template auto makeUniformDescriptor (std::integral_constant, Child child) { return UniformArray{std::move(child)}; } //! Uniform descriptor with dynamic size. template struct UniformVector { //! Default constructor with size. Is enable if the child-type is default constructible. template, int> = 0> explicit UniformVector (std::size_t size) : size_{size} , child_{} {} //! Constructor that stores the size and a single child only. UniformVector (std::size_t size, Child child) : size_{size} , child_{std::move(child)} {} //! Access the i'th child that is always the same, i.e., `child_`. template const Child& operator[] (const Index& /*i*/) const { return child_; } //! The dynamic size information, i.e., number of children. std::size_t size () const { return size_; } private: std::size_t size_; Child child_; }; //! Alias for a uniform vector storing value placeholders using FlatVector = UniformVector; //! Generate a uniform descriptor in case the size is a dynamic value template auto makeUniformDescriptor (std::size_t n, Child child) { return UniformVector{n,std::move(child)}; } namespace Impl { template struct TreeTransform { TreeTransform (const InnerFunc& innerFunc, const LeafFunc& leafFunc) : innerFunc_(innerFunc) , leafFunc_(leafFunc) {} Unknown operator() (const Unknown& tree) const { return tree; } auto operator() (const Value& tree) const { return leafFunc_(tree); } template auto operator() (const Tuple& tree) const { return unpackIntegerSequence([&](auto... ii) { return makeDescriptor(innerFunc_(tree[ii])...); }, std::make_index_sequence()); } template auto operator() (const Array& tree) const { return unpackIntegerSequence([&](auto... ii) { return makeDescriptor(innerFunc_(tree[ii])...); }, std::make_index_sequence()); } template auto operator() (const Vector& tree) const { using W = decltype(innerFunc_(tree[0])); Vector result; result.reserve(tree.size()); for (std::size_t i = 0; i < tree.size(); ++i) result.emplace_back(innerFunc_(tree[i])); return result; } template auto operator() (const UniformArray& tree) const { return makeUniformDescriptor(Dune::index_constant{}, innerFunc_(tree[0])); } template auto operator() (const UniformVector& tree) const { return makeUniformDescriptor(tree.size(), innerFunc_(tree[0])); } private: InnerFunc innerFunc_; LeafFunc leafFunc_; }; /** * Append a size to the leaf-nodes of the tree * * This transform of the given tree is used to implement * a blocked-interleaved index-merging strategy in a power-basis. * * Examples: * append( Flat[Container] c, size ) -> Uniform[Container]( c.size(), Flat[Container](size) ) * append( Descriptor(child...), size ) -> Descriptor( append(child, size)... ) */ template auto appendToTree (Size s, const T& tree) { auto transform = TreeTransform( [s](auto&& node) { return appendToTree(s, node); }, [s](auto&& node) { return makeUniformDescriptor(s, node); }); return transform(tree); } //! Concatenate n descriptors by flat lexicographic merging wrt first digit template auto flatLexicographic (Child... child) { if constexpr ((std::is_same_v && ...)) return FlatVector((child.size() + ...)); else return Unknown{}; } //! Concatenate descriptor n-times by flat lexicographic merging wrt first digit template auto flatLexicographicN (N n, Child child) { return Unknown{}; } //! Concatenate descriptor n-times by flat lexicographic merging wrt first digit template auto flatLexicographicN (N n, UniformVector child) { return UniformVector{child.size()*n, child[0]}; } //! Concatenate descriptor n-times by flat lexicographic merging wrt first digit template auto flatLexicographicN (N n, UniformArray child) { if constexpr (std::is_same_v) return UniformVector{n*m, child[0]}; else return UniformArray{child[0]}; } //! Concatenate descriptor n-times by flat lexicographic merging wrt first digit template auto flatLexicographicN (N n, Vector child) { auto result = Vector{}; result.reserve(child.size()*n); for (auto j : Dune::range(n)) for (auto i : Dune::range(child.size())) result.emplace_back(child[i]); return result; } //! Concatenate descriptor n-times by flat lexicographic merging wrt first digit template auto flatLexicographicN (N n, Array child) { if constexpr (std::is_same_v) { auto result = Vector{}; result.reserve(child.size()*n); for (auto j : Dune::range(n)) for (auto i : Dune::range(child.size())) result.emplace_back(child[i]); return result; } else { auto result = Array{}; for (auto j : Dune::range(n)) for (auto i : Dune::range(child.size())) result.emplace_back(child[i]); return result; } } //! Concatenate descriptor n-times by flat lexicographic merging wrt first digit template auto flatLexicographicN (N n, Tuple child) { constexpr std::size_t m = sizeof...(GrandChild); return Dune::unpackIntegerSequence([&](auto... i) { return makeDescriptor(std::get(child)...); }, std::make_index_sequence{}); } //! Concatenate descriptor n-times by flat interleaved merging wrt first digit template auto flatInterleavedN (N n, Child child) { return Unknown{}; } //! Concatenate descriptor n-times by flat interleaved merging wrt first digit template auto flatInterleavedN (N n, UniformVector child) { return UniformVector{child.size()*n, child[0]}; } //! Concatenate descriptor n-times by flat interleaved merging wrt first digit template auto flatInterleavedN (N n, UniformArray child) { if constexpr (std::is_integral_v) return UniformVector{n*m, child[0]}; else return UniformArray{child[0]}; } //! Concatenate descriptor n-times by flat interleaved merging wrt first digit template auto flatInterleavedN (N n, Vector child) { auto result = Vector{}; result.reserve(child.size()*n); for (auto i : Dune::range(child.size())) for (auto j : Dune::range(n)) result.emplace_back(child[i]); return result; } //! Concatenate descriptor n-times by flat interleaved merging wrt first digit template auto flatInterleavedN (N n, Array child) { if constexpr (std::is_integral_v) { auto result = Vector{}; result.reserve(child.size()*n); for (auto i : Dune::range(child.size())) for (auto j : Dune::range(n)) result.emplace_back(child[i]); return result; } else { auto result = Array{}; for (auto i : Dune::range(child.size())) for (auto j : Dune::range(n)) result.emplace_back(child[i]); return result; } } //! Concatenate descriptor n-times by flat interleaved merging wrt first digit template auto flatInterleavedN (N n, Tuple child) { constexpr std::size_t m = sizeof...(GrandChild); return Dune::unpackIntegerSequence([&](auto... i) { return makeDescriptor(std::get(child)...); }, std::make_index_sequence{}); } } // end namespace Impl } // end namespace ContainerDescriptors } // end namespace Dune::Functions #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_CONTAINERDESCRIPTORS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/cubichermitebasis.hh000066400000000000000000001024411513634022200303030ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_CUBICHERMITEBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_CUBICHERMITEBASIS_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * \file cubichermitebasis.hh * \brief This file provides an implementation of the cubic Hermite finite element in 1 to 3 dimensions. * * For reference, see[Ciarlet, The Finite Element Method for Elliptic Problems, 2002]. * It contains in the following order: * - A GlobalBasis typedef CubicHermiteBasis * - A template H2LocalBasisTraits, extending the dune-localfunctions * LocalBasisTraits by an exported Hessiantype * - A template CubicHermiteLocalFiniteElement providing an implementation * of the LocalFiniteElement interface, along with its subparts (Impl namespace) * - A template CubicHermiteNode * - A template CubicHermitePreBasis * - Two factories hermite() and reducedHermite() in the BasisFactory namespace */ namespace Dune::Functions { template class CubicHermitePreBasis; /** \brief Nodal basis of a scalar cubic Hermite finite element space * * \ingroup FunctionSpaceBasesImplementations * * \note This only works for simplex grids. The Hermite basis is only implemented for degree 3. * \note The Hermite Finite element has the following properties: * - Its global space is in \f$ C^1 \f$ if 1d otherwise in \f$ H^1 \f$. * - The reduced version (only 2d) is part of the Discrete Kirchhoff Triangle. * - Its interpolation evaluates derivatives, i.e. you cannot interpolate into a lambda function. * - Strongly enforcing boundary conditions is not as simple as with langrange bases * - It global space is not nested, i.e. the space on a refined grid is not a subspace of the * space on the coarser grid. * All arguments passed to the constructor will be forwarded to the constructor * of CubicHermitePreBasis. * * \tparam GV The GridView that the space is defined on * \tparam reduced whether to use the reduced Hermite finite element (only in 2d). * \tparam R The range type of the local basis */ template using CubicHermiteBasis = DefaultGlobalBasis >; template struct H2LocalBasisTraits : public LocalBasisTraits { /** \brief Type to represent the Hessian * When \f$ \hat\phi : \mbox{IR}^n \to \mbox{IR}\f$ then HessianType * is an 2D-array of m x m components where entry H[i][j] contains * the derivative \f$\partial_i \partial_j \hat\phi \f$. */ using HessianType = H; }; namespace Impl { // ***************************************************************************** // * Some helper functions for building polynomial bases from monomials // ***************************************************************************** /** \brief Multiply the evaluations of the monomialSet (see dune/functions/analyticfunctions/monomialset.hh) with a coefficient matrix, here FieldMatrix. * \tparam KCoeff The Field type of the coefficient matrix. * \tparam sizePolynom The number of polynomials to evaluate. * \tparam sizeMonom The number of monomials handed as input. * \tparam In input vector. We only assume container access and a correct size. * \tparam Out Output vector. We only assume container access and a correct size. * \note This function may be used to turn the return types from the dune-functions::DifferentiableFunction * interface into those of the dune-localfunctions::LocalBasis interface. */ template void multiplyWithCoefficentMatrix(Dune::FieldMatrix const& coefficients, In const& monomialValues, Out& polynomialValues) { for (int i = 0; i < sizePolynom; ++i) { squeezeTensor(polynomialValues[i]) = 0; for (int j = 0; j < sizeMonom; ++j) squeezeTensor(polynomialValues[i]) += coefficients[i][j]*monomialValues[j]; } } /** \brief Associations of the Hermite degrees of freedom to subentities of the * reference simplex * * \tparam dim Dimension of the reference simplex */ template class CubicHermiteLocalCoefficients { public: using size_type = std::size_t; CubicHermiteLocalCoefficients() : localKeys_(size()) { static_assert((dim > 0) and (dim <= 3), "CubicHermiteLocalCoefficients only implemented for dim=1,2,3"); static_assert((not reduced) or (dim == 2), "Reduced version of CubicHermiteLocalCoefficients only implemented for dim=2"); for (size_type i = 0; i < (dim +1); ++i) { // dim derivatives + 1 evaluation dofs per vertex for (size_type k = 0; k < (dim +1); ++k) localKeys_[(dim +1) * i + k] = LocalKey(i, dim, k); } if constexpr (not reduced) { // 1 evaluation per element (2d) / facets (3d) for (size_type i = 0; i < (dim - 1) * (dim - 1); ++i) localKeys_[(dim +1) * (dim +1) + i] = LocalKey(i, (dim == 2) ? 0 : 1, 0); // inner dofs } } /** \brief number of coefficients */ static constexpr size_type size() { if constexpr (dim==1) return 4; if constexpr ((dim==2) and (reduced)) return 9; if constexpr ((dim==2) and (not reduced)) return 10; if constexpr (dim==3) return 20; return 0; } /** \brief get i'th index */ LocalKey const &localKey(size_type i) const { return localKeys_[i]; } private: std::vector localKeys_; }; /** \brief Implementation of hermite Polynomials * \tparam D Type to represent the field in the domain * \tparam R Type to represent the field in the range * \tparam dim Dimension of the domain simplex */ template class CubicHermiteReferenceLocalBasis { public: using Traits = H2LocalBasisTraits, R, 1, FieldVector, FieldMatrix, FieldMatrix>; private: /** * \brief Get the Hermite Coefficients Matrix * \return FieldMatrix * where size is the dimension of the cubic polynomial space * * This returns the basis transformation matrix from a monomial * basis to the Hermite basis on the reference domain. * I.e. the i-th row of the returned matrix contains the * coefficients of the i-th Hermite basis function with respect to a * monomial basis. The monomials are enumerated as in the * MonomialSet of the corresponding dimension and order. */ static constexpr auto getCubicHermiteCoefficients() { if constexpr (dim == 1) return Dune::FieldMatrix({{1, 0, -3, 2}, {0, 1, -2, 1}, {0, 0, 3, -2}, {0, 0, -1, 1}}); else if constexpr (dim == 2) { if constexpr (reduced) { auto w = std::array{1. / 3, 1. / 18, 1. / 18, 1. / 3, -1. / 9, 1. / 18, 1. / 3, 1. / 18, -1. / 9}; return Dune::FieldMatrix({ {1, 0, 0, -3, -13 + w[0] * 27, -3, 2, 13 - w[0] * 27, 13 - w[0] * 27, 2}, {0, 1, 0, -2, -3 + w[1] * 27, 0, 1, 3 - w[1] * 27, 2 - w[1] * 27, 0}, {0, 0, 1, 0, -3 + w[2] * 27, -2, 0, 2 - w[2] * 27, 3 - w[2] * 27, 1}, {0, 0, 0, 3, -7 + w[3] * 27, 0, -2, 7 - w[3] * 27, 7 - w[3] * 27, 0}, {0, 0, 0, -1, 2 + w[4] * 27, 0, 1, -2 - w[4] * 27, -2 - w[4] * 27, 0}, {0, 0, 0, 0, -1 + w[5] * 27, 0, 0, 2 - w[5] * 27, 1 - w[5] * 27, 0}, {0, 0, 0, 0, -7 + w[6] * 27, 3, 0, 7 - w[6] * 27, 7 - w[6] * 27, -2}, {0, 0, 0, 0, -1 + w[7] * 27, 0, 0, 1 - w[7] * 27, 2 - w[7] * 27, 0}, {0, 0, 0, 0, 2 + w[8] * 27, -1, 0, -2 - w[8] * 27, -2 - w[8] * 27, 1}, }); } else return Dune::FieldMatrix({ {1, 0, 0, -3, -13, -3, 2, 13, 13, 2}, {0, 1, 0, -2, -3, 0, 1, 3, 2, 0}, {0, 0, 1, 0, -3, -2, 0, 2, 3, 1}, // l_2 {0, 0, 0, 3, -7, 0, -2, 7, 7, 0}, {0, 0, 0, -1, 2, 0, 1, -2, -2, 0}, {0, 0, 0, 0, -1, 0, 0, 2, 1, 0}, {0, 0, 0, 0, -7, 3, 0, 7, 7, -2}, // l_6 {0, 0, 0, 0, -1, 0, 0, 1, 2, 0}, {0, 0, 0, 0, 2, -1, 0, -2, -2, 1}, {0, 0, 0, 0, 27, 0, 0, -27, -27, 0}}); // l_9, inner dof } else if constexpr (dim == 3) { return Dune::FieldMatrix({{1, 0, 0, 0, -3, -13, -3, -13, -13, -3, // deg 0 to 2 2, 13, 13, 2, 13, 33, 13, 13, 13, 2}, // deg 3 {0, 1, 0, 0,/*xx*/ -2, /*xy*/-3,/*yy*/ 0,/*xz*/ -3,/*yz*/ 0,/*zz*/ 0, 1, 3, 2, 0, 3, 4, 0, 2, 0, 0}, {0, 0, 1, 0, 0, -3, -2, 0, -3, 0, 0, 2, 3, 1, 0, 4, 3, 0, 2, 0}, {0, 0, 0, 1, 0, 0, 0, -3, -3, -2, 0, 0, 0, 0, 2, 4, 2, 3, 3, 1}, {0, 0, 0, 0, 3, -7, 0, -7, 0, 0, // l_4 -2, 7, 7, 0, 7, 7, 0, 7, 0, 0}, {0, 0, 0, 0, -1, 2, 0, 2, 0, 0, 1, -2, -2, 0, -2, -2, 0, -2, 0, 0}, {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, -7, 3, 0, -7, 0, // l_8 0, 7, 7, -2, 0, 7, 7, 0, 7, 0}, {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, -1, 0, 2, 0, 0, -2, -2, 1, 0, -2, -2, 0, -2, 0}, {0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, -7, -7, 3, // l_12 0, 0, 0, 0, 7, 7, 7, 7, 7, -2}, {0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0}, {0, 0, 0, 0, 0, 0, 0, 2, 2, -1, 0, 0, 0, 0, -2, -2, -2, -2, -2, 1}, // l_16, from here on inner dofs {0, 0, 0, 0, 0, 27, 0, 0, 0, 0, // bottom 0, -27, -27, 0, 0, -27, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 27, 0, 0, // front 0, 0, 0, 0, -27, -27, 0, -27, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 27, 0, // left 0, 0, 0, 0, 0, -27, -27, 0, -27, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // right 0, 0, 0, 0, 0, 27, 0, 0, 0, 0}}); } } static constexpr auto referenceBasisCoefficients = getCubicHermiteCoefficients(); static constexpr MonomialSet monomials = {}; public: CubicHermiteReferenceLocalBasis() { static_assert((dim > 0) and (dim <= 3), "CubicHermiteReferenceLocalBasis only implemented for dim=1,2,3"); static_assert((not reduced) or (dim == 2), "Reduced version of CubicHermiteReferenceLocalBasis only implemented for dim=2"); } /** The number of basis functions in the basis */ static constexpr unsigned int size() { return CubicHermiteLocalCoefficients::size(); } /** The polynomial order of the basis */ unsigned int order() const { return 3; } /** \brief Evaluate function values of all shape functions at a given point * * \param[in] in The evaluation point * \param[out] out Values of all shape functions at that point */ void evaluateFunction(const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto monomialValues = monomials(in); multiplyWithCoefficentMatrix(referenceBasisCoefficients, monomialValues, out); } /** \brief Evaluate Jacobians of all shape functions at a given point * * \param[in] in The evaluation point * \param[out] out Jacobians of all shape functions at that point */ void evaluateJacobian(const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto monomialValues = derivative(monomials)(in); multiplyWithCoefficentMatrix(referenceBasisCoefficients, monomialValues, out); } /** \brief Evaluate Hessians of all shape functions at a given point * * \param[in] in The evaluation point * \param[out] out Hessians of all shape functions at that point */ void evaluateHessian(const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto monomialValues = derivative(derivative(monomials))(in); multiplyWithCoefficentMatrix(referenceBasisCoefficients, monomialValues, out); } /** \brief Evaluate partial derivatives of all shape functions at a given point * * \param[in] order The partial derivative to be computed, as a multi-index * \param[in] in The evaluation point * \param[out] out Jacobians of all shape functions at that point */ void partial(std::array order, const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto totalOrder = std::accumulate(order.begin(), order.end(), 0); if (totalOrder == 0) evaluateFunction(in, out); else if (totalOrder == 1) { evaluateJacobian(in,jacobiansBuffer_); std::size_t which = std::max_element(order.begin(), order.end()) - order.begin(); for (auto i : Dune::range(size())) out[i] = jacobiansBuffer_[i][0][which]; } else if (totalOrder == 2) { evaluateHessian(in, hessianBuffer_); std::size_t first, second; first = std::max_element(order.begin(), order.end()) - order.begin(); if (order[first] == 2) second = first; else { order[first] = 0; second = std::max_element(order.begin(), order.end()) - order.begin(); } for (auto i : Dune::range(size())) out[i] = hessianBuffer_[i][first][second]; } else DUNE_THROW(RangeError, "partial() not implemented for given order"); } private: mutable std::vector jacobiansBuffer_; mutable std::vector hessianBuffer_; }; /** \brief Class that evaluates the push forwards of the global nodes of a * LocalFunction. It stretches the LocalInterpolation interface, because we * evaluate the derivatives of f. * */ template class CubicHermiteLocalInterpolation { using size_type = std::size_t; static constexpr unsigned int size() { return CubicHermiteLocalCoefficients::size(); } using FunctionalDescriptor = Dune::Functions::Impl::FunctionalDescriptor; public: CubicHermiteLocalInterpolation() { static_assert((dim > 0) and (dim <= 3), "CubicHermiteLocalInterpolation only implemented for dim=1,2,3"); static_assert((not reduced) or (dim == 2), "Reduced version of CubicHermiteLocalInterpolation only implemented for dim=2"); if constexpr (dim==1) { descriptors_[0] = FunctionalDescriptor(); descriptors_[1] = FunctionalDescriptor({1}); descriptors_[2] = FunctionalDescriptor(); descriptors_[3] = FunctionalDescriptor({1}); } if constexpr (dim==2) { descriptors_[0] = FunctionalDescriptor(); descriptors_[1] = FunctionalDescriptor({1,0}); descriptors_[2] = FunctionalDescriptor({0,1}); descriptors_[3] = FunctionalDescriptor(); descriptors_[4] = FunctionalDescriptor({1,0}); descriptors_[5] = FunctionalDescriptor({0,1}); descriptors_[6] = FunctionalDescriptor(); descriptors_[7] = FunctionalDescriptor({1,0}); descriptors_[8] = FunctionalDescriptor({0,1}); if (not reduced) descriptors_[9] = FunctionalDescriptor(); } if constexpr (dim==3) { descriptors_[0] = FunctionalDescriptor(); descriptors_[1] = FunctionalDescriptor({1,0,0}); descriptors_[2] = FunctionalDescriptor({0,1,0}); descriptors_[3] = FunctionalDescriptor({0,0,1}); descriptors_[4] = FunctionalDescriptor(); descriptors_[5] = FunctionalDescriptor({1,0,0}); descriptors_[6] = FunctionalDescriptor({0,1,0}); descriptors_[7] = FunctionalDescriptor({0,0,1}); descriptors_[8] = FunctionalDescriptor(); descriptors_[9] = FunctionalDescriptor({1,0,0}); descriptors_[10] = FunctionalDescriptor({0,1,0}); descriptors_[11] = FunctionalDescriptor({0,0,1}); descriptors_[12] = FunctionalDescriptor(); descriptors_[13] = FunctionalDescriptor({1,0,0}); descriptors_[14] = FunctionalDescriptor({0,1,0}); descriptors_[15] = FunctionalDescriptor({0,0,1}); descriptors_[16] = FunctionalDescriptor(); descriptors_[17] = FunctionalDescriptor(); descriptors_[18] = FunctionalDescriptor(); descriptors_[19] = FunctionalDescriptor(); } } /** \brief bind the Interpolation to an element and a local state. */ template void bind( Element const &element, std::arrayconst& averageVertexMeshSize) { averageVertexMeshSize_ = &averageVertexMeshSize; } /** \brief Evaluate a given function and its derivatives at the nodes * * \tparam F Type of function to evaluate * \tparam C Type used for the values of the function * \param[in] f Function to evaluate * \param[out] out Array of function values */ template void interpolate(const F &f, std::vector &out) const { out.resize(size()); auto df = derivative(f); auto const &refElement = Dune::ReferenceElements::simplex(); // Iterate over vertices, dim derivative +1 evaluation dofs per vertex for (int i = 0; i < (dim+1); ++i) { auto x = refElement.position(i, dim); auto&& derivativeValue = df(x); out[i * (dim +1)] = f(x); for (int d = 0; d < dim; ++d) out[i * (dim+1) + d + 1] = squeezeTensor(derivativeValue)[d] * (*averageVertexMeshSize_)[i]; } if constexpr (not reduced) { for (size_type i = 0; i < (dim - 1) * (dim - 1); ++i) out[(dim +1) * (dim +1) + i] = f(refElement.position(i, (dim == 2) ? 0 : 1)); } } /** * \brief Get the object that describes which type of evaluations is performed by the dual basis */ const FunctionalDescriptor& functionalDescriptor(size_type i) const { return descriptors_[i]; } protected: std::array const* averageVertexMeshSize_; std::array descriptors_; }; template struct CubicHermiteLocalBasisTraits : public H2LocalBasisTraits, R, 1, Dune::FieldVector, Dune::FieldMatrix, Dune::FieldMatrix> {}; /** \brief Hermite finite element for simplices, as defined on the reference Element. * Note, that this is a non affine-equivalent finite element, that requires an additional transformation to the relate reference basis with the pullbacks of global basis. * For more Details, see . * * \tparam D Type used for domain coordinates * \tparam R Type used for function values * \tparam dim dimension of the reference element */ template class CubicHermiteLocalFiniteElement : public Impl::TransformedFiniteElementMixin, CubicHermiteLocalBasisTraits> { using Base = Impl::TransformedFiniteElementMixin< CubicHermiteLocalFiniteElement, CubicHermiteLocalBasisTraits>; friend class Impl::TransformedLocalBasis, CubicHermiteLocalBasisTraits>; public: CubicHermiteLocalFiniteElement() : Base() { static_assert((dim > 0) and (dim <= 3), "CubicHermiteLocalFiniteElement only implemented for dim=1,2,3"); static_assert((not reduced) or (dim == 2), "Reduced version of CubicHermiteLocalFiniteElement only implemented for dim=2"); } /** \brief Export number types, dimensions, etc. */ using size_type = std::size_t; using Traits = LocalFiniteElementTraits< Impl::TransformedLocalBasis, CubicHermiteLocalBasisTraits>, Impl::CubicHermiteLocalCoefficients, Impl::CubicHermiteLocalInterpolation>; /** \brief Returns the assignment of the degrees of freedom to the element * subentities */ const typename Traits::LocalCoefficientsType &localCoefficients() const { return coefficients_; } /** \brief Returns object that evaluates degrees of freedom */ const typename Traits::LocalInterpolationType &localInterpolation() const { return interpolation_; } /** \brief The reference element that the local finite element is defined on */ static constexpr GeometryType type() { return GeometryTypes::simplex(dim); } /** The size of the transformed finite element. */ static constexpr size_type size() { return Impl::CubicHermiteLocalCoefficients::size(); } /** Binds the Finite Element to an element. */ template void bind(Mapper const& vertexMapper, std::vector const& globalAverageVertexMeshSize, Element const &e) { // Cache average mesh size for each vertex for (auto i : range(dim+1)) averageVertexMeshSize_[i] = globalAverageVertexMeshSize[vertexMapper.subIndex(e, i, dim)]; // Bind LocalInterpolation to updated local state interpolation_.bind(e, averageVertexMeshSize_); // Compute local transformation matrices for each vertex const auto& geometry = e.geometry(); const auto& refElement = Dune::ReferenceElements::simplex(); for (auto i : range(dim+1)) { scaledVertexJacobians_[i] = geometry.jacobian(refElement.position(i, dim)); scaledVertexJacobians_[i] /= averageVertexMeshSize_[i]; } } protected: /** \brief Returns the local basis, i.e., the set of shape functions */ Impl::CubicHermiteReferenceLocalBasis const& referenceLocalBasis() const { return basis_; } /** Applies the transformation. Note that we do not distinguish for * Scalar/Vector/Matrix Type, but only assume the Values to be Elements of a Vectorspace. * We assume containers with random access iterators. */ template void transform(InputValues const &inValues, OutputValues &outValues) const { assert(inValues.size() == size()); assert(outValues.size() == inValues.size()); auto inIt = inValues.begin(); auto outIt = outValues.begin(); for (auto vertex : Dune::range((dim +1))) { *outIt = *inIt; // value dof is not transformed outIt++, inIt++; // transform the gradient dofs together for (auto &&[row_i, i] : sparseRange(scaledVertexJacobians_[vertex])) { outIt[i] = 0.; for (auto &&[val_i_j, j] : sparseRange(row_i)) outIt[i] += val_i_j * inIt[j]; } // increase pointer by size of gradient = dim outIt += dim, inIt += dim; } // For the non-reduced case: Copy all remaining inner dofs if constexpr (dim > 1 and (not reduced)) std::copy(inIt, inValues.end(), outIt); } private: typename Impl::CubicHermiteReferenceLocalBasis basis_; typename Traits::LocalCoefficientsType coefficients_; typename Traits::LocalInterpolationType interpolation_; // the transformation to correct the lack of affine equivalence boils down to // one transformation matrix per vertex std::array, dim+1> scaledVertexJacobians_; // the local state, i.e. a collection of global information restricted to this element std::array averageVertexMeshSize_; }; } // end namespace Impl // ***************************************************************************** // This is the reusable part of the basis. It contains // // CubicHermitePreBasis // CubicHermiteNode // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template class CubicHermiteNode : public LeafBasisNode { using Mapper = Dune::MultipleCodimMultipleGeomTypeMapper; public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; using FiniteElement = typename Impl::CubicHermiteLocalFiniteElement; CubicHermiteNode(Mapper const& m, std::vector const& averageVertexMeshSize) : element_(nullptr) , vertexMapper_(&m) , averageVertexMeshSize_(&averageVertexMeshSize) {} //! Return current element, throw if unbound Element const &element() const { return *element_; } /** \brief Return the LocalFiniteElement for the element we are bound to * * The LocalFiniteElement implements the corresponding interfaces of the * dune-localfunctions module */ FiniteElement const &finiteElement() const { return finiteElement_; } //! Bind to element. void bind(Element const &e) { element_ = &e; finiteElement_.bind(*vertexMapper_, *averageVertexMeshSize_, *element_); this->setSize(finiteElement_.size()); } //! The order of the local basis. unsigned int order() const { return finiteElement_.localBasis().order(); } protected: FiniteElement finiteElement_; Element const* element_; Mapper const* vertexMapper_; std::vector const* averageVertexMeshSize_; }; /** * \brief A pre-basis for a Hermitebasis * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The grid view that the FE basis is defined on * \tparam R Range type used for shape function values * \note This only works for simplex grids */ template class CubicHermitePreBasis : public LeafPreBasisMapperMixin { using Base = LeafPreBasisMapperMixin; using SubEntityMapper = Dune::MultipleCodimMultipleGeomTypeMapper; using Element = typename GV::template Codim<0>::Entity; using D = typename GV::ctype; static const std::size_t dim = GV::dimension; // helper methods to assign each subentity the number of dofs. Used by the LeafPreBasisMapperMixin. static constexpr auto cubicHermiteMapperLayout(Dune::GeometryType type, int gridDim) { if (type.isVertex()) return 1 + gridDim; // one evaluation dof and gridDim derivative dofs per vertex if (gridDim == 1) // in 1d there are no other dofs return 0; // in 2d we have one inner dof (i.e. on the triangle) or non for the reduced case // and in 3d we have one dof on each face (i.e. on each triangle) if ((type.isTriangle()) and (not reduced)) return 1; else return 0; // this case is only entered for the interior of the 3d element. There are no dofs. } public: //! The grid view that the FE basis is defined on using GridView = GV; //! Type used for indices and size information using size_type = std::size_t; //! Template mapping root tree path to type of created tree node using Node = CubicHermiteNode; public: //! Constructor for a given grid view object CubicHermitePreBasis(const GV &gv) : Base(gv, cubicHermiteMapperLayout) , vertexMapper_({gv, mcmgVertexLayout()}) { static_assert((dim > 0) and (dim <= 3), "CubicHermitePreBasis only implemented for dim=1,2,3"); static_assert((not reduced) or (dim == 2), "Reduced version of CubicHermitePreBasis only implemented for dim=2"); averageVertexMeshSize_ = Impl::computeAverageSubEntityMeshSize(vertexMapper_); } //! Update the stored grid view, to be called if the grid has changed void update(GridView const &gv) { Base::update(gv); vertexMapper_.update(this->gridView()); averageVertexMeshSize_ = Impl::computeAverageSubEntityMeshSize(vertexMapper_); } /** * \brief Create tree node */ Node makeNode() const { return Node{vertexMapper_, averageVertexMeshSize_}; } protected: SubEntityMapper vertexMapper_; std::vector averageVertexMeshSize_; }; // class CubicHermitePreBasis namespace BasisFactory { /** * \brief construct a PreBasisFactory for the full cubic Hermite Finite Element * * \tparam R RangeFieldType * \return the PreBasisFactory * \relates CubicHermiteBasis */ template auto cubicHermite() { return [=](auto const &gridView) { return CubicHermitePreBasis, R>(gridView); }; } /** * \brief construct a PreBasisFactory for the reduced cubic Hermite Finite Element * * \tparam R RangeFieldType * \return the PreBasisFactory * \relates CubicHermiteBasis */ template auto reducedCubicHermite() { return [=](auto const &gridView) { return CubicHermitePreBasis, R, true>(gridView); }; } } // end namespace BasisFactory } // end namespace Dune::Functions #endif dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/defaultglobalbasis.hh000066400000000000000000000141471513634022200304520ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DEFAULTGLOBALBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DEFAULTGLOBALBASIS_HH #include #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { /** * \brief Global basis for given pre-basis * * This class implements the interface of a global basis * using the details from a given pre-basis. Hence * it serves as an example for this interface. * * If you want to implement your own global basis, it may be * better to implement a pre-basis instead. On the one hand * this needs less boiler-plate code. On the other hand * it makes your implementation composable and thus much * more flexible. That is, you can reuse your pre-basis * as one part in a larger product space by plugging it * e.g. into a CompositePreBasis of PowerPreBasis. * The actual global basis for your FooPreBasis is * then obtained by using DefaultGlobalBasis. * * \tparam PB Pre-basis providing the implementation details */ template class DefaultGlobalBasis { public: //! Pre-basis providing the implementation details using PreBasis = PB; //! The empty prefix path that identifies the root in the local ansatz tree using PrefixPath = TypeTree::TreePath<>; //! The grid view that the FE space is defined on using GridView = typename PreBasis::GridView; //! Type used for indices and size information using size_type = std::size_t; //! Type of the local view on the restriction of the basis to a single element using LocalView = DefaultLocalView>; //! Type used for global numbering of the basis vectors using MultiIndex = typename LocalView::MultiIndex; //! Type used for prefixes handed to the size() method using SizePrefix = Dune::ReservedVector; /** * \brief Constructor * * \tparam T Argument list for PreBasis * \param t Argument list for PreBasis * * This will forward all arguments to the constructor of PreBasis */ template = 0, enableIfConstructible = 0> DefaultGlobalBasis(T&&... t) : preBasis_(std::forward(t)...), prefixPath_() { static_assert(models, PreBasis>(), "Type passed to DefaultGlobalBasis does not model the PreBasis concept."); preBasis_.initializeIndices(); } /** * \brief Constructor from a PreBasis factory * * \param gridView The GridView this basis is based on * \param factory A factory functor that gets the `gridView` and returns a `PreBasis` */ template::value, int> = 0> DefaultGlobalBasis(const GridView& gridView, PreBasisFactory&& factory) : preBasis_(factory(gridView)), prefixPath_() { static_assert(models, PreBasis>(), "Type passed to DefaultGlobalBasis does not model the PreBasis concept."); preBasis_.initializeIndices(); } //! Obtain the grid view that the basis is defined on const GridView& gridView() const { return preBasis_.gridView(); } //! Obtain the pre-basis providing the implementation details const PreBasis& preBasis() const { return preBasis_; } //! Obtain the pre-basis providing the implementation details PreBasis& preBasis() { return preBasis_; } /** * \brief Update the stored grid view * * This will update the indexing information of the global basis. * It must be called if the grid has changed. */ void update(const GridView & gv) { preBasis_.update(gv); preBasis_.initializeIndices(); } //! Get the total dimension of the space spanned by this basis size_type dimension() const { return preBasis_.dimension(); } //! Return number of possible values for next position in empty multi index size_type size() const { return preBasis_.size(); } //! Return number of possible values for next position in multi index size_type size(const SizePrefix& prefix) const { return preBasis_.size(prefix); } //! Return local view for basis LocalView localView() const { return LocalView(*this); } //! Return *this because we are not embedded in a larger basis const DefaultGlobalBasis& rootBasis() const { return *this; } //! Return empty path, because this is the root in the local ansatz tree const PrefixPath& prefixPath() const { return prefixPath_; } //! Return the associated container descriptor auto containerDescriptor() const { if constexpr (requires(PreBasis pb){ pb.containerDescriptor(); }) return preBasis_.containerDescriptor(); else return ContainerDescriptors::Unknown{}; } protected: PreBasis preBasis_; PrefixPath prefixPath_; }; template DefaultGlobalBasis(PreBasis&&) -> DefaultGlobalBasis>; template DefaultGlobalBasis(const GridView& gv, PreBasisFactory&& f) -> DefaultGlobalBasis>; namespace BasisFactory { template auto makeBasis(const GridView& gridView, PreBasisFactory&& preBasisFactory) { return DefaultGlobalBasis(preBasisFactory(gridView)); } } // end namespace BasisFactory } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DEFAULTGLOBALBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/defaultlocalview.hh000066400000000000000000000117431513634022200301540ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DEFAULTLOCALVIEW_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DEFAULTLOCALVIEW_HH #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { /** \brief The restriction of a finite element basis to a single element */ template class DefaultLocalView { public: //! The global FE basis that this is a view on using GlobalBasis = GB; //! The grid view the global FE basis lives on using GridView = typename GlobalBasis::GridView; //! Type of the grid element we are bound to using Element = typename GridView::template Codim<0>::Entity; //! The type used for sizes using size_type = std::size_t; //! Tree of local finite elements / local shape function sets using Tree = typename GlobalBasis::PreBasis::Node; protected: using PreBasis = typename GlobalBasis::PreBasis; // Type used to store the multi indices of the basis vectors. // In contrast to MultiIndex this always has dynamic size. // It's guaranteed, that you can always cast it to MultiIndex using MultiIndexStorage = std::conditional_t<(PreBasis::minMultiIndexSize == PreBasis::maxMultiIndexSize), OverflowArray, PreBasis::multiIndexBufferSize>, Dune::ReservedVector>; public: /** \brief Type used for global numbering of the basis vectors */ using MultiIndex = std::conditional_t<(PreBasis::minMultiIndexSize == PreBasis::maxMultiIndexSize), StaticMultiIndex, Dune::ReservedVector>; /** \brief Construct local view for a given global finite element basis */ DefaultLocalView(const GlobalBasis& globalBasis) : globalBasis_(&globalBasis), tree_(globalBasis_->preBasis().makeNode()) { static_assert(models, Tree>(), "Tree type passed to DefaultLocalView does not model the BasisNode concept."); initializeTree(tree_); } /** \brief Deep copy of the local view */ DefaultLocalView(const DefaultLocalView& other) : globalBasis_(other.globalBasis_), tree_(globalBasis_->preBasis().makeNode()) { initializeTree(tree_); if (other.bound()) bind(other.element()); } /** \brief Bind the view to a grid element * * Having to bind the view to an element before being able to actually access any of its data members * offers to centralize some expensive setup code in the 'bind' method, which can save a lot of run-time. */ void bind(const Element& e) { element_ = e; bindTree(tree_, *element_); indices_.resize(size()); globalBasis_->preBasis().indices(tree_, indices_.begin()); } /** \brief Return if the view is bound to a grid element */ bool bound() const { return static_cast(element_); } /** \brief Return the grid element that the view is bound to * * \throws Dune::Exception if the view is not bound to anything */ const Element& element() const { return *element_; } /** \brief Unbind from the current element * * Calling this method should only be a hint that the view can be unbound. */ void unbind() { element_.reset(); } /** \brief Return the local ansatz tree associated to the bound entity * * \returns Tree // This is tree */ const Tree& tree() const { return tree_; } /** \brief Total number of degrees of freedom on this element */ size_type size() const { return tree_.size(); } /** * \brief Maximum local size for any element on the GridView * * This is the maximal size needed for local matrices * and local vectors, i.e., the result is */ size_type maxSize() const { return globalBasis_->preBasis().maxNodeSize(); } //! Maps from subtree index set [0..size-1] to a globally unique multi index in global basis const MultiIndex& index(size_type i) const { return indices_[i]; } /** \brief Return the global basis that we are a view on */ const GlobalBasis& globalBasis() const { return *globalBasis_; } const DefaultLocalView& rootLocalView() const { return *this; } protected: const GlobalBasis* globalBasis_; std::optional element_; Tree tree_; std::vector indices_; }; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DEFAULTLOCALVIEW_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/dynamicpowerbasis.hh000066400000000000000000000370471513634022200303520ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DYNAMICPOWERBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DYNAMICPOWERBASIS_HH #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { // ***************************************************************************** // This is the reusable part of the dynamic power bases. It contains // // DynamicPowerPreBasis // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** /** * \brief A pre-basis for dynamic power bases * * This pre-basis represents a power of a given pre-basis. * Its node type is a DynamicPowerBasisNodes for the given subnode. * * \tparam IMS An IndexMergingStrategy used to merge the global indices of the child factories * \tparam SPB The child pre-basis */ template class DynamicPowerPreBasis { static const bool isBlocked = std::is_same_v or std::is_same_v; public: //! The child pre-basis using SubPreBasis = SPB; //! The grid view that the FE basis is defined on using GridView = typename SPB::GridView; //! Type used for indices and size information using size_type = std::size_t; //! Strategy used to merge the global indices of the child factories using IndexMergingStrategy = IMS; //! Template mapping root tree path to type of created tree node using Node = DynamicPowerBasisNode; static constexpr size_type maxMultiIndexSize = SubPreBasis::maxMultiIndexSize + isBlocked; static constexpr size_type minMultiIndexSize = SubPreBasis::minMultiIndexSize + isBlocked; static constexpr size_type multiIndexBufferSize = SubPreBasis::multiIndexBufferSize + isBlocked; /** * \brief Constructor for given child pre-basis objects * * The child factories will be stored as copies */ template = 0, enableIfConstructible = 0> explicit DynamicPowerPreBasis(std::size_t c, SFArgs&&... sfArgs) : children_(c), subPreBasis_(std::forward(sfArgs)...) { static_assert(models, SubPreBasis>(), "Subprebasis passed to DynamicPowerPreBasis does not model the PreBasis concept."); } //! Initialize the global indices void initializeIndices() { subPreBasis_.initializeIndices(); } //! Obtain the grid view that the basis is defined on const GridView& gridView() const { return subPreBasis_.gridView(); } //! Update the stored grid view, to be called if the grid has changed void update(const GridView& gv) { subPreBasis_.update(gv); } /** * \brief Create tree node */ Node makeNode() const { auto node = Node{children_}; for (std::size_t i=0; i{}); } //! Return number of possible values for next position in multi index template size_type size(const SizePrefix& prefix) const { return sizeImpl(prefix, children_, IndexMergingStrategy{}); } protected: template size_type sizeImpl(SizePrefix prefix, Children children, BasisFactory::FlatInterleaved) const { // The root index size is the root index size of a single subnode // multiplied by the number of subnodes, because we enumerate all // child indices in a row. if (prefix.size() == 0) return children*subPreBasis_.size(); // The FlatInterleaved index merging strategy only changes the first // index digit. Hence, we have to reconstruct the corresponding digit // for the subtree and can then return the corresponding size of the subtree. prefix[0] = prefix[0] / children; return subPreBasis_.size(prefix); } template size_type sizeImpl(SizePrefix prefix, Children children, BasisFactory::FlatLexicographic) const { // The size at the index tree root is the size of at the index tree // root of a single subnode multiplied by the number of subnodes, // because we enumerate all child indices in a row. if (prefix.size() == 0) return children*subPreBasis_.size(); // The first prefix entry refers to one of the (root index size) // subindex trees. Hence, we have to first compute the corresponding // prefix entry for a single subnode subnode. Then we can append // the other prefix entries unmodified, because the index tree // looks the same after the first level. // The FlatLexicographic index merging strategy only changes the first // index digit. Hence, we have to reconstruct the corresponding digit // for the subtree and can then return the corresponding size of the subtree. prefix[0] = prefix[0] % subPreBasis_.size(); return subPreBasis_.size(prefix); } template static void multiIndexPopFront(MultiIndex& M) { for(std::size_t i=0; i size_type sizeImpl(SizePrefix prefix, Children children, BasisFactory::BlockedLexicographic) const { if (prefix.size() == 0) return children; multiIndexPopFront(prefix); return subPreBasis_.size(prefix); } template size_type sizeImpl(SizePrefix prefix, Children children, BasisFactory::BlockedInterleaved) const { if (prefix.size() == 0) return subPreBasis_.size(); // Remember last index, remove it and check if the remaining // prefix refers to a leaf in the subPreBasis index tree. // If yes, then the full prefix must also refer to a // leaf in the merged index tree. If not, then restore the full // prefix and proceed. auto tail = prefix.back(); prefix.pop_back(); if (subPreBasis_.size(prefix) == 0) return 0; prefix.push_back(tail); // Now check if the full prefix refers to a leaf in the subPreBasis // index tree. // If yes, then it has exactly 'children' appended children in the subtree. // If not, then the index tree looks the same in the merged subtree and we // can forward the result. auto subSize = subPreBasis_.size(prefix); if (subSize == 0) return children; return subSize; } public: //! Get the total dimension of the space spanned by this basis size_type dimension() const { return subPreBasis_.dimension() * children_; } //! Get the maximal number of DOFs associated to node for any element size_type maxNodeSize() const { return subPreBasis_.maxNodeSize() * children_; } //! Const access to the stored prebasis of the factor in the power space const SubPreBasis& subPreBasis() const { return subPreBasis_; } //! Mutable access to the stored prebasis of the factor in the power space SubPreBasis& subPreBasis() { return subPreBasis_; } //! Maps from subtree index set [0..size-1] to a globally unique multi index in global basis template requires Dune::TypeTree::Concept::UniformInnerTreeNode It indices(const NodeType& node, It it) const { return indicesImpl(node, it, children_, IndexMergingStrategy{}); } //! Return the associated container descriptor auto containerDescriptor() const { return containerDescriptorImpl(children_); } protected: template It indicesImpl(const NodeType& node, It multiIndices, Children children, BasisFactory::FlatInterleaved) const { using namespace Dune::Indices; size_type subTreeSize = node.child(_0).size(); // Fill indices for first child at the beginning. auto next = subPreBasis().indices(node.child(_0), multiIndices); // Multiply first component of all indices for first child by // number of children to stretch the index range for interleaving. for (std::size_t i = 0; i It indicesImpl(const NodeType& node, It multiIndices, Children children, BasisFactory::FlatLexicographic) const { using namespace Dune::Indices; size_type subTreeSize = node.child(_0).size(); size_type firstIndexEntrySize = subPreBasis().size(); // Fill indices for first child at the beginning. auto next = subPreBasis().indices(node.child(_0), multiIndices); for (std::size_t child = 1; child static void multiIndexPushFront(MultiIndex& M, size_type M0) { M.resize(M.size()+1); for(std::size_t i=M.size()-1; i>0; --i) M[i] = M[i-1]; M[0] = M0; } template It indicesImpl(const NodeType& node, It multiIndices, Children children, BasisFactory::BlockedLexicographic) const { using namespace Dune::Indices; size_type subTreeSize = node.child(_0).size(); // Fill indices for first child at the beginning. auto next = subPreBasis().indices(node.child(_0), multiIndices); // Insert 0 before first component of all indices for first child. for (std::size_t i = 0; i It indicesImpl(const NodeType& node, It multiIndices, Children children, BasisFactory::BlockedInterleaved) const { using namespace Dune::Indices; size_type subTreeSize = node.child(_0).size(); // Fill indices for first child at the beginning. auto next = subPreBasis().indices(node.child(_0), multiIndices); // Append 0 after last component of all indices for first child. for (std::size_t i = 0; i auto containerDescriptorImpl(Children children) const { auto subTree = Dune::Functions::containerDescriptor(subPreBasis_); if constexpr(std::is_same_v) return ContainerDescriptors::Impl::flatInterleavedN(children,std::move(subTree)); else if constexpr(std::is_same_v) return ContainerDescriptors::Impl::flatLexicographicN(children,std::move(subTree)); else if constexpr(std::is_same_v) return ContainerDescriptors::makeUniformDescriptor(children,std::move(subTree)); else if constexpr(std::is_same_v) return ContainerDescriptors::Impl::appendToTree(children,std::move(subTree)); else return ContainerDescriptors::Unknown{}; } protected: std::size_t children_; SubPreBasis subPreBasis_; }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can build a PowerPreBasis * * \ingroup FunctionSpaceBasesImplementations * * \tparam ChildPreBasisFactory Types of child pre-basis factory * \tparam IndexMergingStrategy An IndexMergingStrategy type * \param childPreBasisFactory Child pre-basis factory * * This overload can be used to explicitly supply an IndexMergingStrategy. */ template auto power(ChildPreBasisFactory&& childPreBasisFactory, std::size_t k, const IndexMergingStrategy&) { return [childPreBasisFactory,k](const auto& gridView) { auto childPreBasis = childPreBasisFactory(gridView); return DynamicPowerPreBasis(k,std::move(childPreBasis)); }; } /** * \brief Create a factory builder that can build a PowerPreBasis * * \ingroup FunctionSpaceBasesImplementations * * \tparam ChildPreBasisFactory Types of child pre-basis factory * \param childPreBasisFactory Child pre-basis factory * * This overload will select the BasisFactory::BlockedInterleaved strategy. */ template [[deprecated("Using the method `power` without an explicit index merging strategy" " will change its meaning after the release of dune-functions 2.11." " Previously, the default merging strategy was `BlockedInterleaved`," " but this will change to `FlatInterleaved`." " Change the call to `power(..., blockedInterleaved())` to retain the old behavior.")]] auto power(ChildPreBasisFactory&& childPreBasisFactory, std::size_t k) { return [childPreBasisFactory,k](const auto& gridView) { auto childPreBasis = childPreBasisFactory(gridView); return DynamicPowerPreBasis(k,std::move(childPreBasis)); }; } } // end namespace BasisFactory } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DYNAMICPOWERBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/flatmultiindex.hh000066400000000000000000000023151513634022200276460ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_FLATMULTIINDEX_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_FLATMULTIINDEX_HH #include #include namespace Dune { namespace Functions { /** * \brief A multi-index class with only one level * * \ingroup FunctionSpaceBasesUtilities * * This class provides a multi-index interface in the sense * that it has operator[] access to individual interfaces. * However, since it only supports flat indices of exactly * one level, it also has a cast of the multi-index to * this index. * This is obtianed by deriving from std::array * and adding this cast. * Hence multi-indices of type FlatMultiIndex can be used like * classic indices. */ template using FlatMultiIndex = StaticMultiIndex; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_FLATMULTIINDEX_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/flatvectorview.hh000066400000000000000000000116241513634022200276640ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_FLATVECTORVIEW_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_FLATVECTORVIEW_HH #include #include #include #include #include namespace Dune { namespace Functions { namespace Impl { template struct FlatVectorBackend { template(), int> = 0> static decltype(auto) getEntry(VV&& v, const Index& i) { return v[i]; } template(), int> = 0> static decltype(auto) getEntry(VV&& v, const Index&) { return std::forward(v); } template(), int> = 0> static auto size(VV&& v) { return Dune::Hybrid::size(v); } template(), int>type = 0> static auto size(VV&&) { return Dune::index_constant<1>{}; } }; template struct FlatVectorBackend > { template static decltype(auto) getEntry(VV&& v, const Index& i) { return v[i/m][i%m]; } template static auto size(VV&& v) { return Dune::index_constant{}; } }; template struct FlatVectorBackend< std::array > { template static decltype(auto) getEntry(VV&& v, const Index& i) { const auto innerSize = decltype(FlatVectorBackend::size(v[0]))::value; return FlatVectorBackend::getEntry(v[i/innerSize], i%innerSize); } template static auto size(VV&& v) { const auto innerSize = decltype(FlatVectorBackend::size(v[0]))::value; return Dune::index_constant{}; } }; template class FlatVectorView { using Backend = FlatVectorBackend>; public: FlatVectorView(T& t) : t_(&t) {} auto size() const { return Backend::size(*t_); } template decltype(auto) operator[](const Index& i) const { return Backend::getEntry(*t_, i); } template decltype(auto) operator[](const Index& i) { return Backend::getEntry(*t_, i); } private: T* t_; }; template class FlatVectorView { using Backend = FlatVectorBackend>; public: FlatVectorView(T&& t) : t_(std::move(t)) {} auto size() const { return Backend::size(t_); } template decltype(auto) operator[](const Index& i) const { return Backend::getEntry(t_, i); } template decltype(auto) operator[](const Index& i) { return Backend::getEntry(t_, i); } private: T t_; }; } // namespace Impl /** * \brief Create flat vector view of passed mutable container * * When passed a nested container, the resulting value is * a flat-vector-like view object. It provides an operator[] * method to access all entries of the underlying nested * container using flat consecutive indices and a size() * method to compute the corresponding total size. * * This method will create a view object storing a pointer * to the passed mutable container. */ template auto flatVectorView(T& t) { return Impl::FlatVectorView(t); } /** * \brief Create flat vector view of passed const container * * When passed a nested container, the resulting value is * a flat-vector-like view object. It provides an operator[] * method to access all entries of the underlying nested * container using flat consecutive indices and a size() * method to compute the corresponding total size. * * This method will create a view object storing a pointer * to the passed const container. */ template auto flatVectorView(const T& t) { return Impl::FlatVectorView(t); } /** * \brief Create flat vector view of passed container temporary * * When passed a nested container, the resulting value is * a flat-vector-like view object. It provides an operator[] * method to access all entries of the underlying nested * container using flat consecutive indices and a size() * method to compute the corresponding total size. * * This method will create a 'view' object storing the * provided temporary container by value. */ template auto flatVectorView(T&& t) { return Impl::FlatVectorView(std::move(t)); } } // namespace Dune::Functions } // namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_FLATVECTORVIEW_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/functionaldescriptor.hh000066400000000000000000000042211513634022200310540ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_FUNCTIONALDESCRIPTOR_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_FUNCTIONALDESCRIPTOR_HH #include #include namespace Dune::Functions::Impl { /** * \brief A class describing a dual functional * * This class allows to describe which type of evaluations * is performed by the dual basis implemented by a * LocalFiniteElement::LocalInterpolation. * For a LocalInterpolation object lint * lint.functionalDescriptor(k) should return * a FunctionalDescriptor which describes the * evaluation type performed by the k-th * dual-basis function and thus specifies * the nature of the k-th DOF. * * \warning: This interface is highly experimental * and subject to change. */ template class FunctionalDescriptor { public: using Order = std::array; FunctionalDescriptor() : partialDerivativeOrder_{} , normalDerivativeOrder_(0) {} explicit FunctionalDescriptor(const Order& partialDerivativeOrder) : partialDerivativeOrder_{partialDerivativeOrder} , normalDerivativeOrder_(0) {} explicit FunctionalDescriptor(unsigned int normalDerivativeOrder) : partialDerivativeOrder_{} , normalDerivativeOrder_(normalDerivativeOrder) {} bool isNormalDerivative() const { return normalDerivativeOrder_>0; } bool isPartialDerivative() const { for(auto i: Dune::range(dim)) { if (partialDerivativeOrder(i)>0) return true; } return false; } unsigned int normalDerivativeOrder() const { return normalDerivativeOrder_; } const Order& partialDerivativeOrder() const { return partialDerivativeOrder_; } private: Order partialDerivativeOrder_; unsigned int normalDerivativeOrder_; }; } // namespace Dune::Functions::Impl #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_FUNCTIONALDESCRIPTOR_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/globalvaluedlocalfiniteelement.hh000066400000000000000000000416051513634022200330470ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_GLOBALVALUEDLOCALFINITEELEMENT_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_GLOBALVALUEDLOCALFINITEELEMENT_HH #include #include #include #include #include #include #include #include #include #include namespace Dune::Functions::Impl { /** \brief Transforms shape function values and derivatives from reference element coordinates * to world coordinates using the contravariant Piola transform * * This transformation preserves normal traces of vector fields. * It is therefore the canonical transformation for H(div)-conforming finite elements. * * See for example: * M. Rognes, R. Kirby, A. Logg, "Efficient Assembly of H(div) and H(curl) * conforming finite elements", SIAM J. Sci. Comput., 2009 * * \todo Currently each method computes jacobianTransposed and integrationElement again. * Can we cache this somehow? */ struct ContravariantPiolaTransformator { /** \brief Piola-transform a set of shape-function values * * \param[in,out] values The values to be Piola-transformed */ template static auto apply(Values& values, const LocalCoordinate& xi, const Geometry& geometry) { auto jacobianTransposed = geometry.jacobianTransposed(xi); auto integrationElement = geometry.integrationElement(xi); for (auto& value : values) { auto tmp = value; // Here we cannot directly use // jacobianTransposed.mtv(tmp, value); // because mtv expects the DenseVector interface. auto tmpDenseVector = Impl::DenseVectorView(tmp); auto valueDenseVector = Impl::DenseVectorView(value); jacobianTransposed.mtv(tmpDenseVector, valueDenseVector); value /= integrationElement; } } /** \brief Piola-transform a set of shape-function derivatives * * \param[in,out] gradients The shape function derivatives to be Piola-transformed * * \bug The current implementation works only for affine geometries. * The Piola transformation for non-affine geometries requires * second derivatives of the geometry, which we don't get * from the dune-grid Geometry interface. */ template static auto applyJacobian(Gradients& gradients, const LocalCoordinate& xi, const Geometry& geometry) { auto jacobianTransposed = geometry.jacobianTransposed(xi); auto integrationElement = geometry.integrationElement(xi); for (auto& gradient : gradients) { auto tmp = gradient; gradient = 0; for (size_t k=0; k class LocalValuedFunction { const Function& f_; const Element& element_; using LocalValue = LocalCoordinate; public: LocalValuedFunction(const Function& f, const Element& element) : f_(f), element_(element) {} auto operator()(const LocalCoordinate& xi) const { auto globalValue = f_(xi); // Apply the inverse Piola transform auto jacobianInverseTransposed = element_.geometry().jacobianInverseTransposed(xi); auto integrationElement = element_.geometry().integrationElement(xi); auto localValue = LocalValue{}; // Here we cannot directly use // jacobianInverseTransposed.mtv(globalValue, localValue); // because mtv expects the DenseVector interface. auto globalValueDenseVector = Impl::DenseVectorView(globalValue); auto localValueDenseVector = Impl::DenseVectorView(localValue); jacobianInverseTransposed.mtv(globalValueDenseVector, localValueDenseVector); localValue *= integrationElement; return localValue; } }; }; /** \brief Transforms shape function values and derivatives from reference element coordinates * to world coordinates using the covariant Piola transform * * This transformation preserves tangential traces of vector fields. * It is therefore the canonical transformation for H(curl)-conforming finite elements. * * See for example: * M. Rognes, R. Kirby, A. Logg, "Efficient Assembly of H(div) and H(curl) * conforming finite elements", SIAM J. Sci. Comput., 2009 * * \todo Currently each method computes jacobianTransposed and integrationElement again. * Can we cache this somehow? */ struct CovariantPiolaTransformator { /** \brief Piola-transform a set of shape-function values * * \param[in,out] values The values to be Piola-transformed */ template static auto apply(Values& values, const LocalCoordinate& xi, const Geometry& geometry) { auto jacobianInverseTransposed = geometry.jacobianInverseTransposed(xi); for (auto& value : values) { auto tmp = value; // Here we cannot directly use // jacobianInverseTransposed.mv(tmp, value); // because mv expects the DenseVector interface. auto tmpDenseVector = Impl::DenseVectorView(tmp); auto valueDenseVector = Impl::DenseVectorView(value); jacobianInverseTransposed.mv(tmpDenseVector, valueDenseVector); } } /** \brief Piola-transform a set of shape-function derivatives * * \param[in,out] gradients The shape function derivatives to be Piola-transformed * * \bug The current implementation works only for affine geometries. * The Piola transformation for non-affine geometries requires * second derivatives of the geometry, which we don't get * from the dune-grid Geometry interface. */ template static auto applyJacobian(Gradients& gradients, const LocalCoordinate& xi, const Geometry& geometry) { auto jacobianInverseTransposed = geometry.jacobianInverseTransposed(xi); for (auto& gradient : gradients) { auto tmp = gradient; gradient = 0; for (size_t j=0; j class LocalValuedFunction { const Function& f_; const Element& element_; public: LocalValuedFunction(const Function& f, const Element& element) : f_(f), element_(element) {} auto operator()(const LocalCoordinate& xi) const { auto globalValue = f_(xi); // Apply the inverse Piola transform auto jacobianTransposed = element_.geometry().jacobianTransposed(xi); auto localValue = globalValue; // Here we cannot directly use // jacobianTransposed.mv(globalValue, localValue); // because mv expects the DenseVector interface. auto localValueDenseVector = Impl::DenseVectorView(localValue); auto globalValueDenseVector = Impl::DenseVectorView(globalValue); jacobianTransposed.mv(globalValueDenseVector, localValueDenseVector); return localValue; } }; }; /** \brief Implementation of a dune-localfunctions LocalBasis that applies a transformation * * \tparam Transformator The transformation (e.g., Piola) that is to be applied * \tparam LocalValuedLocalBasis The local-valued LocalBasis this is getting transformed * \tparam Element The element that the global-valued FE lives on */ template class GlobalValuedLocalBasis { public: using Traits = typename LocalValuedLocalBasis::Traits; /** \brief Bind the local basis to a particular grid element */ void bind(const LocalValuedLocalBasis& localValuedLocalBasis, const Element& element) { localValuedLocalBasis_ = &localValuedLocalBasis; element_ = &element; } /** \brief Number of shape functions */ auto size() const { return localValuedLocalBasis_->size(); } //! \brief Evaluate all shape functions void evaluateFunction(const typename Traits::DomainType& x, std::vector& out) const { localValuedLocalBasis_->evaluateFunction(x,out); Transformator::apply(out, x, element_->geometry()); } /** \brief Evaluate Jacobian of all shape functions * * \param x Point in the reference element where to evaluation the Jacobians * \param[out] out The Jacobians of all shape functions at the point x */ void evaluateJacobian(const typename Traits::DomainType& x, std::vector& out) const { localValuedLocalBasis_->evaluateJacobian(x,out); Transformator::applyJacobian(out, x, element_->geometry()); } /** \brief Evaluate partial derivatives of any order of all shape functions * * \param order Order of the partial derivatives, in the classic multi-index notation * \param in Position where to evaluate the derivatives * \param[out] out The desired partial derivatives */ void partial(const std::array& order, const typename Traits::DomainType& x, std::vector& out) const { auto totalOrder = std::accumulate(order.begin(), order.end(), 0); if (totalOrder == 0) { evaluateFunction(x, out); } else if (totalOrder == 1) { auto const direction = std::distance(order.begin(), std::find(order.begin(), order.end(), 1)); out.resize(size()); // TODO: The following is wasteful: We compute the full Jacobian and then return // only a part of it. While we need the full Jacobian of the underlying local-valued LFE, // it should be possible to compute only a partial Piola transform for the requested // partial derivatives. std::vector fullJacobian; localValuedLocalBasis_->evaluateJacobian(x,fullJacobian); Transformator::applyJacobian(fullJacobian, x, element_->geometry()); for (std::size_t i=0; iorder(); } const LocalValuedLocalBasis* localValuedLocalBasis_; const Element* element_; }; /** \brief Implementation of a dune-localfunctions LocalInterpolation * that accepts global-valued functions * * \tparam Transformator The transformation (e.g., Piola) that transforms from local to global values * \tparam LocalValuedLocalInterpolation The local-valued LocalInterpolation * that is used for the actual interpolation * \tparam Element The element that the global-valued FE lives on */ template class GlobalValuedLocalInterpolation { public: /** \brief Bind the local interpolation object to a particular grid element */ void bind(const LocalValuedLocalInterpolation& localValuedLocalInterpolation, const Element& element) { localValuedLocalInterpolation_ = &localValuedLocalInterpolation; element_ = &element; } template void interpolate (const F& f, std::vector& out) const { using LocalCoordinate = typename Element::Geometry::LocalCoordinate; typename Transformator::template LocalValuedFunction localValuedFunction(f, *element_); localValuedLocalInterpolation_->interpolate(localValuedFunction, out); } private: const LocalValuedLocalInterpolation* localValuedLocalInterpolation_; const Element* element_; }; /** \brief LocalFiniteElement implementation that uses values defined wrt particular grid elements * * \tparam Transformator Class implementing range-space transformations (like the Piola transforms) * \tparam LocalValuedLFE LocalFiniteElement implementation whose values are to be transformed * \tparam Element Element where to transform the FE values to */ template class GlobalValuedLocalFiniteElement { using LocalBasis = GlobalValuedLocalBasis; using LocalInterpolation = GlobalValuedLocalInterpolation; public: /** \brief Export number types, dimensions, etc. */ using Traits = LocalFiniteElementTraits; GlobalValuedLocalFiniteElement() {} void bind(const LocalValuedLFE& localValuedLFE, const Element& element) { globalValuedLocalBasis_.bind(localValuedLFE.localBasis(), element); globalValuedLocalInterpolation_.bind(localValuedLFE.localInterpolation(), element); localValuedLFE_ = &localValuedLFE; } /** \brief Returns the local basis, i.e., the set of shape functions */ const typename Traits::LocalBasisType& localBasis() const { return globalValuedLocalBasis_; } /** \brief Returns the assignment of the degrees of freedom to the element subentities */ const typename Traits::LocalCoefficientsType& localCoefficients() const { return localValuedLFE_->localCoefficients(); } /** \brief Returns object that evaluates degrees of freedom */ const typename Traits::LocalInterpolationType& localInterpolation() const { return globalValuedLocalInterpolation_; } /** \brief The number of shape functions */ std::size_t size() const { return localValuedLFE_->size(); } /** \brief The reference element that the local finite element is defined on */ GeometryType type() const { return localValuedLFE_->type(); } private: typename Traits::LocalBasisType globalValuedLocalBasis_; typename Traits::LocalInterpolationType globalValuedLocalInterpolation_; const LocalValuedLFE* localValuedLFE_; }; } // namespace Dune::Functions::Impl #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_GLOBALVALUEDLOCALFINITEELEMENT_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/hierarchicallagrangebasis.hh000066400000000000000000000105311513634022200317550ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_HIERARCHICALLAGRANGEBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_HIERARCHICALLAGRANGEBASIS_HH #include #include #include #include #include #include #include namespace Dune { namespace Functions { // ***************************************************************************** // Implementation for Hierarchical Lagrange Basis // // - only orders k=1,2 are implemented up to now // - order k=1 is identical to the standard Lagrange basis // - implementation is restricted to simplex grids // // ***************************************************************************** /** * \brief A pre-basis for a hierarchical Lagrange basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The grid view that the FE basis is defined on * \tparam k The polynomial order of ansatz functions, (0 < k <= 2) * \tparam R Range field-type used for shape function values */ template class HierarchicalLagrangePreBasis; template class HierarchicalLagrangePreBasis : public LFEPreBasisMixin> { using Base = LFEPreBasisMixin>; public: HierarchicalLagrangePreBasis (const GV& gridView) : Base(gridView, [](GeometryType gt, int) -> std::size_t { return gt.isVertex() ? 1 : 0; }) { for (auto gt : gridView.indexSet().types(0)) { if (!gt.isSimplex()) DUNE_THROW(Dune::NotImplemented, "Hierarchical Lagrange basis only implemented for simplex grids."); } } }; template class HierarchicalLagrangePreBasis : public LFEPreBasisMixin> { using Base = LFEPreBasisMixin>; public: HierarchicalLagrangePreBasis (const GV& gridView) : Base(gridView, [](GeometryType gt, int) -> std::size_t { return (gt.isVertex() || gt.isLine()) ? 1 : 0; }) { for (auto gt : gridView.indexSet().types(0)) { if (!gt.isSimplex()) DUNE_THROW(Dune::NotImplemented, "Hierarchical Lagrange basis only implemented for simplex grids."); } } }; namespace BasisFactory { /** * \brief A factory that can create a HierarchicalLagrange pre-basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam k The polynomial order of the ansatz functions (0 < k <= 2) * \tparam R The range field-type of the local basis */ template auto hierarchicalLagrange() { static_assert(0 < k && k <= 2); return [](const auto& gridView) { return HierarchicalLagrangePreBasis, k, R>(gridView); }; } } // end namespace BasisFactory /** \brief Basis of a scalar Hierarchical Lagrange finite element space * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The GridView that the space is defined on * \tparam k The order of the basis (0 < k <= 2) * \tparam R The range type of the local basis * * \note currently only supports simplex grids */ template using HierarchicalLagrangeBasis = DefaultGlobalBasis >; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_HIERARCHICALLAGRANGEBASIS_HH hierarchicallagrangewithelementbubblebasis.hh000066400000000000000000000122071513634022200353220ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/functionspacebases// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_HierarchicalLagrangeWOTHELEMENTBUBBLEBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_HierarchicalLagrangeWOTHELEMENTBUBBLEBASIS_HH #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { // ***************************************************************************** // Implementation for Hierarchical Lagrange Basis (of order 1-2) with an // element bubble function (of order dim+1). // // - currently only supports simplex grids // // ***************************************************************************** /** * \brief A pre-basis for a hierarchical basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The grid view that the FE basis is defined on * \tparam k The polynomial order of ansatz functions (0 < k <= 2) * \tparam R Range field-type used for shape function values */ template class HierarchicalLagrangeWithElementBubblePreBasis; template class HierarchicalLagrangeWithElementBubblePreBasis : public LFEPreBasisMixin> { using Base = LFEPreBasisMixin>; public: HierarchicalLagrangeWithElementBubblePreBasis (const GV& gridView) : Base(gridView, [](GeometryType gt, int) { return (gt.isVertex() || gt.dim() == GV::dimension) ? 1 : 0; }) { for (auto gt : gridView.indexSet().types(0)) { if (!gt.isSimplex()) DUNE_THROW(Dune::NotImplemented, "Hierarchical Lagrange basis only implemented for simplex grids."); } } }; template class HierarchicalLagrangeWithElementBubblePreBasis : public LFEPreBasisMixin> { using Base = LFEPreBasisMixin>; public: HierarchicalLagrangeWithElementBubblePreBasis (const GV& gridView) : Base(gridView, [](GeometryType gt, int) { return (gt.dim() <= 1 || gt.dim() == GV::dimension) ? 1 : 0; }) { for (auto gt : gridView.indexSet().types(0)) { if (!gt.isSimplex()) DUNE_THROW(Dune::NotImplemented, "Hierarchical Lagrange basis only implemented for simplex grids."); } } }; namespace BasisFactory { /** * \brief A factory that can create a HierarchicalLagrangeWithElementBubble pre-basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam k The polynomial order of the ansatz functions (0 < k <= 2) * \tparam R The range field-type of the local basis */ template auto hierarchicalLagrangeWithElementBubble() { static_assert(0 < k && k <= 2); return [](const auto& gridView) { return HierarchicalLagrangeWithElementBubblePreBasis, k, R>(gridView); }; } //! Explicit factory for k=1 for the HierarchicalLagrangeWithElementBubblePreBasis pre-basis template auto hierarchicalP1B() { return hierarchicalLagrangeWithElementBubble<1,R>(); } //! Explicit factory for k=2 for the HierarchicalLagrangeWithElementBubblePreBasis pre-basis template auto hierarchicalP2B() { return hierarchicalLagrangeWithElementBubble<2,R>(); } } // end namespace BasisFactory /** \brief Basis of a Hierarchical Lagrange finite element space with element bubble functions * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The GridView that the space is defined on * \tparam k The order of the basis (0 < k <= 2) * \tparam R The range field-type of the local basis * * \note currently only supports simplex grids */ template using HierarchicalLagrangeWithElementBubbleBasis = DefaultGlobalBasis >; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_HierarchicalLagrangeWOTHELEMENTBUBBLEBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/hierarchicnodetorangemap.hh000066400000000000000000000031751513634022200316470ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_HIERARCHICNODETORANGEMAP_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_HIERARCHICNODETORANGEMAP_HH #include #include #include #include #include namespace Dune { namespace Functions { /** * \brief A simple node to range map using the nested tree indices * * This map directly usses the tree path entries of the given * node to access the nested container. * * If the container does not provide any operator[] access, * it is simply forwarded for all nodes. */ struct HierarchicNodeToRangeMap { template>(), int> = 0> decltype(auto) operator()(const Node&, const TreePath& treePath, Range&& y) const { return resolveStaticMultiIndex(y, treePath); } template>(), int> = 0> decltype(auto) operator()(const Node&, const TreePath&, Range&& y) const { return std::forward(y); } }; } // namespace Dune::Functions } // namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_HIERARCHICNODETORANGEMAP_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/interpolate.hh000066400000000000000000000227041513634022200271470ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_INTERPOLATE_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_INTERPOLATE_HH #include #include #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { namespace Imp { struct AllTrueBitSetVector { struct AllTrueBitSet { bool test(int) const { return true; } } allTrue_; operator bool() const { return true; } template const AllTrueBitSetVector& operator[](const I&) const { return *this; } template void resize(const SP&) const {} }; // This helper function implements the restriction of some given function of type F. // The restriction is a simple callback that is applied to the values of the // function and the values of its derivative. template class ComponentFunction { public: ComponentFunction(F f, Restriction restriction) : f_(std::move(f)), restriction_(std::move(restriction)) {} template auto operator()(const Domain& x) const { return restriction_(f_(x)); } friend auto derivative(const ComponentFunction& cf) { // This provides support for capturing the derivative of the function by reference // using forwardCapture for perfect forwarding capture. If the function caches its // derivative, this saves a potentially costly copy. auto&& df = derivative(Dune::resolveRef(cf.f_)); return [&, df=forwardCapture(std::forward(df))](auto&& x) { return cf.restriction_(df.forward()(x)); }; } private: F f_; Restriction restriction_; }; // This helper function implements caching of the derivative for local functions. // When using an algorithm that gets a LocalFunction and calls its derivative // on each element, this leads to a costly call of derivative(f). E.g. for a // DiscreteGlobalBasisFunction, this will allocate several buffer. // To avoid this, this helper function caches the derivative and hands // out the cached derivative by reference. To ensure that the handed out // derivative is appropriately bound, binding the function will automatically // bind the cached derivative. // // Notice that we cannot simply create the derivative in the constructor, // because this may throw for functions that do not implement the derivative. template class CachedDerivativeLocalFunction { using Derivative = std::decay_t())))>; public: CachedDerivativeLocalFunction(F f) : f_(f) {} template void bind(const Element& element) { Dune::resolveRef(f_).bind(element); if (derivative_) derivative_.value().bind(element); } template auto operator()(const X& x) const { return f_(x); } friend const Derivative& derivative(const CachedDerivativeLocalFunction& cdlf) { if (not cdlf.derivative_) { auto&& lf = Dune::resolveRef(cdlf.f_); cdlf.derivative_ = derivative(lf); if (lf.bound()) cdlf.derivative_.value().bind(lf.localContext()); } return cdlf.derivative_.value(); } private: F f_; mutable std::optional derivative_; }; template void interpolateLocal(VectorBackend& vector, const BitVectorBackend& bitVector, const LocalFunction& localF, const LocalView& localView, const NodeToRangeEntry& nodeToRangeEntry) { Dune::TypeTree::forEachLeafNode(localView.tree(), [&](auto&& node, auto&& treePath) { if (node.empty()) return; using Node = std::decay_t; using FiniteElement = typename Node::FiniteElement; using FiniteElementRangeField = typename FiniteElement::Traits::LocalBasisType::Traits::RangeFieldType; auto interpolationCoefficients = std::vector(); auto&& fe = node.finiteElement(); auto localF_RE = ComponentFunction(std::cref(localF), [&](auto&& y) { return nodeToRangeEntry(node, treePath, y); }); fe.localInterpolation().interpolate(localF_RE, interpolationCoefficients); for (size_t i=0; i auto require(F&& f) -> decltype(derivative(f)); }; } // namespace Imp /** * \brief Interpolate given function in discrete function space * * Only vector coefficients marked as 'true' in the * bitVector argument are interpolated. Use this, e.g., to * interpolate Dirichlet boundary values. * * Notice that this will only work if the range type of f and * the block type of coeff are compatible and supported by * flatVectorView. * * \param basis Global function space basis of discrete function space * \param coeff Coefficient vector to represent the interpolation * \param f Function to interpolate * \param bv A bit vector with flags marking all DOFs that should be interpolated * \param nodeToRangeEntry Polymorphic functor mapping local ansatz nodes to range-indices of given function */ template void interpolate(const B& basis, C&& coeff, const F& f, const BV& bv, const NTRE& nodeToRangeEntry) { using GridView = typename B::GridView; using Element = typename GridView::template Codim<0>::Entity; using GlobalDomain = typename Element::Geometry::GlobalCoordinate; static_assert(Dune::Functions::Concept::isCallable(), "Function passed to interpolate does not model the Callable concept"); auto&& gridView = basis.gridView(); // Small helper functions to wrap vectors using istlVectorBackend // if they do not already satisfy the VectorBackend interface. auto toVectorBackend = [&](auto& v) -> decltype(auto) { if constexpr (models, decltype(v)>()) { return v; } else { return istlVectorBackend(v); } }; auto toConstVectorBackend = [&](auto& v) -> decltype(auto) { if constexpr (models, decltype(v)>()) { return v; } else { return istlVectorBackend(v); } }; auto&& bitVector = toConstVectorBackend(bv); auto&& vector = toVectorBackend(coeff); vector.resize(basis); // Make a grid function supporting local evaluation out of f auto gf = makeGridViewFunction(f, gridView); // Obtain a local view of f // To avoid costly reconstruction of the derivative on each element, // we use the CachedDerivativeLocalFunction wrapper if the function // is differentiable. This wrapper will handout // a reference to a single cached derivative object. auto localF = [&](){ if constexpr (models()) return Imp::CachedDerivativeLocalFunction(localFunction(gf)); else return localFunction(gf); }(); auto localView = basis.localView(); for (const auto& e : elements(gridView)) { localView.bind(e); localF.bind(e); Imp::interpolateLocal(vector, bitVector, localF, localView, nodeToRangeEntry); } } /** * \brief Interpolate given function in discrete function space * * Only vector coefficients marked as 'true' in the * bitVector argument are interpolated. Use this, e.g., to * interpolate Dirichlet boundary values. * * Notice that this will only work if the range type of f and * the block type of coeff are compatible and supported by * flatVectorView. * * \param basis Global function space basis of discrete function space * \param coeff Coefficient vector to represent the interpolation * \param f Function to interpolate * \param bitVector A vector with flags marking all DOFs that should be interpolated */ template void interpolate(const B& basis, C&& coeff, const F& f, const BV& bitVector) { interpolate(basis, coeff, f, bitVector, HierarchicNodeToRangeMap()); } /** * \brief Interpolate given function in discrete function space * * Notice that this will only work if the range type of f and * the block type of coeff are compatible and supported by * flatVectorView. * * This function will only work, if the local ansatz tree of * the basis is trivial, i.e., a single leaf node. * * \param basis Global function space basis of discrete function space * \param coeff Coefficient vector to represent the interpolation * \param f Function to interpolate */ template void interpolate(const B& basis, C&& coeff, const F& f) { interpolate (basis, coeff, f, Imp::AllTrueBitSetVector(), HierarchicNodeToRangeMap()); } } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_INTERPOLATE_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/lagrangebasis.hh000066400000000000000000000453251513634022200274270ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LAGRANGEBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LAGRANGEBASIS_HH #include #include #include #include #include #include #include namespace Dune { namespace Functions { // ***************************************************************************** // This is the reusable part of the LagrangeBasis. It contains // // LagrangePreBasis // LagrangeNode // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template class LagrangeNode; template class LagrangePreBasis; /** * \brief A pre-basis for a PQ-lagrange bases with given order * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The grid view that the FE basis is defined on * \tparam k The polynomial order of ansatz functions; -1 means 'order determined at run-time' * \tparam R Range type used for shape function values * * \note This only works for certain grids. The following restrictions hold * - If k is no larger than 2, then the grids can have any dimension * - If k is larger than 3 then the grid must be two-dimensional * - If k is 3, then the grid can be 3d *if* it is a simplex grid * * \warning For pyramid elements in 3d, the shape functions are different for * run-time and compile-time order. While the former are defined using the * Duffy-transformation the latter are continuous and piecewise polynomial * with discontinuous gradients. */ template class LagrangePreBasis : public LeafPreBasisMixin< LagrangePreBasis > { static const int dim = GV::dimension; static const bool useDynamicOrder = (k<0); public: //! The grid view that the FE basis is defined on using GridView = GV; //! Type used for indices and size information using size_type = std::size_t; //! Template mapping root tree path to type of created tree node using Node = LagrangeNode; //! Constructor for a given grid view object with compile-time order LagrangePreBasis(const GridView& gv) : LagrangePreBasis(gv, std::numeric_limits::max()) {} //! Constructor for a given grid view object and run-time order LagrangePreBasis(const GridView& gv, unsigned int order) : gridView_(gv), order_(order) { if (!useDynamicOrder && order!=std::numeric_limits::max()) DUNE_THROW(RangeError, "Template argument k has to be -1 when supplying a run-time order!"); for (int i=0; i<=dim; i++) { dofsPerCube_[i] = computeDofsPerCube(i); dofsPerSimplex_[i] = computeDofsPerSimplex(i); } dofsPerPrism_ = computeDofsPerPrism(); dofsPerPyramid_ = computeDofsPerPyramid(); } //! Initialize the global indices void initializeIndices() { vertexOffset_ = 0; edgeOffset_ = vertexOffset_ + dofsPerCube(0) * ((size_type)gridView_.size(dim)); if (dim>=2) { triangleOffset_ = edgeOffset_ + dofsPerCube(1) * ((size_type) gridView_.size(dim-1)); quadrilateralOffset_ = triangleOffset_ + dofsPerSimplex(2) * ((size_type)gridView_.size(Dune::GeometryTypes::triangle)); } if (dim==3) { tetrahedronOffset_ = quadrilateralOffset_ + dofsPerCube(2) * ((size_type)gridView_.size(Dune::GeometryTypes::quadrilateral)); prismOffset_ = tetrahedronOffset_ + dofsPerSimplex(3) * ((size_type)gridView_.size(Dune::GeometryTypes::tetrahedron)); hexahedronOffset_ = prismOffset_ + dofsPerPrism() * ((size_type)gridView_.size(Dune::GeometryTypes::prism)); pyramidOffset_ = hexahedronOffset_ + dofsPerCube(3) * ((size_type)gridView_.size(Dune::GeometryTypes::hexahedron)); } } //! Obtain the grid view that the basis is defined on const GridView& gridView() const { return gridView_; } //! Update the stored grid view, to be called if the grid has changed void update (const GridView& gv) { gridView_ = gv; } /** * \brief Create tree node */ Node makeNode() const { return Node{order_}; } //! Get the total dimension of the space spanned by this basis size_type dimension() const { switch (dim) { case 1: return dofsPerCube(0) * ((size_type)gridView_.size(dim)) + dofsPerCube(1) * ((size_type)gridView_.size(dim-1)); case 2: { return dofsPerCube(0) * ((size_type)gridView_.size(dim)) + dofsPerCube(1) * ((size_type)gridView_.size(dim-1)) + dofsPerSimplex(2) * ((size_type)gridView_.size(Dune::GeometryTypes::triangle)) + dofsPerCube(2) * ((size_type)gridView_.size(Dune::GeometryTypes::quadrilateral)); } case 3: { return dofsPerCube(0) * ((size_type)gridView_.size(dim)) + dofsPerCube(1) * ((size_type)gridView_.size(dim-1)) + dofsPerSimplex(2) * ((size_type)gridView_.size(Dune::GeometryTypes::triangle)) + dofsPerCube(2) * ((size_type)gridView_.size(Dune::GeometryTypes::quadrilateral)) + dofsPerSimplex(3) * ((size_type)gridView_.size(Dune::GeometryTypes::tetrahedron)) + dofsPerPyramid() * ((size_type)gridView_.size(Dune::GeometryTypes::pyramid)) + dofsPerPrism() * ((size_type)gridView_.size(Dune::GeometryTypes::prism)) + dofsPerCube(3) * ((size_type)gridView_.size(Dune::GeometryTypes::hexahedron)); } } DUNE_THROW(Dune::NotImplemented, "No size method for " << dim << "d grids available yet!"); } //! Get the maximal number of DOFs associated to node for any element size_type maxNodeSize() const { // That cast to unsigned int is necessary because GV::dimension is an enum, // which is not recognized by the power method as an integer type... return power(order()+1, (unsigned int)GV::dimension); } template It indices(const Node& node, It it) const { for (size_type i = 0, end = node.finiteElement().size() ; i < end ; ++it, ++i) { Dune::LocalKey localKey = node.finiteElement().localCoefficients().localKey(i); const auto& gridIndexSet = gridView().indexSet(); const auto& element = node.element(); // The dimension of the entity that the current dof is related to auto dofDim = dim - localKey.codim(); // Test for a vertex dof // The test for k==1 is redundant, but having it here allows the compiler to conclude // at compile-time that the dofDim==0 case is the only one that will ever happen. // This leads to measurable speed-up: see // https://gitlab.dune-project.org/staging/dune-functions/issues/30 if (k==1 || dofDim==0) { *it = {{ (size_type)(gridIndexSet.subIndex(element,localKey.subEntity(),dim)) }}; continue; } if (dofDim==1) { // edge dof if (dim==1) // element dof -- any local numbering is fine { *it = {{ edgeOffset_ + dofsPerCube(1) * ((size_type)gridIndexSet.subIndex(element,0,0)) + localKey.index() }}; continue; } else { const auto refElement = Dune::referenceElement(element.type()); // We have to reverse the numbering if the local element edge is // not aligned with the global edge. auto v0 = (size_type)gridIndexSet.subIndex(element,refElement.subEntity(localKey.subEntity(),localKey.codim(),0,dim),dim); auto v1 = (size_type)gridIndexSet.subIndex(element,refElement.subEntity(localKey.subEntity(),localKey.codim(),1,dim),dim); bool flip = (v0 > v1); *it = {{ (flip) ? edgeOffset_ + dofsPerCube(1)*((size_type)gridIndexSet.subIndex(element,localKey.subEntity(),localKey.codim())) + (dofsPerCube(1)-1)-localKey.index() : edgeOffset_ + dofsPerCube(1)*((size_type)gridIndexSet.subIndex(element,localKey.subEntity(),localKey.codim())) + localKey.index() }}; continue; } } if (dofDim==2) { if (dim==2) // element dof -- any local numbering is fine { if (element.type().isTriangle()) { *it = {{ triangleOffset_ + dofsPerSimplex(2)*((size_type)gridIndexSet.subIndex(element,0,0)) + localKey.index() }}; continue; } else if (element.type().isQuadrilateral()) { *it = {{ quadrilateralOffset_ + dofsPerCube(2)*((size_type)gridIndexSet.subIndex(element,0,0)) + localKey.index() }}; continue; } else DUNE_THROW(Dune::NotImplemented, "2d elements have to be triangles or quadrilaterals"); } else { const auto refElement = Dune::referenceElement(element.type()); if (order()>3) DUNE_THROW(Dune::NotImplemented, "LagrangeBasis for 3D grids is only implemented if k<=3"); if (order()==3 and !refElement.type(localKey.subEntity(), localKey.codim()).isTriangle()) DUNE_THROW(Dune::NotImplemented, "LagrangeBasis for 3D grids with k==3 is only implemented if the grid is a simplex grid"); *it = {{ triangleOffset_ + ((size_type)gridIndexSet.subIndex(element,localKey.subEntity(),localKey.codim())) }}; continue; } } if (dofDim==3) { if (dim==3) // element dof -- any local numbering is fine { if (element.type().isTetrahedron()) { *it = {{ tetrahedronOffset_ + dofsPerSimplex(3)*((size_type)gridIndexSet.subIndex(element,0,0)) + localKey.index() }}; continue; } else if (element.type().isHexahedron()) { *it = {{ hexahedronOffset_ + dofsPerCube(3)*((size_type)gridIndexSet.subIndex(element,0,0)) + localKey.index() }}; continue; } else if (element.type().isPrism()) { *it = {{ prismOffset_ + dofsPerPrism()*((size_type)gridIndexSet.subIndex(element,0,0)) + localKey.index() }}; continue; } else if (element.type().isPyramid()) { *it = {{ pyramidOffset_ + dofsPerPyramid()*((size_type)gridIndexSet.subIndex(element,0,0)) + localKey.index() }}; continue; } else DUNE_THROW(Dune::NotImplemented, "3d elements have to be tetrahedra, hexahedra, prisms, or pyramids"); } else DUNE_THROW(Dune::NotImplemented, "Grids of dimension larger than 3 are no supported"); } DUNE_THROW(Dune::NotImplemented, "Grid contains elements not supported for the LagrangeBasis"); } return it; } //! Polynomial order used in the local Lagrange finite-elements unsigned int order() const { return (useDynamicOrder) ? order_ : k; } protected: GridView gridView_; // Run-time order, only valid if k<0 unsigned int order_; //! Number of degrees of freedom assigned to a simplex (without the ones assigned to its faces!) size_type dofsPerSimplex(std::size_t simplexDim) const { return useDynamicOrder ? dofsPerSimplex_[simplexDim] : computeDofsPerSimplex(simplexDim); } //! Number of degrees of freedom assigned to a cube (without the ones assigned to its faces!) size_type dofsPerCube(std::size_t cubeDim) const { return useDynamicOrder ? dofsPerCube_[cubeDim] : computeDofsPerCube(cubeDim); } size_type dofsPerPrism() const { return useDynamicOrder ? dofsPerPrism_ : computeDofsPerPrism(); } size_type dofsPerPyramid() const { return useDynamicOrder ? dofsPerPyramid_ : computeDofsPerPyramid(); } //! Number of degrees of freedom assigned to a simplex (without the ones assigned to its faces!) size_type computeDofsPerSimplex(std::size_t simplexDim) const { return order() == 0 ? (dim == simplexDim ? 1 : 0) : Dune::binomial(std::size_t(order()-1),simplexDim); } //! Number of degrees of freedom assigned to a cube (without the ones assigned to its faces!) size_type computeDofsPerCube(std::size_t cubeDim) const { return order() == 0 ? (dim == cubeDim ? 1 : 0) : Dune::power(order()-1, cubeDim); } size_type computeDofsPerPrism() const { return order() == 0 ? (dim == 3 ? 1 : 0) : (order()-1)*(order()-1)*(order()-2)/2; } size_type computeDofsPerPyramid() const { return order() == 0 ? (dim == 3 ? 1 : 0) : (order()-2)*(order()-1)*(2*order()-3)/6; } // When the order is given at run-time, the following numbers are pre-computed: std::array dofsPerSimplex_; std::array dofsPerCube_; size_type dofsPerPrism_; size_type dofsPerPyramid_; size_type vertexOffset_; size_type edgeOffset_; size_type triangleOffset_; size_type quadrilateralOffset_; size_type tetrahedronOffset_; size_type pyramidOffset_; size_type prismOffset_; size_type hexahedronOffset_; }; template class LagrangeNode : public LeafBasisNode { static constexpr int dim = GV::dimension; static constexpr bool useDynamicOrder = (k<0); // Compute the GeometryType id in case the grid has only a single GeometryType static constexpr GeometryType::Id geometryTypeId() { if constexpr(Dune::Capabilities::hasSingleGeometryType::v) return GeometryType(Dune::Capabilities::hasSingleGeometryType::topologyId, GV::dimension); else return GeometryType::Id(~0u); } // Select the static LFECache if k >= 0, else the dynamic LFECache using FiniteElementCache = std::conditional_t<(useDynamicOrder), DynamicLagrangeLocalFiniteElementCache, StaticLagrangeLocalFiniteElementCache >; // Construct the FiniteElementCache depending on whether the order is dynamic or static static auto makeFiniteElementCache(unsigned int order) { if constexpr (useDynamicOrder) return FiniteElementCache{order}; else return FiniteElementCache{}; } public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; using FiniteElement = typename FiniteElementCache::FiniteElementType; //! Constructor without order (uses the compile-time value) LagrangeNode() : LagrangeNode(k) {} //! Constructor with a run-time order LagrangeNode(unsigned int order) : order_(order), cache_(makeFiniteElementCache(order)), finiteElement_(nullptr), element_(nullptr) {} //! Return current element, throw if unbound const Element& element() const { return *element_; } /** \brief Return the LocalFiniteElement for the element we are bound to * * The LocalFiniteElement implements the corresponding interfaces of the dune-localfunctions module */ const FiniteElement& finiteElement() const { return *finiteElement_; } //! Bind to element. void bind(const Element& e) { element_ = &e; finiteElement_ = &(cache_.get(element_->type())); this->setSize(finiteElement_->size()); } protected: unsigned int order() const { return order_; } // Run-time order, only valid if k<0 unsigned int order_; FiniteElementCache cache_; const FiniteElement* finiteElement_; const Element* element_; }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can create a Lagrange pre-basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam k The polynomial order of the ansatz functions; -1 means 'order determined at run-time' * \tparam R The range type of the local basis */ template auto lagrange() { return [](const auto& gridView) { return LagrangePreBasis, k, R>(gridView); }; } /** * \brief Create a pre-basis factory that can create a Lagrange pre-basis with a run-time order * * \ingroup FunctionSpaceBasesImplementations * * \tparam R The range type of the local basis */ template auto lagrange(int order) { return [=](const auto& gridView) { return LagrangePreBasis, -1, R>(gridView, order); }; } } // end namespace BasisFactory /** \brief Nodal basis of a scalar k-th-order Lagrangean finite element space * * \ingroup FunctionSpaceBasesImplementations * * \note This only works for certain grids. The following restrictions hold * - If k is no larger than 2, then the grids can have any dimension * - If k is larger than 3 then the grid must be two-dimensional * - If k is 3, then the grid can be 3d *if* it is a simplex grid * * All arguments passed to the constructor will be forwarded to the constructor * of LagrangePreBasis. * * \warning The implementation of the basis with run-time order order uses the * LagrangeFiniteElement implementation of dune-localfunctions, which is known * to violate strict-aliasing rules * (see https://gitlab.dune-project.org/core/dune-localfunctions/issues/14) * Keep this in mind if ever you experience difficult-to-explain crashes * or wrong results. * * \warning For pyramid elements in 3d, the shape functions are different for * run-time and compile-time order. While the former are defined using the * Duffy-transformation the latter are continuous and piecewise polynomial * with discontinuous gradients. * * \tparam GV The GridView that the space is defined on * \tparam k The order of the basis; -1 means 'order determined at run-time' * \tparam R The range type of the local basis */ template using LagrangeBasis = DefaultGlobalBasis >; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LAGRANGEBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/lagrangedgbasis.hh000066400000000000000000000150251513634022200277340ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LAGRANGEDGBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LAGRANGEDGBASIS_HH #include #include #include #include #include #include namespace Dune { namespace Functions { // ***************************************************************************** // This is the reusable part of the basis. It contains // // LagrangeDGPreBasis // LagrangeDGNode // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template using LagrangeDGNode = LagrangeNode; /** \brief PreBasis implementation for a Lagrangean-DG finite element space * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The grid view that the FE basis is defined on * \tparam k The order of ansatz functions; -1 means 'order determined at run-time' * \tparam R Range type used for shape function values */ template class LagrangeDGPreBasis : public Dune::Functions::LeafPreBasisMapperMixin { using Base = Dune::Functions::LeafPreBasisMapperMixin; static constexpr bool useDynamicOrder = (k<0); static MCMGLayout dofLayout(int order) { return [order](Dune::GeometryType type, size_t dimGrid) { if (type.dim() == dimGrid) { if (type.isLine()) return order+1; if (type.isTriangle()) return (order+1)*(order+2)/2; if (type.isQuadrilateral()) return (order+1)*(order+1); if (type.isTetrahedron()) return (order+1)*(order+2)*(order+3)/6; if (type.isPrism()) return (order+1)*(order+1)*(order+2)/2; if (type.isHexahedron()) return (order+1)*(order+1)*(order+1); if (type.isPyramid()) return (order+1)*(order+2)*(2*order+3)/6; DUNE_THROW(Dune::NotImplemented, "Using LagrangeDGPreBasis with non-supported GeometryType"); } return 0; }; } public: /** \brief The grid view that the FE space is defined on */ using GridView = GV; using size_type = typename Base::size_type; // Precompute the number of dofs per entity type [[deprecated("This constant will be removed after Dune 2.11")]] const static int dofsPerEdge = k+1; [[deprecated("This constant will be removed after Dune 2.11")]] const static int dofsPerTriangle = (k+1)*(k+2)/2; [[deprecated("This constant will be removed after Dune 2.11")]] const static int dofsPerQuad = (k+1)*(k+1); [[deprecated("This constant will be removed after Dune 2.11")]] const static int dofsPerTetrahedron = (k+1)*(k+2)*(k+3)/6; [[deprecated("This constant will be removed after Dune 2.11")]] const static int dofsPerPrism = (k+1)*(k+1)*(k+2)/2; [[deprecated("This constant will be removed after Dune 2.11")]] const static int dofsPerHexahedron = (k+1)*(k+1)*(k+1); [[deprecated("This constant will be removed after Dune 2.11")]] const static int dofsPerPyramid = (k+1)*(k+2)*(2*k+3)/6; using Node = LagrangeDGNode; /** * \brief Constructor for a given grid view object * * This constructor requires that the order is given * at compile-time using `k>=0` */ LagrangeDGPreBasis(const GridView& gv) requires(k>=0) : Base(gv, dofLayout(k)) , order_(k) {} /** * \brief Constructor for a given grid view object * * This constructor has to be used to set the order * dynamically which is enables using `k<0`. */ LagrangeDGPreBasis(const GridView& gv, unsigned int order) requires(k<0) : Base(gv, dofLayout(order)) , order_(order) {} /** * \brief Create tree node */ Node makeNode() const { return Node{order_}; } template It indices(const Node& node, It it) const { size_type elementOffset = Base::mapper_.index(node.element()); for(auto i : Dune::range(node.size())) { *it = {{ (size_type)(elementOffset+i) }}; ++it; } return it; } //! Polynomial order used in the local Lagrange finite-elements unsigned int order() const { return (useDynamicOrder) ? order_ : k; } protected: unsigned int order_; }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can create a LagrangeDG pre-basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam order The polynomial order of the ansatz functions * \tparam R The range type of the local basis */ template auto lagrangeDG() { return [](const auto& gridView) { return LagrangeDGPreBasis, order, R>(gridView); }; } /** * \brief Create a pre-basis factory that can create a LagrangeDG pre-basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam R The range type of the local basis * \param order The polynomial order of the ansatz functions */ template auto lagrangeDG(unsigned int order) { return [order](const auto& gridView) { return LagrangeDGPreBasis, -1, R>(gridView, order); }; } } // end namespace BasisFactory // ***************************************************************************** // This is the actual global basis implementation based on the reusable parts. // ***************************************************************************** /** \brief Basis of a scalar k-th-order Lagrangean-DG finite element space * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The GridView that the space is defined on * \tparam k The order of the basis * \tparam R The range type of the local basis */ template using LagrangeDGBasis = DefaultGlobalBasis >; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LAGRANGEDGBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/leafprebasismappermixin.hh000066400000000000000000000105701513634022200315310ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LEAFPREBASISMAPPERMIXIN_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LEAFPREBASISMAPPERMIXIN_HH #include #include #include #include namespace Dune::Functions { // Helper function returning a random access range // of global indices associated to the element. The ordering // is derived from the LocalCoefficients object. // Having this as a member of MCMGMapper would be nice. // But this would introduce the LocalCoefficient in dune-grid. // This would introduce at least a weak 'conceptual' dependency problem. template auto subIndexRange(const Dune::MultipleCodimMultipleGeomTypeMapper& mapper, const typename GridView::template Codim<0>::Entity& element, const LocalCoefficients& localCoefficients) { // Here we make use of the 'hidden' (poorly documented) MCMGMapper feature to support // multiple DOFs per subentity. However, we do not take care for any reordering. return Dune::transformedRangeView(Dune::range(localCoefficients.size()), [&](auto localIndex) { auto localKey = localCoefficients.localKey(localIndex); return mapper.subIndex(element, localKey.subEntity(), localKey.codim()) + localKey.index(); }); } /** * \brief A generic MixIn class for PreBasis with flat indices computed from a mapper. * * This abstracts all index computations that can be implemented using a * MultipleCodimMultipleGeomTypeMapper with appropriate MCMGLayout. * In order to use this, you need to derive from this class and * pass the layout in the constructor. Then the mixin takes care * for all the index and size computation and the derived class * only needs to add the node creation. * * Be careful: This does not do any reordering of indices * if multiple basis functions are associated to the same * subentity. * * \tparam GV The grid view the basis is defined on. */ template class LeafPreBasisMapperMixin : public LeafPreBasisMixin> { static const int gridDim = GV::dimension; public: //! Type of the associated GridView using GridView = GV; //! Type used for index digits using size_type = std::size_t; //! Construct from GridView and local DOF layout LeafPreBasisMapperMixin(const GridView& gv, Dune::MCMGLayout layout) : gridView_(gv), mapper_(gridView_, std::move(layout)) {} //! Initialize the global index information void initializeIndices() { // Determine upper bound for node size by traversing // the layout for all element types in the grid maxNodeSize_ = 0; for(const GeometryType& elementType : gridView_.indexSet().types(0)) { auto referenceElement = Dune::referenceElement(elementType); for(auto codim : Dune::range(gridDim+1)) for(auto i : Dune::range(referenceElement.size(codim))) maxNodeSize_ += mapper_.layout()(referenceElement.type(i, codim), gridDim); } } //! Export the stored GridView const GridView& gridView() const { return gridView_; } //! Update the stored GridView void update(const GridView& gv) { gridView_ = gv; mapper_.update(gridView_); } //! Return total number of basis functions size_type dimension() const { return mapper_.size(); } //! Return maximal number of basis functions per element size_type maxNodeSize() const { return maxNodeSize_; } //! Fill cache with global indices of DOFs associated to the given bound node template It indices(const Node& node, It it) const { for(const auto& globalIndex : subIndexRange(mapper_, node.element(), node.finiteElement().localCoefficients())) { *it = {{ (size_type)globalIndex }}; ++it; } return it; } protected: GridView gridView_; Dune::MultipleCodimMultipleGeomTypeMapper mapper_; std::size_t maxNodeSize_; }; } // end namespace Dune::Functions #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LEAFPREBASISMAPPERMIXIN_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/leafprebasismixin.hh000066400000000000000000000046011513634022200303220ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LEAFPREBASISMIXIN_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LEAFPREBASISMIXIN_HH #include #include #include #include namespace Dune::Functions { /** * \brief A generic MixIn class for PreBasis * * Extends the interface of a `Derived` class by common * constants and the `size()` methods. A requirement is * that `Derived` implements the method `dimension()` * returning the total number of basis functions the * pre-basis represents. * * This mixin class can be used for all pre bases that * are on the leaf of the basis-tree. These pre-bases * are supposed to have a size only for empty size-prefixes * and this size is the same as the given dimension. * * \tparam Derived The actual implementation of a pre-basis */ template class LeafPreBasisMixin { public: //! Type used for indices and size information using size_type = std::size_t; //! Maximal length of global multi-indices static constexpr size_type maxMultiIndexSize = 1; //! Minimal length of global multi-indices static constexpr size_type minMultiIndexSize = 1; //! Size required temporarily when constructing global multi-indices static constexpr size_type multiIndexBufferSize = 1; //! Return number of possible values for next position in multi index template().size(), bool{}) = true> size_type size(const SizePrefix& prefix) const { assert(prefix.size() == 0 || prefix.size() == 1); return (prefix.size() == 0) ? derived().dimension() : 0; } //! Get the total dimension of the space spanned by this basis size_type size() const { return derived().dimension(); } //! Return a flat container-descriptor auto containerDescriptor() const { return ContainerDescriptors::FlatVector{size()}; } private: const Derived& derived() const { return static_cast(*this); } }; } // end namespace Dune::Functions #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LEAFPREBASISMIXIN_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/lfeprebasismixin.hh000066400000000000000000000113731513634022200301650ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LFEPREBASISMIXIN_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LFEPREBASISMIXIN_HH #include #include #include #include #include #include namespace Dune::Functions { /** * \brief A pre-basis mixin class parametrized with a local finite-element and a DOF layout. * * \ingroup FunctionSpaceBasesImplementations * * This mixin class allows for simple construction of leaf pre-bases that are based on * a local finite-element and a DOF layout only. Examples are the refined Lagrange pre-bases, * or a hierarchical Lagrange pre-basis. Note that the layout is currently not capable of * describing a reordering of local DOFs if there are multiple assigned to a grid entity. * Thus higher-order continuous finite-elements are currently not possible to describe by * this mixin class. Note also that this mixin fixes the local finite-element type and thus * cannot handle mixed GeometryTypes. * * \b Example * \code{.cpp} template class RefinedP0PreBasis : public LFEPreBasisMixin> { using LFE = RefinedP0LocalFiniteElement; using Base = LFEPreBasisMixin; static const int dim = GV::dimension; public: RefinedP0PreBasis (const GV& gv) : Base(gv, [](GeometryType gt, int) { return (gt.dim()==dim) ? (1< class LFEPreBasisMixin : public LeafPreBasisMapperMixin< GV > { using Base = LeafPreBasisMapperMixin< GV >; public: //! The grid view that the FE basis is defined on using GridView = GV; //! Type of the tree node class Node; /** * \brief Constructor for a given grid view object and layout. * * Requires that the local-finite element is default constructible. */ template , int> = 0> LFEPreBasisMixin (const GridView& gv, MCMGLayout layout) : Base(gv, layout) , lfe_{} {} /** * \brief Constructor for a given grid view object, local finite-element and layout. * * Requires that the local-finite element is copyable or movable. */ template LFEPreBasisMixin (const GridView& gv, LFE_&& lfe, MCMGLayout layout) : Base(gv, layout) , lfe_(std::forward(lfe)) {} //! Create tree node Node makeNode () const { return Node(lfe_); } private: LFE lfe_; }; // deduction guide template LFEPreBasisMixin(const GV&, const LFE&, MCMGLayout) -> LFEPreBasisMixin; /** * \brief Leaf basis node that encapsulates a local finite-element * given from the LFEPreBasisMixin of type `LFE`. * * The Node implements the `LEafBasisNode` interface. Its stores a * pointer to the local finite-element given by the `LFEPreBasisMixin`. * Thus, the lifetime of the pre-basis must be greater than the lifetime * of this node. **/ template class LFEPreBasisMixin::Node : public LeafBasisNode { static constexpr int dim = GV::dimension; public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; using FiniteElement = LFE; //! Constructor; stores a pointer to the passed local finite-element `lfe`. explicit Node (const LFE& lfe) : lfe_{&lfe} , element_{nullptr} {} //! Return current element; might raise an error if unbound const Element& element () const { assert(!!element_); return *element_; } /** * \brief Return the LocalFiniteElement for the element we are bound to; might raise an error if unbound. * * The LocalFiniteElement implements the corresponding interfaces of the dune-localfunctions module */ const FiniteElement& finiteElement () const { assert(!!lfe_); return *lfe_; } //! Bind to element. Stores a pointer to the passed element reference. void bind (const Element& e) { element_ = &e; this->setSize(lfe_->size()); } protected: const FiniteElement* lfe_; const Element* element_; }; } // end namespace Dune::Functions #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_LFEPREBASISMIXIN_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/morleybasis.hh000066400000000000000000000557431513634022200271630ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_MORLEYBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_MORLEYBASIS_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * \file morleybasis.hh * \brief This file provides an implementation of the quadratic Morley finite element in 1 to 3 dimensions. * * The implementation is based on [Robert Kirby, A general approach to transforming * finite elements, 2018]. * It contains in the following order: * - A GlobalBasis typedef MorleyBasis * - A template H2LocalBasisTraits, extending the dune-localfunctions * LocalBasisTraits by an exported Hessiantype * - A template MorleyLocalFiniteElement providing an implementation * of the LocalFiniteElement interface, along with its subparts (Impl namespace) * - A template MorleyNode * - A template MorleyPreBasis * - A factory morley() in the BasisFactory namespace */ namespace Dune::Functions { template class MorleyPreBasis; /** \brief Nodal basis of a scalar quadratic Morley finite element space * * \ingroup FunctionSpaceBasesImplementations * * \note This only works for simplex grids. The Morley basis is only implemented for degree 2. * \note The Morley Finite element has the following properties: * - Its global space is not in \f$ H^1 \f$ but can be used for nonconforming method in \f$ H^2\f$. * - Its interpolation evaluates derivatives, i.e. you cannot interpolate into a lambda function. * - Strongly enforcing boundary conditions is not as simple as with langrange bases. * - It global space is not nested, i.e. the space on a refined grid is not a subspace of the * space on the coarser grid. * All arguments passed to the constructor will be forwarded to the constructor * of MorleyPreBasis. * * \tparam GV The GridView that the space is defined on * \tparam R The range type of the local basis */ template using MorleyBasis = DefaultGlobalBasis >; namespace Impl { /** * \brief Implementation of morley Polynomials * \tparam D Type to represent the field in the domain * \tparam R Type to represent the field in the range * \tparam dim Dimension of the domain simplex */ template class MorleyReferenceLocalBasis { public: static constexpr int dim = 2; using Traits = H2LocalBasisTraits, R, 1, FieldVector, FieldMatrix, FieldMatrix>; private: /** * \brief Get the Morley Coefficients Matrix * \return FieldMatrix * where size is the dimension of the cubic polynomial space * * This returns the basis transformation matrix from a monomial * basis to the Morley basis on the reference domain. * I.e. the i-th row of the returned matrix contains the * coefficients of the i-th Morley basis function with respect to a * monomial basis. The monomials are enumerated as in the * MonomialSet of the corresponding dimension and order. */ static constexpr auto getMorleyCoefficients() { // Define std::sqrt(2.) manually in double precision, // because std::sqrt is not constexpr before C++26. D sqrt2 = 0.5 * 1.414213562373095; return Dune::FieldMatrix({{1, -1, -1, 0, 2, 0}, {0, 0.5, 0.5, 0.5, -1, -0.5}, {0, 0.5, 0.5, -0.5, -1, 0.5}, {0, 0, 1, 0, 0, -1}, {0, -1., 0, 1., 0, 0}, {0, sqrt2, sqrt2, -sqrt2, -2. * sqrt2, -sqrt2}}); } static constexpr auto referenceBasisCoefficients = getMorleyCoefficients(); MonomialSet monomials; public: /** The number of basis functions in the basis */ static constexpr unsigned int size() { return 6; } /** The polynomial order of the basis */ static constexpr unsigned int order() { return 2; } /** \brief Evaluate function values of all shape functions at a given point * * \param[in] in The evaluation point * \param[out] out Values of all shape functions at that point */ void evaluateFunction(const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto monomialValues = monomials(in); multiplyWithCoefficentMatrix(referenceBasisCoefficients, monomialValues, out); } /** \brief Evaluate Jacobians of all shape functions at a given point * * \param[in] in The evaluation point * \param[out] out Jacobians of all shape functions at that point */ void evaluateJacobian(const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto monomialValues = derivative(monomials)(in); multiplyWithCoefficentMatrix(referenceBasisCoefficients, monomialValues, out); } /** \brief Evaluate Hessians of all shape functions at a given point * * \param[in] in The evaluation point * \param[out] out Hessians of all shape functions at that point */ void evaluateHessian(const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto monomialValues = derivative(derivative(monomials))(in); multiplyWithCoefficentMatrix(referenceBasisCoefficients, monomialValues, out); } /** \brief Evaluate partial derivatives of all shape functions at a given point * * \param[in] order The partial derivative to be computed, as a multi-index * \param[in] in The evaluation point * \param[out] out Jacobians of all shape functions at that point */ void partial(std::array order, const typename Traits::DomainType &in, std::vector &out) const { out.resize(size()); auto totalOrder = std::accumulate(order.begin(), order.end(), 0); if (totalOrder == 0) evaluateFunction(in, out); else if (totalOrder == 1) { evaluateJacobian(in,jacobiansBuffer_); std::size_t which = std::max_element(order.begin(), order.end()) - order.begin(); for (auto i : Dune::range(size())) out[i] = jacobiansBuffer_[i][0][which]; } else if (totalOrder == 2) { evaluateHessian(in, hessianBuffer_); std::size_t first, second; first = std::max_element(order.begin(), order.end()) - order.begin(); if (order[first] == 2) second = first; else { order[first] = 0; second = std::max_element(order.begin(), order.end()) - order.begin(); } for (auto i : Dune::range(size())) out[i] = hessianBuffer_[i][first][second]; } else DUNE_THROW(RangeError, "partial() not implemented for given order"); } private: mutable std::vector jacobiansBuffer_; mutable std::vector hessianBuffer_; }; /** \brief Associations of the Morley degrees of freedom to subentities of the * reference simplex */ class MorleyLocalCoefficients { public: using size_type = std::size_t; MorleyLocalCoefficients() { for (size_type i = 0; i < 3; ++i) { localKeys_[i] = LocalKey(i, 2, 0); // vertex dofs localKeys_[3 + i] = LocalKey(i, 1, 0); // edge dofs } } /** number of coefficients */ static constexpr size_type size() { return 6; } /** get i'th index */ constexpr LocalKey const &localKey(size_type i) const { return localKeys_[i]; } private: std::array localKeys_; }; /** * \brief Class that evaluates the push forwards of the global nodes of a * LocalFunction. It stretches the LocalInterpolation interface, because we * evaluate the derivatives of f. */ template class MorleyLocalInterpolation { using size_type = std::size_t; using FunctionalDescriptor = Dune::Functions::Impl::FunctionalDescriptor<2>; public: MorleyLocalInterpolation() { descriptors_[0] = FunctionalDescriptor(); descriptors_[1] = FunctionalDescriptor(); descriptors_[2] = FunctionalDescriptor(); descriptors_[3] = FunctionalDescriptor(1); descriptors_[4] = FunctionalDescriptor(1); descriptors_[5] = FunctionalDescriptor(1); } /** \brief bind the Interpolation to an element and a localState. */ template void bind(const Element &element, const std::bitset<3> &edgeOrientation) { auto geometry = element.geometry(); // get global Normals and midpoints auto refElement = Dune::referenceElement(geometry.type()); for (std::size_t i = 0; i < 3; ++i) { localVertices_[i] = refElement.position(i, 2); localMidpoints_[i] = refElement.position(i, 1); std::size_t lower = (i == 2) ? 1 : 0; std::size_t upper = (i == 0) ? 1 : 2; auto edge = geometry.global(refElement.position(upper, 2)) - geometry.global(refElement.position(lower, 2)); // normalize and orient edge /= edge.two_norm() * (edgeOrientation[i] ? -1. : 1.); // Rotation by pi/2. Note that Kirby rotates by 3*pi/2 globalNormals_[i] = {-edge[1], edge[0]}; } } /** \brief Evaluate a given function at the Lagrange nodes * * \tparam F Type of function to evaluate * \tparam C Type used for the values of the function * \param[in] f Function to evaluate * \param[out] out Array of function values */ template void interpolate(const F &f, std::vector &out) const { out.resize(6); auto&& df = derivative(f); for (size_type i = 0; i < 3; ++i) { out[i] = f(localVertices_[i]); out[3 + i] = squeezeTensor(df(localMidpoints_[i])).dot(globalNormals_[i]); } } const FunctionalDescriptor& functionalDescriptor(size_type i) const { return descriptors_[i]; } protected: std::array, 3> globalNormals_; std::array, 3> localMidpoints_; std::array, 3> localVertices_; std::array descriptors_; }; template using MorleyLocalBasisTraits = typename Impl::MorleyReferenceLocalBasis::Traits; /** \brief Morley finite element for simplices, as defined on the reference Element. * Note, that this is a non affine-equivalent finite element, that requires an additional transformation to the relate reference basis with the pullbacks of global basis. * For more Details, see . * * \tparam D Type used for domain coordinates * \tparam R Type used for function values * \tparam dim dimension of the reference element */ template class MorleyLocalFiniteElement : public Impl::TransformedFiniteElementMixin, MorleyLocalBasisTraits> { using Base = Impl::TransformedFiniteElementMixin< MorleyLocalFiniteElement, MorleyLocalBasisTraits>; friend class Impl::TransformedLocalBasis, MorleyLocalBasisTraits>; static constexpr int dim = 2; public: /** \brief Export number types, dimensions, etc. */ using size_type = std::size_t; using Traits = LocalFiniteElementTraits< Impl::TransformedLocalBasis, MorleyLocalBasisTraits>, Impl::MorleyLocalCoefficients, Impl::MorleyLocalInterpolation>; /** \brief Returns the assignment of the degrees of freedom to the element * subentities */ const typename Traits::LocalCoefficientsType &localCoefficients() const { return coefficients_; } /** \brief Returns object that evaluates degrees of freedom */ const typename Traits::LocalInterpolationType &localInterpolation() const { return interpolation_; } /** \brief The reference element that the local finite element is defined on */ static constexpr GeometryType type() { return GeometryTypes::simplex(dim); } /** The size of the transformed finite element. */ static constexpr size_type size() { return 6; } /** Binds the Finite Element to an element. */ template void bind(std::bitset<3> const& data, Element const &e) { edgeOrientation_ = data; fillMatrix(e.geometry()); interpolation_.bind(e, edgeOrientation_); } protected: /** \brief Returns the local basis, i.e., the set of shape functions */ Impl::MorleyReferenceLocalBasis const& referenceLocalBasis() const { return basis_; } /** Apply the transformation. Note that we do not distinguish for * Scalar/Vector/Matrix Type, * but only assume the Values to be Elements of a Vectorspace. * We assume random access containers. */ template void transform(InputValues const& inValues, OutputValues& outValues) const { // Here we cannot directly use // mat_.mv(inValues, outValues); // because mv expects the DenseVector interface. auto inValuesDenseVector = Impl::DenseVectorView(inValues); auto outValuesDenseVector = Impl::DenseVectorView(outValues); mat_.mv(inValuesDenseVector, outValuesDenseVector); } private: /** * \brief Fill the transformationmatrix \f$ M \f$ * * \tparam Geometry the Geometry class * \param geometry the geometry of the element we are bound to */ template void fillMatrix(Geometry const &geometry) { std::array B_11; std::array B_12; std::array l_inv; std::array, 3> referenceTangents; std::array, 3> globalTangents; // By default, edges point from the vertex with the smaller index // to the vertex with the larger index. // get local and global Tangents auto refElement = Dune::referenceElement(geometry.type()); auto x = refElement.position(0,0); for (std::size_t i = 0; i < 3; ++i) { std::size_t lower = (i == 2) ? 1 : 0; std::size_t upper = (i == 0) ? 1 : 2; auto edge = refElement.position(upper, 2) - refElement.position(lower, 2); referenceTangents[i] = edge / edge.two_norm(); auto globalEdge = geometry.global(refElement.position(upper, 2)) - geometry.global(refElement.position(lower, 2)); l_inv[i] = 1. / globalEdge.two_norm(); globalTangents[i] = globalEdge * l_inv[i]; } auto jacobianTransposed = geometry.jacobianTransposed(x); for (std::size_t i = 0; i < 3; ++i) { B_11[i] = -referenceTangents[i][1] *(-globalTangents[i][1] * jacobianTransposed[0][0] + globalTangents[i][0] * jacobianTransposed[0][1]) + referenceTangents[i][0] *(-globalTangents[i][1] * jacobianTransposed[1][0] + globalTangents[i][0] * jacobianTransposed[1][1]); B_12[i] = -referenceTangents[i][1] *(globalTangents[i][0] * jacobianTransposed[0][0] + globalTangents[i][1] * jacobianTransposed[0][1]) + referenceTangents[i][0] *(globalTangents[i][0] * jacobianTransposed[1][0] + globalTangents[i][1] * jacobianTransposed[1][1]); } // Actually setup matrix int sign = -1; for (std::size_t i = 0; i < 3; ++i) { mat_[i][i] = 1.; for (std::size_t j = 0; j < 3; ++j) { if (j != (2 - i)) // dune specific edge order { mat_[j][3 + i] = sign * B_12[i] * l_inv[i]; sign *= -1; } } mat_[3 + i][3 + i] = (edgeOrientation_[i] ? -1. : 1.) * B_11[i]; } } // a finite element consists of a basis, coeffiecents and an interpolation typename Impl::MorleyReferenceLocalBasis basis_; typename Traits::LocalCoefficientsType coefficients_; typename Traits::LocalInterpolationType interpolation_; // This is the matrix M in Kirbys paper Dune::FieldMatrixmat_; // the local state, i.e. a collection of global information restricted to this element std::bitset<3> edgeOrientation_; }; } // end namespace Impl // ***************************************************************************** // This is the reusable part of the basis. It contains // // MorleyPreBasis // MorleyNode // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template class MorleyNode : public LeafBasisNode { using Mapper = Dune::MultipleCodimMultipleGeomTypeMapper; public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; using FiniteElement = typename Impl::MorleyLocalFiniteElement; MorleyNode(Mapper const& m, std::vector> const& data) : mapper_(&m), data_(&data) {} //! Return current element, throw if unbound Element const &element() const { return *element_; } /** \brief Return the LocalFiniteElement for the element we are bound to * * The LocalFiniteElement implements the corresponding interfaces of the * dune-localfunctions module */ FiniteElement const &finiteElement() const { return finiteElement_; } //! Bind to element. void bind(Element const &e) { element_ = &e; finiteElement_.bind((*data_)[mapper_->index(e)], *element_); this->setSize(finiteElement_.size()); } //! The order of the local basis. unsigned int order() const { return finiteElement_.localBasis().order(); } protected: FiniteElement finiteElement_; Element const* element_; Mapper const* mapper_; std::vector> const* data_; }; /** * \brief A pre-basis for a Morleybasis * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The grid view that the FE basis is defined on * \tparam R Range type used for shape function values * \note This only works for simplex grids */ template class MorleyPreBasis : public LeafPreBasisMapperMixin { using Base = LeafPreBasisMapperMixin; using SubEntityMapper = Dune::MultipleCodimMultipleGeomTypeMapper; using Element = typename GV::template Codim<0>::Entity; using D = typename GV::ctype; static const std::size_t dim = GV::dimension; // helper methods to assign each subentity the number of dofs. Used by the LeafPreBasisMapperMixin. static constexpr auto morleyMapperLayout(Dune::GeometryType type, int gridDim) { assert(gridDim == 2); if (type.isVertex()) return 1; // one evaluation dof per vertex if (type.isLine()) return 1; if ((type.isTriangle()) ) return 0; else return 0; } public: //! The grid view that the FE basis is defined on using GridView = GV; //! Type used for indices and size information using size_type = std::size_t; //! Template mapping root tree path to type of created tree node using Node = MorleyNode; //! Constructor for a given grid view object MorleyPreBasis(const GV &gv) : Base(gv, morleyMapperLayout) , mapper_({gv, mcmgElementLayout()}) { data_ = Impl::computeEdgeOrientations(mapper_); } //! Update the stored grid view, to be called if the grid has changed void update(GridView const &gv) { Base::update(gv); data_ = Impl::computeEdgeOrientations(mapper_); } /** * \brief Create tree node */ Node makeNode() const { return Node{mapper_, data_}; } protected: SubEntityMapper mapper_; std::vector> data_; }; // class MorleyPreBasis namespace BasisFactory { /** * \brief construct a PreBasisFactory for the Morley Finite Element * * \tparam R RangeFieldType * \return the PreBasisFactory */ template auto morley() { return [=](auto const &gridView) { return MorleyPreBasis, R>(gridView); }; } } // end namespace BasisFactory } // end namespace Dune::Functions #endif dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/nedelecbasis.hh000066400000000000000000000243231513634022200272410ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_NEDELECBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_NEDELECBASIS_HH #include #include #include #include #include #include #include #include #include #include namespace Dune::Functions { namespace Impl { template class Nedelec1stKindLocalFiniteElementMap { using D = typename GV::ctype; constexpr static bool hasFixedElementType = Capabilities::hasSingleGeometryType::v; using CubeFiniteElement = Nedelec1stKindCubeLocalFiniteElement; using SimplexFiniteElement = Nedelec1stKindSimplexLocalFiniteElement; public: using T = LocalBasisTraits, R, dim, FieldVector, FieldMatrix >; constexpr static unsigned int topologyId = Capabilities::hasSingleGeometryType::topologyId; // meaningless if hasFixedElementType is false constexpr static GeometryType type = GeometryType(topologyId, GV::dimension); using FiniteElement = std::conditional_t, LocalFiniteElementVariant >; static std::size_t numVariants(GeometryType type) { if (order!=1) // I am not sure whether the formula below is correct for all orders. DUNE_THROW(NotImplemented, "Only Nedelec elements of order 1 are implemented!"); auto numEdges = referenceElement(type).size(dim-1); return power(2,numEdges); } Nedelec1stKindLocalFiniteElementMap(const GV& gv) : elementMapper_(gv, mcmgElementLayout()), orientation_(gv.size(0)) { update(gv); } void update(const GV& gv) { elementMapper_.update(gv); orientation_.resize(gv.size(0)); // create all variants if constexpr (hasFixedElementType) { variants_.resize(numVariants(type)); for (size_t i = 0; i < numVariants(type); i++) variants_[i] = FiniteElement(i); } else { // for mixed grids add offset for cubes variants_.resize(numVariants(GeometryTypes::simplex(dim)) + numVariants(GeometryTypes::cube(dim))); for (size_t i = 0; i < numVariants(GeometryTypes::simplex(dim)); i++) variants_[i] = SimplexFiniteElement(i); for (size_t i = 0; i < numVariants(GeometryTypes::cube(dim)); i++) variants_[i + numVariants(GeometryTypes::simplex(dim))] = CubeFiniteElement(i); } // compute orientation for all elements const auto& indexSet = gv.indexSet(); for(const auto& element : elements(gv)) { const auto& refElement = referenceElement(element); auto elementIndex = elementMapper_.index(element); orientation_[elementIndex] = 0; for (std::size_t i=0; iglobalV1) || (localV0>localV1 && globalV0 const auto& find(const Element& element) const { return variants_[orientation_[elementMapper_.index(element)]]; } private: std::vector variants_; Dune::MultipleCodimMultipleGeomTypeMapper elementMapper_; std::vector orientation_; }; } // namespace Impl // ***************************************************************************** // This is the reusable part of the basis. It contains // // NedelecPreBasis // NedelecNode // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template class NedelecNode; template class NedelecPreBasis : public LeafPreBasisMapperMixin { static const int dim = GV::dimension; static_assert(kind==1, "Only the Nedelec basis of the first kind is currently implemented!"); using FiniteElementMap = typename Impl::Nedelec1stKindLocalFiniteElementMap; using Mapper = Dune::MultipleCodimMultipleGeomTypeMapper; public: /** \brief The grid view that the FE space is defined on */ using GridView = GV; using size_type = std::size_t; using Node = NedelecNode; /** \brief Constructor for a given grid view object */ NedelecPreBasis(const GridView& gv) : LeafPreBasisMapperMixin(gv, mcmgLayout(Dim<1>{})) , finiteElementMap_(gv) { if (kind!=1) DUNE_THROW(NotImplemented, "Only Nedelec elements of the first kind are implemented!"); // There is no inherent reason why the basis shouldn't work for grids with more than two // element types. Somebody simply has to sit down and implement the missing bits. if (gv.indexSet().types(0).size() > 2) DUNE_THROW(NotImplemented, "Nédélec basis is only implemented for grids with simplex and cube elements"); for(auto type : gv.indexSet().types(0)) if (!type.isSimplex() && !type.isCube()) DUNE_THROW(NotImplemented, "Nédélec basis is only implemented for grids with simplex or cube elements."); if (order>1) DUNE_THROW(NotImplemented, "Only first-order elements are implemented"); if (dim!=2 && dim!=3) DUNE_THROW(NotImplemented, "Only 2d and 3d Nédélec elements are implemented"); } /** \brief Update the stored grid view, to be called if the grid has changed */ void update (const GridView& gv) { LeafPreBasisMapperMixin::update(gv); finiteElementMap_.update(gv); } /** * \brief Create tree node */ Node makeNode() const { return Node{&finiteElementMap_}; } protected: FiniteElementMap finiteElementMap_; }; template class NedelecNode : public LeafBasisNode { static const int dim = GV::dimension; public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; static_assert(kind==1, "Only Nedelec elements of the first kind are implemented!"); using FiniteElementMap = typename Impl::Nedelec1stKindLocalFiniteElementMap; using FiniteElement = Impl::GlobalValuedLocalFiniteElement; NedelecNode(const FiniteElementMap* finiteElementMap) : element_(nullptr), finiteElementMap_(finiteElementMap) { } //! Return current element, throw if unbound const Element& element() const { return *element_; } /** \brief Return the LocalFiniteElement for the element we are bound to * * The LocalFiniteElement implements the corresponding interfaces of the dune-localfunctions module */ const FiniteElement& finiteElement() const { return finiteElement_; } //! Bind to element. void bind(const Element& e) { element_ = &e; finiteElement_.bind((finiteElementMap_->find(*element_)), e); this->setSize(finiteElement_.size()); } protected: FiniteElement finiteElement_; const Element* element_; const FiniteElementMap* finiteElementMap_; }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can create a Nédélec pre-basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam kind Kind of the Nédélec element (1 or 2) * \tparam order Order of the Nédélec element (lowest order is '1') * \tparam Range Number type used for shape function values */ template auto nedelec() { return [](const auto& gridView) { return NedelecPreBasis, Range, kind, order>(gridView); }; } } // end namespace BasisFactory // ***************************************************************************** // This is the actual global basis implementation based on the reusable parts. // ***************************************************************************** /** \brief Basis of a k-th-order Nédélec finite element space * * \tparam GV The GridView that the space is defined on * \tparam Range Number type used for shape function values * \tparam kind Kind of the basis: 1 (for Nédélec element of the first kind) or 2 * \tparam order The order of the basis (lowest order is '1') */ template using NedelecBasis = DefaultGlobalBasis >; } // end namespace Dune::Functions #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_NEDELECBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/nodes.hh000066400000000000000000000271121513634022200257270ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_NODES_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_NODES_HH #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_DUNE_TYPETREE #include #endif namespace Dune { namespace Functions { namespace Impl { // This class encapsulates the access to the setOffset() // and setTreeIndex() methods of a node. This way we // can hide the methods from the user but still provide // access where this is needed. struct BasisNodeSetupHelper { template static void setSize(Node& node, const size_type size) { node.setSize(size); } template static void setOffset(Node& node, const size_type offset) { node.setOffset(offset); } template static void setTreeIndex(Node& node, const size_type index) { node.setTreeIndex(index); } }; // A mixin class for generalized child access from // multiple indices or a tree path. The derived class // only has to provide the child(i) method with // a single index for accessing direct children. template class ChildAccessMixIn { Impl& asImpl() { return static_cast(*this); } const Impl& asImpl() const { return static_cast(*this); } public: /** * \brief Const access to descendent node by indices * * \param ii Indices of descendents */ template const auto& child(II... ii) const requires (sizeof...(II) != 1) { return Dune::TypeTree::child(asImpl(), ii...); } /** * \brief Mutable access to descendent node by indices * * \param ii Indices of descendents */ template auto& child(II... ii) requires (sizeof...(II) != 1) { return Dune::TypeTree::child(asImpl(), ii...); } /** * \brief Const access to descendent node by tree path * * \param treePath Tree path identifying the descendent */ template const auto& child(Dune::HybridMultiIndex treePath) const { return Dune::TypeTree::child(asImpl(), treePath); } /** * \brief Mutable access to descendent node by tree path * * \param treePath Tree path identifying the descendent */ template auto& child(Dune::HybridMultiIndex treePath) { return Dune::TypeTree::child(asImpl(), treePath); } }; } // end namespace Impl class BasisNodeMixin { friend struct Impl::BasisNodeSetupHelper; public: using size_type = std::size_t; BasisNodeMixin() : offset_(0), size_(0), treeIndex_(0) {} size_type localIndex(size_type i) const { assert(i < size_); return offset_ + i; } /** * \brief Obtain the number of basis function in the local node. * * Notice that it is undefined behaviour to access the `element()` * and `finiteElement()` methods of the node if it is empty, i.e., * if its size is zero. */ size_type size() const { return size_; } /** * \brief Check if the node is empty * * This is equivalent to `size()==0`. * Notice that it is undefined behaviour to access the `element()` * and `finiteElement()` methods of the node if it is empty, i.e., * if its size is zero. */ bool empty() const { return (size_ == 0); } size_type treeIndex() const { return treeIndex_; } protected: size_type offset() const { return offset_; } void setOffset(const size_type offset) { offset_ = offset; } void setSize(const size_type size) { size_ = size; } void setTreeIndex(size_type treeIndex) { treeIndex_ = treeIndex; } private: size_type offset_; size_type size_; size_type treeIndex_; }; class LeafBasisNode : public BasisNodeMixin { public: // Begin of node interface static constexpr auto degree() { return Dune::index_constant<0>{}; } // Historic node interface static const bool isLeaf [[deprecated]] = true; static const bool isPower [[deprecated]] = false; static const bool isComposite [[deprecated]] = false; #ifdef HAVE_DUNE_TYPETREE using NodeTag [[deprecated]] = Dune::TypeTree::LeafNodeTag; #endif // End of node interface }; template class InnerBasisNodeMixin : public BasisNodeMixin { public: void bind(const Element& entity) { Node& self = *static_cast(this); std::size_t offset = this->offset(); Dune::Hybrid::forEach(Dune::range(self.degree()), [&](auto i) { bindTree(self.child(i), entity, offset); offset += self.child(i).size(); }); this->setSize(offset - this->offset()); } }; template class PowerBasisNode : public InnerBasisNodeMixin, typename T::Element>, public Impl::ChildAccessMixIn> { public: // Begin of node interface static constexpr auto degree() { return Dune::index_constant{}; } template requires (std::is_convertible_v) const auto& child(Index i) const { return children_[i].value(); } template requires (std::is_convertible_v) auto& child(Index i) { return children_[i].value(); } using Impl::ChildAccessMixIn>::child; // Historic node interface using ChildType [[deprecated]] = T; static const bool isLeaf [[deprecated]] = false; static const bool isPower [[deprecated]] = true; static const bool isComposite [[deprecated]] = false; #ifdef HAVE_DUNE_TYPETREE using NodeTag [[deprecated]] = Dune::TypeTree::PowerNodeTag; #endif // End of node interface using Element = typename T::Element; PowerBasisNode() = default; const Element& element() const { return child(Dune::Indices::_0).element(); } template void setChild(Index i, TT&& t) { children_[i].emplace(std::forward(t)); } private: std::array, n> children_; }; template class DynamicPowerBasisNode : public InnerBasisNodeMixin, typename T::Element>, public Impl::ChildAccessMixIn> { public: // Begin of node interface std::size_t degree() const { return children_.size(); } template requires (std::is_convertible_v) const auto& child(Index i) const { return children_[i].value(); } template requires (std::is_convertible_v) auto& child(Index i) { return children_[i].value(); } using Impl::ChildAccessMixIn>::child; // Historic node interface using ChildType [[deprecated]] = T; static const bool isLeaf [[deprecated]] = false; static const bool isPower [[deprecated]] = true; static const bool isComposite [[deprecated]] = false; #ifdef HAVE_DUNE_TYPETREE using NodeTag [[deprecated]] = Dune::TypeTree::DynamicPowerNodeTag; #endif // End of node interface using Element = typename T::Element; DynamicPowerBasisNode (std::size_t children) : children_(children) {} const Element& element() const { return child(Dune::Indices::_0).element(); } template void setChild(Index i, TT&& t) { children_[i].emplace(std::forward(t)); } private: std::vector> children_; }; template class CompositeBasisNode : public InnerBasisNodeMixin, typename TypeListEntry_t<0, TypeList>::Element>, public Impl::ChildAccessMixIn> { public: // Begin of node interface static constexpr auto degree() { return Dune::index_constant{}; } template const auto& child(Dune::index_constant ii) const { return children_[ii].value(); } template auto& child(Dune::index_constant ii) { return children_[ii].value(); } using Impl::ChildAccessMixIn>::child; // Historic node interface using ChildTypes [[deprecated]] = std::tuple; static const bool isLeaf [[deprecated]] = false; static const bool isPower [[deprecated]] = false; static const bool isComposite [[deprecated]] = true; #ifdef HAVE_DUNE_TYPETREE using NodeTag [[deprecated]] = Dune::TypeTree::CompositeNodeTag; #endif template struct [[deprecated]] Child { static_assert((k < degree()), "child index out of range"); //! The type of the child. using Type = typename std::tuple_element_t>; using type = Type; }; // End of node interface using Element = typename std::tuple_element_t<0, std::tuple>::Element; CompositeBasisNode() = default; explicit CompositeBasisNode(const T&... children) : children_(children...) {} const Element& element() const { return child(Dune::Indices::_0).element(); } template void setChild (TT&& t, Dune::index_constant ii = {}) { children_[ii].emplace(std::forward(t)); } private: Dune::TupleVector...> children_; }; template void bindTree(Tree& tree, const Entity& entity, std::size_t offset = 0) { Impl::BasisNodeSetupHelper::setOffset(tree, offset); tree.bind(entity); } template void initializeTree(Tree& tree, std::size_t treeIndexOffset = 0) { Dune::TypeTree::forEachNode(tree, [&](auto& node, const auto& treePath) { Impl::BasisNodeSetupHelper::setTreeIndex(node, treeIndexOffset); ++treeIndexOffset; }); } } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_NODES_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/periodicbasis.hh000066400000000000000000000152721513634022200274430ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_PERIODICBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_PERIODICBASIS_HH #include #include #include #include #include #include #include #include namespace Dune::Functions { namespace BasisFactory { // The PeriodicBasis class is in the Experimental namespace because we are // not completely sure yet whether we like it. We reserve the right to // modify it without advance warning. Use at your own risk! namespace Experimental { /** * \brief Container storing identified indices for a periodic basis * * This class is intended to be passed to the BasisFactory::periodic() * function. * The class stores a set of index pairs which should be * identified in order to construct a basis with periodic functions. */ class PeriodicIndexSet { using IndexPairSet = std::set>; public: /** * \brief Insert a pair of indices * * The two bases functions associated to the provided * indices will be combined into a single basis function * by associating them to a shared global index. */ void unifyIndexPair(std::size_t a, std::size_t b) { if (a>b) std::swap(a,b); if (a==b) return; indexPairSet_.insert(std::make_pair(a,b)); } const auto& indexPairSet() const { return indexPairSet_; } private: IndexPairSet indexPairSet_; }; namespace Impl { // An index transformation for a TransformedIndexPreBasis // implementing periodic functions by merging indices. // Currently only flat indices are supported. class PeriodicIndexingTransformation { public: static constexpr std::size_t minIndexSize = 1; static constexpr std::size_t maxIndexSize = 1; template PeriodicIndexingTransformation(const RawPreBasis& rawPreBasis, const IndexPairSet& indexPairSet) { static_assert(RawPreBasis::maxMultiIndexSize==1, "PeriodicIndexingTransformation is only implemented for flat multi-indices"); std::size_t invalid = {std::numeric_limits::max()}; mappedIdx_.resize(rawPreBasis.size(), invalid); numIndices_ = 0; std::size_t i = 0; for(const auto& [a, b] : indexPairSet) { for(; i<=a; ++i) if (mappedIdx_[i] == invalid) mappedIdx_[i] = numIndices_++; mappedIdx_[b] = mappedIdx_[a]; } for(; i void transformIndex(MultiIndex& multiIndex, const PreBasis& preBasis) const { multiIndex = {{ mappedIdx_[multiIndex[0]] }}; } template std::size_t size(const Prefix& prefix, const PreBasis& preBasis) const { if (prefix.size() == 1) return 0; return numIndices_; } template auto dimension(const PreBasis& preBasis) const { return numIndices_; } //! Return a flat container descriptor for this preBasis template auto containerDescriptor(const PreBasis& preBasis) const { return Dune::Functions::containerDescriptor(preBasis); } private: std::vector mappedIdx_; std::size_t numIndices_; }; template class PeriodicPreBasisFactory { public: PeriodicPreBasisFactory() {} template PeriodicPreBasisFactory(RPBI&& rawPreBasisIndicator, PIS&& periodicIndexSet) : rawPreBasisIndicator_(std::forward(rawPreBasisIndicator)), periodicIndexSet_(std::forward(periodicIndexSet)) {} template,RawPreBasisIndicator>(), int> = 0> auto operator()(const GridView& gridView) const { const auto& rawPreBasis = rawPreBasisIndicator_.preBasis(); auto transformation = PeriodicIndexingTransformation(rawPreBasis, periodicIndexSet_.indexPairSet()); return Dune::Functions::Experimental::TransformedIndexPreBasis(std::move(rawPreBasis), std::move(transformation)); } template,RawPreBasisIndicator>(), int> = 0> auto operator()(const GridView& gridView) const { const auto& rawPreBasis = rawPreBasisIndicator_; auto transformation = PeriodicIndexingTransformation(rawPreBasis, periodicIndexSet_.indexPairSet()); return Dune::Functions::Experimental::TransformedIndexPreBasis(std::move(rawPreBasis), std::move(transformation)); } template,RawPreBasisIndicator>(), int> = 0, std::enable_if_t,RawPreBasisIndicator>(), int> = 0> auto operator()(const GridView& gridView) const { auto rawPreBasis = rawPreBasisIndicator_(gridView); rawPreBasis.initializeIndices(); auto transformation = PeriodicIndexingTransformation(rawPreBasis, periodicIndexSet_.indexPairSet()); return Dune::Functions::Experimental::TransformedIndexPreBasis(std::move(rawPreBasis), std::move(transformation)); } private: RawPreBasisIndicator rawPreBasisIndicator_; PeriodicIndexSet periodicIndexSet_; }; } // end namespace BasisFactory::Impl /** * \brief Create a pre-basis factory that can create a periodic pre-basis * * \param rawPreBasisIndicator Object encoding the raw non-periodic basis * \param periodicIndexSet A PeriodicIndexSet containing the indices to be identified * * The rawPreBasisIndicator can either be a PreBasisFactory, a PreBasis, or a GlobalBasis. * In the latter two cases the multi index type of those bases and the periodic * basis to be constructed, has to coincide. Both arguments will be copied. * Currently only wrapped bases with flat indices are supported. * * \ingroup FunctionSpaceBasesImplementations */ template auto periodic( RawPreBasisIndicator&& rawPreBasisIndicator, PIS&& periodicIndexSet ) { return Impl::PeriodicPreBasisFactory>( std::forward(rawPreBasisIndicator), std::forward(periodicIndexSet)); } } // end namespace Experimental } // end namespace BasisFactory } // end namespace Dune::Functions #endif // DUNE_FUFEM_PERIODICBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/powerbasis.hh000066400000000000000000000135731513634022200270030ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_POWERBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_POWERBASIS_HH #include #include #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { // ***************************************************************************** // This is the reusable part of the power bases. It contains // // PowerPreBasis // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** /** * \brief A pre-basis for power bases * * This pre-basis represents a power of a given pre-basis. * Its node type is a PowerBasisNodes for the given subnode. * * \tparam IMS An IndexMergingStrategy used to merge the global indices of the child factories * \tparam SPB The child pre-basis * \tparam C The exponent of the power node */ template class PowerPreBasis : public DynamicPowerPreBasis { using Base = DynamicPowerPreBasis; public: //! The child pre-basis using SubPreBasis = SPB; //! Template mapping root tree path to type of created tree node using Node = PowerBasisNode; //! Type used for indices and size information using size_type = typename Base::size_type; //! Strategy used to merge the global indices of the child factories using IndexMergingStrategy = IMS; //! Number of children provided as an integral constant inline static constexpr std::integral_constant children = {}; /** * \brief Constructor for given child pre-basis objects for static size of the power-basis * * The child factories will be stored as copies */ template = 0, enableIfConstructible = 0> explicit PowerPreBasis(SFArgs&&... sfArgs) : Base(std::size_t(C), std::forward(sfArgs)...) {} /** * \brief Create tree node */ Node makeNode() const { Node node{}; for (std::size_t i=0; i{}); } //! Return number of possible values for next position in multi index template size_type size(const SizePrefix& prefix) const { return Base::sizeImpl(prefix, children, IndexMergingStrategy{}); } //! Maps from subtree index set [0..size-1] to a globally unique multi index in global basis template requires Dune::TypeTree::Concept::UniformInnerTreeNode It indices(const NodeType& node, It it) const { return Base::indicesImpl(node, it, children, IndexMergingStrategy{}); } //! Return the associated container descriptor auto containerDescriptor() const { return Base::containerDescriptorImpl(children); } }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can build a PowerPreBasis * * \ingroup FunctionSpaceBasesImplementations * * \tparam ChildPreBasisFactory Types of child pre-basis factory * \tparam IndexMergingStrategy An IndexMergingStrategy type * \param childPreBasisFactory Child pre-basis factory * * This overload can be used to explicitly supply an IndexMergingStrategy. */ template auto power(ChildPreBasisFactory&& childPreBasisFactory, const IndexMergingStrategy&) { return [childPreBasisFactory](const auto& gridView) { auto childPreBasis = childPreBasisFactory(gridView); return PowerPreBasis(std::move(childPreBasis)); }; } /** * \brief Create a factory builder that can build a PowerPreBasis * * \ingroup FunctionSpaceBasesImplementations * * \tparam ChildPreBasisFactory Types of child pre-basis factory * \param childPreBasisFactory Child pre-basis factory * * This overload will select the BasisFactory::BlockedInterleaved strategy. */ template [[deprecated("Using the method `power` without an explicit index merging strategy" " will change its meaning after the release of dune-functions 2.11." " Previously, the default merging strategy was `BlockedInterleaved`," " but this will change to `FlatInterleaved`." " Change the call to `power(..., blockedInterleaved())` to retain the old behavior.")]] auto power(ChildPreBasisFactory&& childPreBasisFactory) { return [childPreBasisFactory](const auto& gridView) { auto childPreBasis = childPreBasisFactory(gridView); return PowerPreBasis(std::move(childPreBasis)); }; } } // end namespace BasisFactory } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_POWERBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/rannacherturekbasis.hh000066400000000000000000000152221513634022200306540ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_RANNACHERTUREKBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_RANNACHERTUREKBASIS_HH #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { // ***************************************************************************** // This is the reusable part of the basis. It contains // // RannacherTurekPreBasis // RannacherTurekNode // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template class RannacherTurekNode; template class RannacherTurekPreBasis; /** * \brief Pre-basis for a Rannacher-Turek basis * * \ingroup FunctionSpaceBasesImplementations * * These are Crouzeix-Raviart-elements for quadrilateral elements. * See * Rolf Rannacher and Stefan Turek. Simple nonconforming quadrilateral Stokes * element. Numerical Methods for Partial Differential Equations, 8:97–111, 1992. * * \tparam GV The grid view that the FE basis is defined on */ template class RannacherTurekPreBasis : public LeafPreBasisMixin< RannacherTurekPreBasis > { static const int dim = GV::dimension; public: //! The grid view that the FE basis is defined on using GridView = GV; //! Type used for indices and size information using size_type = std::size_t; //! Template mapping root tree path to type of created tree node using Node = RannacherTurekNode; //! Constructor for a given grid view object RannacherTurekPreBasis(const GridView& gv) : gridView_(gv) { for(auto type : gv.indexSet().types(0)) if (!type.isSimplex() && !type.isCube()) DUNE_THROW(Dune::NotImplemented, "Rannacher-Turek or Crouzeix-Raviart elements are only implemented for grids with simplex or cube elements."); } //! Initialize the global indices void initializeIndices() {} //! Obtain the grid view that the basis is defined on const GridView& gridView() const { return gridView_; } //! Update the stored grid view, to be called if the grid has changed void update (const GridView& gv) { gridView_ = gv; } /** * \brief Create tree node */ Node makeNode() const { return Node{}; } //! Same as size(prefix) with empty prefix size_type dimension() const { return (size_type)(gridView_.size(1)); } //! Get the maximal number of DOFs associated to node for any element size_type maxNodeSize() const { return 2*GV::dimension; } template It indices(const Node& node, It it) const { for (size_type i = 0, end = node.size() ; i < end ; ++i, ++it) { Dune::LocalKey localKey = node.finiteElement().localCoefficients().localKey(i); const auto& gridIndexSet = gridView().indexSet(); const auto& element = node.element(); *it = {{ (size_type)(gridIndexSet.subIndex(element,localKey.subEntity(),1)) }}; } return it; } protected: GridView gridView_; }; template class RannacherTurekNode : public LeafBasisNode { static const int dim = GV::dimension; static const int maxSize = 2*dim; constexpr static bool hasFixedElementType = Capabilities::hasSingleGeometryType::v; using CubeFiniteElement = RannacherTurekLocalFiniteElement; using SimplexFiniteElement = CrouzeixRaviartLocalFiniteElement; constexpr static unsigned int topologyId = Capabilities::hasSingleGeometryType::topologyId; // meaningless if hasFixedElementType is false constexpr static GeometryType type = GeometryType(topologyId, GV::dimension); public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; using FiniteElement = std::conditional_t, LocalFiniteElementVariant >; RannacherTurekNode() : finiteElement_(), element_(nullptr) {} //! Return current element, throw if unbound const Element& element() const { return *element_; } /** \brief Return the LocalFiniteElement for the element we are bound to * * The LocalFiniteElement implements the corresponding interfaces of the dune-localfunctions module */ const FiniteElement& finiteElement() const { return finiteElement_; } //! Bind to element. void bind(const Element& e) { element_ = &e; if constexpr (!hasFixedElementType) finiteElement_ = e.type().isCube() ? static_cast(CubeFiniteElement()) : static_cast(SimplexFiniteElement()) ; this->setSize(finiteElement_.size()); } protected: FiniteElement finiteElement_; const Element* element_; }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can create a Rannacher-Turek pre-basis * * \ingroup FunctionSpaceBasesImplementations */ template auto rannacherTurek() { return [](const auto& gridView) { return RannacherTurekPreBasis>(gridView); }; } } // end namespace BasisFactory /** \brief Rannacher-Turek basis * * \ingroup FunctionSpaceBasesImplementations * * These are Crouzeix-Raviart-elements for quadrilateral elements. * See * Rolf Rannacher and Stefan Turek. Simple nonconforming quadrilateral Stokes * element. Numerical Methods for Partial Differential Equations, 8:97–111, 1992. * * \tparam GV The GridView that the space is defined on */ template using RannacherTurekBasis = DefaultGlobalBasis >; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_RANNACHERTUREKBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/raviartthomasbasis.hh000066400000000000000000000331111513634022200305210ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_RAVIARTTHOMASBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_RAVIARTTHOMASBASIS_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { namespace Impl { template struct RaviartThomasSimplexLocalInfo { // Dummy type, must be something that we can have a std::unique_ptr to using FiniteElement = void*; }; template struct RaviartThomasSimplexLocalInfo<2,D,R,0> { using FiniteElement = RT02DLocalFiniteElement; }; template struct RaviartThomasSimplexLocalInfo<2,D,R,1> { using FiniteElement = RT12DLocalFiniteElement; }; template struct RaviartThomasSimplexLocalInfo<3,D,R,0> { using FiniteElement = RT03DLocalFiniteElement; }; template struct RaviartThomasCubeLocalInfo { // Dummy type, must be something that we can have a std::unique_ptr to using FiniteElement = void*; }; template struct RaviartThomasCubeLocalInfo<2,D,R,0> { using FiniteElement = RT0Cube2DLocalFiniteElement; }; template struct RaviartThomasCubeLocalInfo<2,D,R,1> { using FiniteElement = RT1Cube2DLocalFiniteElement; }; template struct RaviartThomasCubeLocalInfo<2,D,R,2> { using FiniteElement = RT2Cube2DLocalFiniteElement; }; template struct RaviartThomasCubeLocalInfo<3,D,R,0> { using FiniteElement = RT0Cube3DLocalFiniteElement; }; template struct RaviartThomasCubeLocalInfo<3,D,R,1> { using FiniteElement = RT1Cube3DLocalFiniteElement; }; template struct RaviartThomasPyramidLocalInfo { // Dummy type, must be something that we can have a std::unique_ptr to using FiniteElement = void*; }; template struct RaviartThomasPyramidLocalInfo<3,D,R,0> { using FiniteElement = RT0PyramidLocalFiniteElement; }; template struct RaviartThomasPrismLocalInfo { // Dummy type, must be something that we can have a std::unique_ptr to using FiniteElement = void*; }; template struct RaviartThomasPrismLocalInfo<3,D,R,0> { using FiniteElement = RT0PrismLocalFiniteElement; }; template class RaviartThomasLocalFiniteElementMap { using D = typename GV::ctype; constexpr static bool hasFixedElementType = Capabilities::hasSingleGeometryType::v; using CubeFiniteElement = typename RaviartThomasCubeLocalInfo::FiniteElement; using SimplexFiniteElement = typename RaviartThomasSimplexLocalInfo::FiniteElement; using PyramidFiniteElement = typename RaviartThomasPyramidLocalInfo::FiniteElement; using PrismFiniteElement = typename RaviartThomasPrismLocalInfo::FiniteElement; public: using T = LocalBasisTraits, R, dim, FieldVector, FieldMatrix >; constexpr static unsigned int topologyId = Capabilities::hasSingleGeometryType::topologyId; // meaningless if hasFixedElementType is false constexpr static GeometryType type = GeometryType(topologyId, GV::dimension); using FiniteElement = std::conditional_t, std::conditional_t, LocalFiniteElementVariant > >; // Each element facet can have its orientation reversed, hence there are // 2^#facets different variants. static std::size_t numVariants(GeometryType type) { auto numFacets = referenceElement(type).size(1); return power(2,numFacets); } RaviartThomasLocalFiniteElementMap(const GV& gv) : elementMapper_(gv, mcmgElementLayout()) { update(gv); } void update (const GV& gv) { elementMapper_.update(gv); if constexpr (hasFixedElementType) { variants_.resize(numVariants(type)); for (size_t i = 0; i < numVariants(type); i++) variants_[i] = FiniteElement(i); } else { // for mixed grids add offset for cubes size_t numVariantsSimplex = numVariants(GeometryTypes::simplex(dim)); size_t numVariantsCube = numVariants(GeometryTypes::cube(dim)); variants_.resize(numVariantsSimplex + numVariantsCube); for (size_t i = 0; i < numVariantsSimplex; i++) variants_[i] = SimplexFiniteElement(i); for (size_t i = 0; i < numVariantsCube; i++) variants_[i + numVariantsSimplex] = CubeFiniteElement(i); if constexpr (dim == 3) { size_t numVariantsPyramid = numVariants(GeometryTypes::pyramid); size_t numVariantsPrism = numVariants(GeometryTypes::prism); variants_.resize(numVariantsSimplex + numVariantsCube + numVariantsPyramid + numVariantsPrism ); for (size_t i = 0; i < numVariantsPyramid; i++) variants_[i + numVariantsSimplex + numVariantsCube] = PyramidFiniteElement(i); for (size_t i = 0; i < numVariantsPrism; i++) variants_[i + numVariantsSimplex + numVariantsCube + numVariantsPyramid] = PrismFiniteElement(i); } } orient_.resize(gv.size(0)); for(const auto& cell : elements(gv)) { unsigned int myId = elementMapper_.index(cell); orient_[myId] = 0; for (const auto& intersection : intersections(gv,cell)) { if (intersection.neighbor() && (gv.contains(intersection.outside())) && (elementMapper_.index(intersection.outside()) > myId)) orient_[myId] |= (1 << intersection.indexInInside()); } // for mixed grids add offset for cubes and pyramids if constexpr (!hasFixedElementType) { size_t numVariantsSimplex = numVariants(GeometryTypes::simplex(dim)); size_t numVariantsCube = numVariants(GeometryTypes::cube(dim)); if (cell.type().isCube()) orient_[myId] += numVariantsSimplex; if constexpr (dim == 3) { size_t numVariantsPyramid = numVariants(GeometryTypes::pyramid); if (cell.type().isPyramid()) orient_[myId] += numVariantsSimplex + numVariantsCube; if (cell.type().isPrism()) orient_[myId] += numVariantsSimplex + numVariantsCube + numVariantsPyramid; } } } } template const FiniteElement& find(const EntityType& e) const { return variants_[orient_[elementMapper_.index(e)]]; } private: std::vector variants_; Dune::MultipleCodimMultipleGeomTypeMapper elementMapper_; std::vector orient_; }; } // namespace Impl // ***************************************************************************** // This is the reusable part of the basis. It contains // // RaviartThomasPreBasis // RaviartThomasNode // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template class RaviartThomasNode; template class RaviartThomasPreBasis : public LeafPreBasisMapperMixin { using Base = LeafPreBasisMapperMixin; // the layout is defined in terms of a MCMGLayout specialized for k == 0 or 1 static MCMGLayout dofLayout() { return [](GeometryType gt, size_t gridDim) -> size_t { if ((gt.isPyramid()) and (k==0)) return 1; if (gt.dim() == gridDim) return gt.isCube() ? ((dim == 2) ? k*(k+1)*dim : k*(k+1)*(k+1)*dim) : k*dim; if (gt.dim() == gridDim-1) return gt.isCube() ? (dim-2)*2*k+k+1 : (dim-1)*k+1 ; return 0; }; } static const int dim = GV::dimension; using FiniteElementMap = typename Impl::RaviartThomasLocalFiniteElementMap; public: /** \brief The grid view that the FE space is defined on */ using GridView = GV; using size_type = std::size_t; using Node = RaviartThomasNode; /** \brief Constructor for a given grid view object */ RaviartThomasPreBasis(const GridView& gv) : Base(gv, dofLayout()), finiteElementMap_(gv) { // Currently there are some unresolved bugs with hybrid grids and higher order Raviart-Thomas elements if (gv.indexSet().types(0).size() > 1 and k>0) DUNE_THROW(Dune::NotImplemented, "Raviart-Thomas basis with index k>0 is only implemented for grids with a single element type"); for(auto type : gv.indexSet().types(0)) if (!type.isSimplex() && !type.isCube() && !type.isPyramid() && !type.isPrism()) DUNE_THROW(Dune::NotImplemented, "Raviart-Thomas elements are only implemented for grids with simplex, cube, pyramid or prism elements."); } /** \brief Update the stored grid view, to be called if the grid has changed */ void update(const GridView& gv) { Base::update(gv); finiteElementMap_.update(gv); } /** * \brief Create tree node */ Node makeNode() const { return Node{&finiteElementMap_}; } protected: FiniteElementMap finiteElementMap_; }; template class RaviartThomasNode : public LeafBasisNode { static const int dim = GV::dimension; public: using size_type = std::size_t; using Element = typename GV::template Codim<0>::Entity; using FiniteElementMap = typename Impl::RaviartThomasLocalFiniteElementMap; using FiniteElement = Impl::GlobalValuedLocalFiniteElement; RaviartThomasNode(const FiniteElementMap* finiteElementMap) : element_(nullptr), finiteElementMap_(finiteElementMap) { } //! Return current element, throw if unbound const Element& element() const { return *element_; } /** \brief Return the LocalFiniteElement for the element we are bound to * * The LocalFiniteElement implements the corresponding interfaces of the dune-localfunctions module */ const FiniteElement& finiteElement() const { return finiteElement_; } //! Bind to element. void bind(const Element& e) { element_ = &e; finiteElement_.bind((finiteElementMap_->find(*element_)), e); this->setSize(finiteElement_.size()); } protected: FiniteElement finiteElement_; const Element* element_; const FiniteElementMap* finiteElementMap_; }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can create a Raviart-Thomas pre-basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam k Order of the Raviart-Thomas element */ template auto raviartThomas() { return [](const auto& gridView) { return RaviartThomasPreBasis, k>(gridView); }; } } // end namespace BasisFactory // ***************************************************************************** // This is the actual global basis implementation based on the reusable parts. // ***************************************************************************** /** \brief Basis of a k-th-order Raviart Thomas finite element space * * TODO: Fix this for grids with more than one element type * * \tparam GV The GridView that the space is defined on * \tparam k The order of the basis */ template using RaviartThomasBasis = DefaultGlobalBasis >; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_RAVIARTTHOMASBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/refinedlagrangebasis.hh000066400000000000000000000142321513634022200307550ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_REFINEDLAGRANGEBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_REFINEDLAGRANGEBASIS_HH #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { template class RefinedLagrangeNode; /** * \brief A pre-basis for a refined Lagrange bases * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The grid view that the FE basis is defined on * \tparam k The polynomial order of ansatz functions * \tparam R Range field-type used for shape function values * * \note This only works for simplex grids. */ template class RefinedLagrangePreBasis : public LeafPreBasisMapperMixin< GV > { using Base = LeafPreBasisMapperMixin< GV >; static const int dim = GV::dimension; // refined basis only implemented for P0 and P1 static_assert(k == 0 || k == 1); // the layout is defined in terms of a MCMGLayout specialized for k == 0 or 1 static MCMGLayout dofLayout() { if constexpr(k == 0) // a refined P0 basis assigns each element 2^dim DOFs return [](GeometryType gt, int) -> size_t { return (gt.dim() == dim) ? (1 << dim) : 0; }; else if constexpr(k == 1) // a refined P1 basis has the same layout as a P2 basis return [](GeometryType gt, int) -> size_t { return Dune::binomial(int(k),int(gt.dim())); }; else DUNE_THROW(Dune::NotImplemented, "Refined basis not implemented for higher-order Lagrange (k>=2) elements."); } public: //! The grid view that the FE basis is defined on using GridView = GV; //! Type of the refined Lagrange tree node using Node = RefinedLagrangeNode; /** * \brief Constructor for a given grid view object. * * \param gv The GridView the basis is defined on. * \throws Dune::NotImplemented If an element of type !simplex is found. */ RefinedLagrangePreBasis (const GridView& gv) : Base(gv, dofLayout()) { for (auto gt : gv.indexSet().types(0)) { if (!gt.isSimplex()) DUNE_THROW(Dune::NotImplemented, "Refined Lagrange basis only implemented for simplex grids."); } } //! Create tree node Node makeNode () const { return Node{}; } /** * \brief Polynomial order used in the local Lagrange finite-elements. * * \note The local function is of order `k` only in subdomains of the element. * It might be necessary to use a subdivided quadrature rule for * integration. */ static constexpr unsigned int order() { return k; } }; template class RefinedLagrangeNode : public LeafBasisNode { static constexpr int dim = GV::dimension; // refined basis only implemented for P0 and P1 static_assert(k == 0 || k == 1); public: //! Type of the element in the GridView using Element = typename GV::template Codim<0>::Entity; //! Type of the local finite-element using FiniteElement = std::conditional_t<(k==0), Dune::RefinedP0LocalFiniteElement, Dune::RefinedP1LocalFiniteElement>; /** * \brief The default constructor initializes all members to their default. * * The constructor default constructs the local finite-element and sets the * element pointer to `nullptr`, meaning that the node is not bound to any * element yet. * * \note Before the node can be used it needs to be bound to an element. **/ RefinedLagrangeNode () : finiteElement_{} , element_(nullptr) {} /** * \brief Return current element. * The behavior is undefined if the node is not bound to any element. */ const Element& element () const { return *element_; } /** * \brief Return the LocalFiniteElement for the element we are bound to. * * The LocalFiniteElement implements the corresponding interfaces of the * dune-localfunctions module. */ const FiniteElement& finiteElement () const { return finiteElement_; } //! Bind the node to the element `e`. void bind (const Element& e) { element_ = &e; this->setSize(finiteElement_.size()); } /** * \brief Polynomial order used in the local Lagrange finite-elements in * subdomains of the element. */ static constexpr unsigned int order() { return k; } protected: const FiniteElement finiteElement_; const Element* element_; }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can create a RefinedLagrange pre-basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam R The range type of the local basis */ template auto refinedLagrange () { return [](const auto& gridView) { return RefinedLagrangePreBasis, k, R>(gridView); }; } } // end namespace BasisFactory /** \brief Nodal basis of a continuous Lagrange finite-element space on a uniformly refined simplex element * * \ingroup FunctionSpaceBasesImplementations * * \note This only works for simplex grids. * * All arguments passed to the constructor will be forwarded to the constructor * of RefinedLagrangeBasis. * * \tparam GV The GridView that the space is defined on * \tparam k The order of the basis * \tparam R The range type of the local basis */ template using RefinedLagrangeBasis = DefaultGlobalBasis >; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_REFINEDLAGRANGEBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/restrictedbasis.hh000066400000000000000000000157311513634022200300150ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_RESTRICTEDBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_RESTRICTEDBASIS_HH #include #include #include #include namespace Dune::Functions::Experimental { /** * \brief A pre-basis restricted to a sub-domain * * \ingroup FunctionSpaceBasesImplementations * * This pre-basis wraps another pre-basis and restricts it to a sub-domain. * The wrapped pre-basis is assumed to be defined on a sub-domain of the * full grid view. Then the RestrictedPreBasis defines a pre-basis on * the full grid view implemented in terms of the pre-basis on the sub-domain. * Most of the methods are forwarded to the sub-domain pre-basis with * two exceptions: When binding a `RestrictedPreBasis::Node`, this only * calls `subDomainNode.bind(element)` if the element is contained in * the sub-domain, otherwise the sizes of the node and all its descendents * is set to zero. Furthermore it only calls `subDomainPreBasis.indices(node,it)` * if `node.size()` is not zero. * * \tparam GV Type of the (full) grid view this pre-basis is defined on. * \tparam SDPB Type of a pre-basis defined on sub-set of the full grid view * \tparam SD Type of the sub-domain */ template class RestrictedPreBasis { using This = RestrictedPreBasis; public: using SubDomain = SD; using SubDomainPreBasis = SDPB; using SubDomainGridView = typename SubDomainPreBasis::GridView; //! The grid view that the FE basis is defined on using GridView = GV; //! Type used for indices and size information using size_type = std::size_t; //! Template mapping root tree path to type of created tree node class Node : public SubDomainPreBasis::Node { using Base = typename SubDomainPreBasis::Node; public: using Element = typename Base::Element; Node(Base&& base, const SubDomainGridView& subDomainGridView, const SubDomain& subDomain) : Base(base) , subDomainGridView_(subDomainGridView) , subDomain_(subDomain) {} void bind(const Element& element) { if (subDomainGridView_.contains(element)) Base::bind(element); else { Dune::TypeTree::forEachNode(static_cast(*this) , [&](auto& node, const auto& treePath) { Dune::Functions::Impl::BasisNodeSetupHelper::setOffset(node, this->offset()); Dune::Functions::Impl::BasisNodeSetupHelper::setSize(node, 0); }); } } const SubDomain& subDomain() const { return subDomain_; } private: const SubDomainGridView& subDomainGridView_; const SubDomain& subDomain_; }; static constexpr size_type maxMultiIndexSize = SubDomainPreBasis::maxMultiIndexSize; static constexpr size_type minMultiIndexSize = SubDomainPreBasis::minMultiIndexSize; static constexpr size_type multiIndexBufferSize = SubDomainPreBasis::multiIndexBufferSize; /** * \brief Constructor for given sub-domain pre-basis * * The grid view and sub-domain pre-basis will be stored as copy * while a pointer to the sub-domain object is stored. */ RestrictedPreBasis(const GridView& gridView, SubDomainPreBasis&& subDomainPreBasis, const SubDomain& subDomain) : gridView_(gridView) , subDomainPreBasis_(std::move(subDomainPreBasis)) , subDomainPtr_(&subDomain) {} //! Initialize the global indices void initializeIndices() { subDomainPreBasis_.initializeIndices(); } //! Obtain the grid view that the basis is defined on const GridView& gridView() const { return gridView_; } /** * \brief Update the stored grid view, to be called * * This will also call `subDomainPreBasis.update(subDomainPtr->gridView())` * with the stored sub-domain pointer to update the sub-domain pre-basis. * Hence it requires the `subDomain` object passed to the present pre-basis * has been correctly updated externally, before calling this method. */ void update(const GridView& gv) { gridView_ = gv; subDomainPreBasis_.update(subDomainPtr_->gridView()); } /** * \brief Create tree node */ Node makeNode() const { return Node(subDomainPreBasis_.makeNode(), subDomainPreBasis_.gridView(), *subDomainPtr_); } //! Same as size(prefix) with empty prefix size_type size() const { return subDomainPreBasis_.size(); } //! Return number of possible values for next position in multi index template size_type size(const SizePrefix& prefix) const { return subDomainPreBasis_.size(prefix); } //! Return the container descriptor of the pre-basis auto containerDescriptor() const { return subDomainPreBasis_.containerDescriptor(); } //! Get the total dimension of the space spanned by this basis size_type dimension() const { return subDomainPreBasis_.dimension(); } //! Get the maximal number of DOFs associated to node for any element size_type maxNodeSize() const { return subDomainPreBasis_.maxNodeSize(); } const SubDomainPreBasis& subDomainPreBasis() const { return subDomainPreBasis_; } SubDomainPreBasis& subDomainPreBasis() { return subDomainPreBasis_; } template It indices(const Node& node, It it) const { if (node.size() == 0) return it; else return subDomainPreBasis_.indices(node, it); } protected: GridView gridView_; SubDomainPreBasis subDomainPreBasis_; const SubDomain* subDomainPtr_; }; namespace BasisFactory { /** * \brief Create a RestrictedPreBasisFactory * * \ingroup FunctionSpaceBasesImplementations * * This creates a preBasisFactory that restricts a pre-basis * to a sub-domain. The \p subDomain object must support * `subDomain.gridView()`. The returned `gridView` must * be defined on a subset of the host grid view, it must * support `gridView.contains(element)` to check if an * element is contained in the sub-domain and it must provide * all the interface functions required by the to-be-constructed * pre-basis type. * * \param subPreBasisFactory A PreBasisFactory use to create a pre-basis on the sub-domain * \param subDomain A sub-domain object */ template auto restrict(SubDomainBasisFactory&& subPreBasisFactory, const SubDomain& subDomain) { return [ subPreBasisFactory=std::forward(subPreBasisFactory), &subDomain ](const auto& gridView) { return Dune::Functions::Experimental::RestrictedPreBasis(gridView, subPreBasisFactory(subDomain.gridView()), subDomain); }; } } // end namespace BasisFactory } // end namespace Dune::Functions::Experimental #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_RESTRICTEDBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/subentitydofs.hh000066400000000000000000000164471513634022200275320ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_SUBENTITYDOFS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_SUBENTITYDOFS_HH #include #include #include namespace Dune { namespace Functions { /** * \brief Range of DOFs associated to sub-entity * * \ingroup FunctionSpaceBasesUtilities * * This provides a range of DOFs associated to a given sub-entity * by its LocalKeys. In order to use this, it has to be bound to * a bound LocalView and a sub-entity. The latter is either encoded * by its local index and codimension wrt the element the LocalView * is bound to, or by an intersection having this element as inside. * * After being bound this class can be used as range of local indices * of those DOFs that are associated to the sub-entity or sub-sub-entities * of this sub-entity. Furthermore it allows to ask if a given local index * is contained in this range. * * Notice that the class itself does only depend on the GridView * because it needs to allocate some dimension-dependent containers * for caching the information computed during bind. * * \tparam GridView The GridView LocalViews should act on */ template class SubEntityDOFs { static const int dim = GridView::dimension; public: /** * \brief Bind SubEntityDOFs object to LocalView and sub-entity * * Notice that this method will pre-compute and cache the contained * local indices as well as a look-up table for checking if a local * index is contained. * The sub-entity is encoded as (index,codim) with respect to * the element the LocalView is bound to. * In order to be able to bind and use the range in a single * expression this returns *this for convenience. * * \param localView A bound LocalView to bind to * \param subEntityIndex Index of sub-entity in localView.element() * \param subEntityCodim Codimension of sub-entity in localView.element() * \returns *this for convenience */ template SubEntityDOFs& bind(const LocalView& localView, std::size_t subEntityIndex, std::size_t subEntityCodim) { // fill vector with local indices of all DOFs contained in subentity containedDOFs_.clear(); dofIsContained_.assign(localView.size(), false); auto re = Dune::referenceElement(localView.element().type()); Dune::TypeTree::forEachLeafNode(localView.tree(), [&](auto&& node, auto&& /*treePath*/) { if (node.empty()) return; const auto& localCoefficients = node.finiteElement().localCoefficients(); std::size_t localSize = localCoefficients.size(); for(std::size_t i=0; i SubEntityDOFs& bind(const LocalView& localView, const Intersection& intersection) { return bind(localView, intersection.indexInInside(), 1); } //! Create begin iterator for access to range of contained local indices auto begin() const { return containedDOFs_.cbegin(); } //! Create end iterator for access to range of contained local indices auto end() const { return containedDOFs_.cend(); } //! Return number of contained DOFs auto size() const { return containedDOFs_.size(); } //! Return i-th entry of the range of contained local indices decltype(auto) operator[](std::size_t i) const { return containedDOFs_[i]; } //! Check if given local index is contained in this range of DOFs bool contains(std::size_t localIndex) const { return dofIsContained_[localIndex]; } private: std::vector containedDOFs_; std::vector dofIsContained_; }; /** * \brief Create SubEntityDOFs object * * \ingroup FunctionSpaceBasesUtilities * * The only requirement on the passed argument t * is, that its type T has to provide a typedef * T::GridView. Hence t it can be a GlobalBasis or * a LocalView. * * \tparam T A GlobalBasis type or a LocalView type */ template auto subEntityDOFs(const T&) { return SubEntityDOFs{}; } /** * \brief Create bound SubEntityDOFs object * * \ingroup FunctionSpaceBasesUtilities * * This creates a SubEntityDOFs object and binds * it to the given LocalView and sub-entity. * * Notice that the SubEntityDOFs object will allocate * some internal buffers. For efficiency reasons you * should thus prefer to first create a SubEntityDOFs * object and then bind it to each element you want to * process instead of creating a new bound SubEntityDOFs * object for each element. * * \param localView A bound LocalView to bind to * \param subEntityIndex Index of sub-entity in localView.element() * \param subEntityCodim Codimension of sub-entity in localView.element() */ template auto subEntityDOFs(const LocalView& localView, std::size_t subEntityIndex, std::size_t subEntityCodim) { using GridView = typename LocalView::GridView; SubEntityDOFs subEntityDOFs; subEntityDOFs.bind(localView, subEntityIndex, subEntityCodim); return subEntityDOFs; } /** * \brief Create bound SubEntityDOFs object * * \ingroup FunctionSpaceBasesUtilities * * This creates a SubEntityDOFs object and binds * it to the given LocalView and intersection. * * Notice that the SubEntityDOFs object will allocate * some internal buffers. For efficiency reasons you * should thus prefer to first create a SubEntityDOFs * object and then bind it to each element you want to * process instead of creating a new bound SubEntityDOFs * object for each element. * * \param localView A bound LocalView to bind to * \param intersection An Intersection encoding the sub-entity to bind to */ template auto subEntityDOFs(const LocalView& localView, const Intersection& intersection) { using GridView = typename LocalView::GridView; SubEntityDOFs subEntityDOFs; subEntityDOFs.bind(localView, intersection); return subEntityDOFs; } } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_SUBENTITYDOFS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/subspacebasis.hh000066400000000000000000000107731513634022200274530ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_SUBSPACEBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_SUBSPACEBASIS_HH #include #include #include #include #include #include #include namespace Dune { namespace Functions { template class SubspaceBasis { public: using RootBasis = RB; using RootLocalView = typename RootBasis::LocalView; using PrefixPath = TP; //! The grid view that the FE space is defined on using GridView = typename RootBasis::GridView; //! Type used for global numbering of the basis vectors using MultiIndex = typename RootBasis::MultiIndex; using size_type = std::size_t; //! Type of the local view on the restriction of the basis to a single element using LocalView = SubspaceLocalView; using SizePrefix = typename RootBasis::SizePrefix; /** \brief Constructor for a given grid view object */ SubspaceBasis(const RootBasis& rootBasis, const PrefixPath& prefixPath) : rootBasis_(&rootBasis), prefixPath_(prefixPath) {} /** \brief Constructor from another SubspaceBasis * * This will join the tree paths and use them relative to * rootBasis.rootBasis(). */ template SubspaceBasis(const SubspaceBasis& rootBasis, const OuterTP& prefixPath) : SubspaceBasis(rootBasis.rootBasis(), Dune::TypeTree::join(rootBasis.prefixPath(), prefixPath)) {} /** \brief Obtain the grid view that the basis is defined on */ const GridView& gridView() const { return rootBasis_->gridView(); } /** * \todo This method has been added to the interface without prior discussion. */ size_type dimension() const { return rootBasis_->dimension(); } //! Return number of possible values for next position in empty multi index size_type size() const { return rootBasis_->size(); } //! Return number possible values for next position in multi index size_type size(const SizePrefix& prefix) const { return rootBasis_->size(prefix); } /** \brief Return local view for basis * */ LocalView localView() const { return LocalView(*this, prefixPath_); } const RootBasis& rootBasis() const { return *rootBasis_; } const PrefixPath& prefixPath() const { return prefixPath_; } //! Return the associated container descriptor auto containerDescriptor() const { return rootBasis_->containerDescriptor(); } protected: const RootBasis* rootBasis_; PrefixPath prefixPath_; }; // CTAD guide for a non-SubspaceBasis root basis template SubspaceBasis(const RB&, const TP) -> SubspaceBasis; // CTAD guide for a SubspaceBasis root basis template SubspaceBasis(const SubspaceBasis& rootBasis, const OuterTP& prefixPath) -> SubspaceBasis, decltype(Dune::TypeTree::join(rootBasis.prefixPath(), prefixPath))>; /** * \brief Create SubspaceBasis from a root basis and a prefixPath * * This will not return a nested SubspaceBasis if rootBasis is already * a SubspaceBasis. Instead it will join the tree paths and use them * to construct a non-nested SubspaceBasis relative to rootBasis.rootBasis(). * * \param rootBasis Create a subspace basis relative to this basis * \param prefixPath A prefix path of the subspace within the root basis */ template auto subspaceBasis(const RootBasis& rootBasis, const TypeTree::TreePath& prefixPath) { return SubspaceBasis(rootBasis, prefixPath); } template auto subspaceBasis(const RootBasis& rootBasis, const PrefixTreeIndices&... prefixTreeIndices) { return subspaceBasis(rootBasis, TypeTree::treePath(prefixTreeIndices...)); } } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_DEFAULTGLOBALBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/subspacelocalview.hh000066400000000000000000000101021513634022200303210ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_SUBSPACELOCALVIEW_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_SUBSPACELOCALVIEW_HH #include #include #include #include namespace Dune { namespace Functions { template class SubspaceBasis; /** \brief The restriction of a finite element basis to a single element */ template class SubspaceLocalView { using PrefixPath = PP; public: using RootLocalView = RLV; //! The global FE basis that this is a view on using GlobalBasis = SubspaceBasis; //! The grid view the global FE basis lives on using GridView = typename GlobalBasis::GridView; //! Type of the grid element we are bound to using Element = typename GridView::template Codim<0>::Entity; //! The type used for sizes using size_type = std::size_t; //! Tree of local finite elements / local shape function sets using RootTree = typename RootLocalView::Tree; //! Tree of local finite elements / local shape function sets using Tree = typename TypeTree::ChildForTreePath; /** \brief Type used for global numbering of the basis vectors */ using MultiIndex = typename RootLocalView::MultiIndex; /** \brief Construct local view for a given global finite element basis */ SubspaceLocalView(const GlobalBasis& globalBasis, const PrefixPath& /*prefixPath*/) : globalBasis_(&globalBasis), rootLocalView_(globalBasis.rootBasis().localView()) { // static_assert(models, Tree>(), "Tree type passed to SubspaceLocalView does not model the BasisNode concept."); } /** \brief Bind the view to a grid element * * Having to bind the view to an element before being able to actually access any of its data members * offers to centralize some expensive setup code in the 'bind' method, which can save a lot of run-time. */ void bind(const Element& e) { rootLocalView_.bind(e); } /** \brief Return the grid element that the view is bound to * * \throws Dune::Exception if the view is not bound to anything */ const Element& element() const { return rootLocalView_.element(); } /** \brief Unbind from the current element * * Calling this method should only be a hint that the view can be unbound. */ void unbind() { rootLocalView_.unbind(); } /** \brief Return if the view is bound to a grid element */ bool bound() const { return rootLocalView_.bound(); } /** \brief Return the local ansatz tree associated to the bound entity * * \returns Tree // This is tree */ const Tree& tree() const { return TypeTree::child(rootLocalView_.tree(), globalBasis_->prefixPath()); } /** \brief Total number of degrees of freedom on this element */ size_type size() const { return rootLocalView_.size(); } /** * \brief Maximum local size for any element on the GridView * * This is the maximal size needed for local matrices * and local vectors, i.e., the result is */ size_type maxSize() const { return rootLocalView_.maxSize(); } //! Maps from subtree index set [0..size-1] to a globally unique multi index in global basis MultiIndex index(size_type i) const { return rootLocalView_.index(i); } /** \brief Return the global basis that we are a view on */ const GlobalBasis& globalBasis() const { return *globalBasis_; } const RootLocalView& rootLocalView() const { return rootLocalView_; } protected: const GlobalBasis* globalBasis_; RootLocalView rootLocalView_; }; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_SUBSPACELOCALVIEW_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/taylorhoodbasis.hh000066400000000000000000000232251513634022200300260ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TAYLORHOODBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TAYLORHOODBASIS_HH #include #include #include #include #include #include namespace Dune { namespace Functions { // ***************************************************************************** // This is the reusable part of the basis. It contains // // TaylorHoodPreBasis // TaylorHoodBasisTree // TaylorHoodVelocityTree // // The pre-basis allows to create the others and is the owner of possible shared // state. These components do _not_ depend on the global basis and local view // and can be used without a global basis. // ***************************************************************************** template class TaylorHoodVelocityTree; template class TaylorHoodBasisTree; /** * \brief Pre-basis for lowest order Taylor-Hood basis * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The grid view that the FE basis is defined on * \tparam HI Flag to select hybrid indices * * \note This mainly serves as an example, since you can construct a pre-basis with * the same functionality manually using * \code * static const int k = 1; * using VelocityPreBasis = PowerPreBasis,dim>; * using PressurePreBasis = LagrangePreBasis; * using TaylorHoodKPreBasis = CompositePreBasis; * \endcode * Where IMS is BlockedInterleaved if HI is set and * FlatInterleaved otherwise. */ template class TaylorHoodPreBasis { static const bool useHybridIndices = HI; static const int dim = GV::dimension; public: //! The grid view that the FE basis is defined on using GridView = GV; //! Type used for indices and size information using size_type = std::size_t; //! Template mapping root tree path to type of created tree node using Node = TaylorHoodBasisTree; static constexpr size_type maxMultiIndexSize = useHybridIndices ? 3 : 2; static constexpr size_type minMultiIndexSize = 2; static constexpr size_type multiIndexBufferSize = maxMultiIndexSize; private: using PQ1PreBasis = LagrangePreBasis; using PQ2PreBasis = LagrangePreBasis; public: //! Constructor for a given grid view object TaylorHoodPreBasis(const GridView& gv) : gridView_(gv), pq1PreBasis_(gv), pq2PreBasis_(gv) {} //! Initialize the global indices void initializeIndices() { pq1PreBasis_.initializeIndices(); pq2PreBasis_.initializeIndices(); } //! Obtain the grid view that the basis is defined on const GridView& gridView() const { return gridView_; } //! Update the stored grid view, to be called if the grid has changed void update (const GridView& gv) { pq1PreBasis_.update(gv); pq2PreBasis_.update(gv); } /** * \brief Create tree node */ Node makeNode() const { return Node{}; } //! Same as size(prefix) with empty prefix size_type size() const { return 2; } //! Return number of possible values for next position in multi index template size_type size(const SizePrefix& prefix) const { return sizeImp(prefix); } private: template = 0> size_type sizeImp(const SizePrefix& prefix) const { if (prefix.size() == 0) return 2; if (prefix.size() == 1) { if (prefix[0] == 0) return dim * pq2PreBasis_.size(); if (prefix[0] == 1) return pq1PreBasis_.size(); } assert(prefix.size() == 2); return 0; } template = 0> size_type sizeImp(const SizePrefix& prefix) const { if (prefix.size() == 0) return 2; if (prefix.size() == 1) { if (prefix[0] == 0) return pq2PreBasis_.size(); if (prefix[0] == 1) return pq1PreBasis_.size(); } if (prefix.size() == 2) { if (prefix[0] == 0) return dim; if (prefix[0] == 1) return 0; } assert(prefix.size() == 3); return 0; } public: //! Get the total dimension of the space spanned by this basis size_type dimension() const { return dim * pq2PreBasis_.size() + pq1PreBasis_.size(); } //! Get the maximal number of DOFs associated to node for any element size_type maxNodeSize() const { return dim * pq2PreBasis_.maxNodeSize() + pq1PreBasis_.maxNodeSize(); } template It indices(const Node& node, It it) const { return indicesImp(node, it); } /** * \brief Return an container descriptor depending on the flag `HI`. * Either return a `Tuple` if hybrid indices should be used, * otherwise return an `Array`. **/ auto containerDescriptor() const { namespace CD = Dune::Functions::ContainerDescriptors; if constexpr(HI) return CD::makeDescriptor( CD::makeUniformDescriptor(pq2PreBasis_.size(), CD::FlatArray{}), CD::FlatVector{pq1PreBasis_.size()}); else return CD::Array{ CD::FlatVector{GV::dimension * pq2PreBasis_.size()}, CD::FlatVector{pq1PreBasis_.size()} }; } protected: template static const void multiIndexPushFront(MultiIndex& M, size_type M0) { M.resize(M.size()+1); for(std::size_t i=M.size()-1; i>0; --i) M[i] = M[i-1]; M[0] = M0; } template = 0> It indicesImp(const Node& node, It multiIndices) const { using namespace Dune::Indices; for(std::size_t child=0; child = 0> It indicesImp(const Node& node, It multiIndices) const { using namespace Dune::Indices; for(std::size_t child=0; child class TaylorHoodVelocityTree : public PowerBasisNode, GV::dimension> { using PQ2Node = LagrangeNode; using Base = PowerBasisNode; public: TaylorHoodVelocityTree() { for(int i=0; isetChild(i, PQ2Node()); } }; template class TaylorHoodBasisTree : public CompositeBasisNode< TaylorHoodVelocityTree, LagrangeNode > { using VelocityNode=TaylorHoodVelocityTree; using PressureNode=LagrangeNode; using Base=CompositeBasisNode; public: TaylorHoodBasisTree() { this->template setChild<0>(VelocityNode()); this->template setChild<1>(PressureNode()); } }; namespace BasisFactory { /** * \brief Create a pre-basis factory that can create a Taylor-Hood pre-basis * * \ingroup FunctionSpaceBasesImplementations * */ inline auto taylorHood() { return [](const auto& gridView) { return TaylorHoodPreBasis>(gridView); }; } } // end namespace BasisFactory // ***************************************************************************** // This is the actual global basis implementation based on the reusable parts. // ***************************************************************************** /** * \brief Nodal basis for a lowest order Taylor-Hood Lagrangean finite element space * * \ingroup FunctionSpaceBasesImplementations * * \tparam GV The GridView that the space is defined on. * * \note This mainly serves as an example, since you can construct a basis with * the same functionality manually using * \code * static const int k = 1; * auto taylorHoodBasis = makeBasis( * gridView, * composite( * power( * lagrange(), * flatInterleaved()), * lagrange() * )); * \endcode */ template using TaylorHoodBasis = DefaultGlobalBasis >; } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TAYLORHOODBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/000077500000000000000000000000001513634022200252525ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/.gitignore000066400000000000000000000004421513634022200272420ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # temporary files generated by the test system *.log *.trs # individual tests gridviewfunctionspacebasistest dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/CMakeLists.txt000066400000000000000000000041741513634022200300200ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # tests that should build and run successfully # Path to the example grid files in dune-grid link_libraries(Dune::Functions) add_definitions(-DDUNE_GRID_EXAMPLE_GRIDS_PATH=\"${DUNE_GRID_EXAMPLE_GRIDS_PATH}\") dune_add_test(SOURCES argyristest.cc LABELS quick) dune_add_test(SOURCES brezzidouglasmarinibasistest.cc LABELS quick) dune_add_test(SOURCES bsplinebasistest.cc LABELS quick) dune_add_test(SOURCES containerdescriptortest.cc LABELS quick) dune_add_test(SOURCES cubichermitetest.cc LABELS quick) dune_add_test(SOURCES morleytest.cc LABELS quick) dune_add_test(SOURCES globalvaluedlfetest.cc LABELS quick) target_compile_definitions(globalvaluedlfetest PRIVATE -DDUNE_DEPRECATED_INTERPOLATE_CHECK=1) dune_add_test(SOURCES gridviewfunctionspacebasistest.cc LABELS quick) dune_add_test(SOURCES lagrangebasistest.cc LABELS quick) dune_add_test(SOURCES lagrangedgbasistest.cc LABELS quick) dune_add_test(SOURCES lfebasistest.cc LABELS quick) dune_add_test(SOURCES nedelecbasistest.cc LABELS quick) dune_add_test(SOURCES periodicbasistest.cc LABELS quick) dune_add_test(SOURCES taylorhoodbasistest.cc LABELS quick) dune_add_test(SOURCES rannacherturekbasistest.cc LABELS quick) dune_add_test(SOURCES raviartthomasbasistest.cc LABELS quick) dune_add_test(SOURCES restrictedprebasistest.cc LABELS quick) dune_add_test(SOURCES subspacebasistest.cc LABELS quick) dune_add_test(SOURCES compositebasistest.cc LABELS quick) dune_add_test(SOURCES makebasistest.cc LABELS quick) dune_add_test(SOURCES hierarchicallagrangebasistest.cc LABELS quick) dune_add_test(SOURCES hierarchicallagrangewithelementbubblebasistest.cc LABELS quick) dune_add_test(SOURCES refinedlagrangebasistest.cc CMAKE_GUARD Alberta_FOUND) add_dune_alberta_flags(refinedlagrangebasistest WORLDDIM 2) install( FILES basistest.hh interpolatetest.hh enabledifferentiabilitycheck.hh testboundlocalfe.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/functions/functionspacebases/test) dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/argyristest.cc000066400000000000000000000026451513634022200301500ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; int main(int argc, char *argv[]) { MPIHelper::instance(argc, argv); TestSuite test("ArgyrisBasis"); using Grid = UGGrid<2>; // using Grid = YaspGrid<2>; auto grid = StructuredGridFactory::createSimplexGrid({0., 0.}, {1., 1.}, {3, 3}); auto gridView = grid->leafGridView(); using namespace Dune::Functions::BasisFactory; auto basis = makeBasis(gridView, argyris()); test.subTest(checkBasis(basis, EnableContinuityCheck(), EnableDifferentiabilityCheck(), CheckLocalFiniteElementFlag<2>())); return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/basistest.hh000066400000000000000000000636321513634022200276060ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_BASISTEST_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_BASISTEST_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct CheckBasisFlag {}; struct AllowZeroBasisFunctions {}; // Enable checks that compare evaluateJacobian/partial methods up to order diffOrder with a // finite difference approximation, check duality, and representation of constants on each // element in the gridView template struct CheckLocalFiniteElementFlag { static constexpr int diffOrder = i; }; template struct IsContained : public std::disjunction...> {}; /* * Get string identifier of element */ template std::string elementStr(const Element& element, const GridView& gridView) { std::stringstream s; s << element.type() << "#" << gridView.indexSet().index(element); return s.str(); } /* * Check if two multi-indices are consecutive. * This is a used by checkBasisIndexTreeConsistency() */ template bool multiIndicesConsecutive(const MultiIndex& a, const MultiIndex& b) { std::size_t i = 0; // find largest common prefix for (; (i Dune::TestSuite checkBasisIndexTreeConsistency(const MultiIndexSet& multiIndexSet) { Dune::TestSuite test("index tree consistency check"); using namespace Dune; auto it = multiIndexSet.begin(); auto end = multiIndexSet.end(); // get first multi-index auto lastMultiIndex = *it; // assert that index is non-empty test.require(lastMultiIndex.size()>0, "multi-index size check") << "empty multi-index found"; // check if first multi-index is [0,...,0] for (decltype(lastMultiIndex.size()) i = 0; i0, "multi-index size check") << "empty multi-index found"; // assert that indices are consecutive test.check(multiIndicesConsecutive(lastMultiIndex, multiIndex), "consecutive index check") << "multi-indices " << lastMultiIndex << " and " << multiIndex << " are subsequent but not consecutive"; lastMultiIndex = multiIndex; } return test; } /* * Check consistency of basis.size(prefix) */ template Dune::TestSuite checkBasisSizeConsistency(const Basis& basis, const MultiIndexSet& multiIndexSet) { Dune::TestSuite test("index size consistency check"); // Based on the index tree, build a map that contains all possible // prefixes and maps each prefix to the size (of the subsequent digit). using Prefix = typename Basis::SizePrefix; auto prefixSet = std::map(); for(const auto& index : multiIndexSet) { auto prefix = Prefix(); for (const auto& i: index) { prefixSet[prefix] = std::max(prefixSet[prefix], i+1); prefix.push_back(i); } prefixSet[prefix] = 0; } // Now check for all prefixes, if the size computed from the // index tree is consistent with basis.size(prefix). for(const auto& [prefix, size] : prefixSet) { auto prefixSize = basis.size(prefix); test.check(prefixSize == size, "basis.size(prefix) check") << "basis.size(" << prefix << ")=" << prefixSize << ", but should be " << size; } return test; } /* * Check indices of basis: * - First store the whole index tree in a set * - Check if this corresponds to a consistent index tree * - Check if index tree is consistent with basis.size(prefix) and basis.dimension() */ template Dune::TestSuite checkBasisIndices(const Basis& basis) { Dune::TestSuite test("basis index check"); using MultiIndex = typename Basis::MultiIndex; static_assert(Dune::IsIndexable(), "MultiIndex must support operator[]"); auto compare = [](const auto& a, const auto& b) { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); }; auto multiIndexSet = std::set{compare}; auto localView = basis.localView(); for (const auto& e : elements(basis.gridView())) { localView.bind(e); for (decltype(localView.size()) i=0; i< localView.size(); ++i) { auto multiIndex = localView.index(i); for(auto mi: multiIndex) test.check(mi>=0) << "Global multi-index contains negative entry for shape function " << i << " in element " << elementStr(localView.element(), basis.gridView()); multiIndexSet.insert(multiIndex); } } test.subTest(checkBasisIndexTreeConsistency(multiIndexSet)); test.subTest(checkBasisSizeConsistency(basis, multiIndexSet)); test.check(basis.dimension() == multiIndexSet.size()) << "basis.dimension() does not equal the total number of basis functions."; return test; } /* * Check if shape functions are not constant zero. * This is called by checkLocalView(). */ template Dune::TestSuite checkNonZeroShapeFunctions(const LocalFiniteElement& fe, std::size_t order = 5, double tol = 1e-10) { Dune::TestSuite test; static const int dimension = LocalFiniteElement::Traits::LocalBasisType::Traits::dimDomain; auto quadRule = Dune::QuadratureRules::rule(fe.type(), order); std::vector values; std::vector isNonZero; isNonZero.resize(fe.size(), false); for (const auto& qp : quadRule) { fe.localBasis().evaluateFunction(qp.position(), values); for(std::size_t i=0; i tol)); } for(std::size_t i=0; i Dune::TestSuite checkLocalFiniteElement(const LocalFiniteElement &fe, Element const &element) { Dune::TestSuite test("Local Finite Element Test"); test.check(testBoundFE(fe, element)); return test; } /* * Check localView. This especially checks for * consistency of local indices and local size. */ template Dune::TestSuite checkLocalView(const Basis& basis, const LocalView& localView, Flags... flags) { if (not localView.bound()) { Dune::TestSuite test(std::string("Unbound LocalView")); Dune::TypeTree::forEachNode(localView.tree(), [&](const auto& node, auto&& treePath) { test.check(node.size()==0) << "Node of unbound LocalView has non-zero size " << node.size(); }); test.check(localView.size()==0) << "Unbound LocalView has non-zero size " << localView.size(); return test; } Dune::TestSuite test(std::string("LocalView on ") + elementStr(localView.element(), basis.gridView())); test.check(localView.size() <= localView.maxSize(), "localView.size() check") << "localView.size() is " << localView.size() << " but localView.maxSize() is " << localView.maxSize(); // Count all local indices appearing in the tree. std::vector localIndices; localIndices.resize(localView.size(), 0); Dune::TypeTree::forEachLeafNode(localView.tree(), [&](const auto& node, auto&& treePath) { if (node.empty()) return; test.check(node.size() == node.finiteElement().size()) << "Size of leaf node and finite element are different."; for(std::size_t i=0; i=1) << "Local index " << i << " did not appear"; test.check(localIndices[i]<=1) << "Local index " << i << " appears multiple times"; } // Check that all basis functions are non-zero. if (not IsContained::value) { Dune::TypeTree::forEachLeafNode(localView.tree(), [&](const auto& node, auto&& treePath) { if (node.empty()) return; test.subTest(checkNonZeroShapeFunctions(node.finiteElement())); }); } if constexpr(IsContained, Flags...>::value) { auto e = localView.element(); Dune::TypeTree::forEachLeafNode( localView.tree(), [&](const auto &node, [[maybe_unused]] auto &&treePath) { test.subTest(checkLocalFiniteElement<0>(node.finiteElement(), e)); }); } else if constexpr (IsContained, Flags...>::value) { auto e = localView.element(); Dune::TypeTree::forEachLeafNode( localView.tree(), [&](const auto &node, [[maybe_unused]] auto &&treePath) { test.subTest(checkLocalFiniteElement<1>(node.finiteElement(), e)); }); } else if constexpr (IsContained, Flags...>::value) { auto e = localView.element(); Dune::TypeTree::forEachLeafNode( localView.tree(), [&](const auto &node, [[maybe_unused]] auto &&treePath) { test.subTest(checkLocalFiniteElement<2>(node.finiteElement(), e)); }); } // Check if copies of the local view are independent. // To this end we create a copy, bind it to another element // and check if the original LocalView still behaves correct. // The check is done by first creating a fresh LocalView bound // to the same element as reference. { // Create a new local view as reference auto localView_reference = basis.localView(); localView_reference.bind(localView.element()); // Create and modify a copy of the local view auto localView_copy = localView; localView_copy.bind(*basis.gridView().template begin<0>()); Dune::TypeTree::forEachNode(localView.tree(), [&](const auto& node, auto&& treePath) { const auto& node_reference = Dune::TypeTree::child(localView_reference.tree(), treePath); const auto& node_copy = Dune::TypeTree::child(localView_copy.tree(), treePath); test.check(&node != &node_copy) << "LocalView and its copy share node " << treePath; test.check(node.size() == node_reference.size()) << "Size of node changed by modifying a copy of the LocalView."; if (node.empty()) return; for(std::size_t i=0; i auto localJumpContinuityCheck(const JumpEvaluator& jumpEvaluator, std::size_t order, double tol) const { return [=](const auto& intersection, const auto& treePath, const auto& insideNode, const auto& outsideNode, const auto& insideToOutside) { using Intersection = std::decay_t; using Node = std::decay_t; std::vector isContinuous(insideNode.size(), true); const auto& quadRule = Dune::QuadratureRules::rule(intersection.type(), order); using Range = typename Node::FiniteElement::Traits::LocalBasisType::Traits::RangeType; std::vector> values; std::vector> neighborValues; // Evaluate inside and outside basis functions. values.resize(quadRule.size()); neighborValues.resize(quadRule.size()); for(std::size_t k=0; k double { return jump.infinity_norm(); }; return localJumpContinuityCheck(jumpNorm, order_, tol_); } }; // Flag to enable a local normal-continuity check for checking strong // continuity across an intersection within checkBasisContinuity(). // // For each inside basis function this will compute the normal jump against // zero or the corresponding inside basis function. The latter is then // checked for being (up to a tolerance) zero on a set of quadrature points. struct EnableNormalContinuityCheck : public EnableContinuityCheck { auto localContinuityCheck() const { auto normalJump = [](auto&&jump, auto&& intersection, auto&& x) -> double { return jump * intersection.unitOuterNormal(x); }; return localJumpContinuityCheck(normalJump, order_, tol_); } }; // Flag to enable a local tangential-continuity check for checking continuity // of tangential parts of a vector-valued basis across an intersection // within checkBasisContinuity(). // // For each inside basis function this will compute the tangential jump against // zero or the corresponding outside basis function. The jump is then // checked for being (up to a tolerance) zero on a set of quadrature points. struct EnableTangentialContinuityCheck : public EnableContinuityCheck { auto localContinuityCheck() const { auto tangentialJumpNorm = [](auto&&jump, auto&& intersection, auto&& x) -> double { auto tangentialJump = jump - (jump * intersection.unitOuterNormal(x)) * intersection.unitOuterNormal(x); return tangentialJump.two_norm(); }; return localJumpContinuityCheck(tangentialJumpNorm, order_, tol_); } }; // Flag to enable a center continuity check for checking continuity in the // center of an intersection within checkBasisContinuity(). // // For each inside basis function this will compute the jump against // zero or the corresponding inside basis function. The latter is then // checked for being (up to a tolerance) zero in the center of mass // of the intersection. struct EnableCenterContinuityCheck : public EnableContinuityCheck { template auto localJumpCenterContinuityCheck(const JumpEvaluator& jumpEvaluator, double tol) const { return [=](const auto& intersection, const auto& treePath, const auto& insideNode, const auto& outsideNode, const auto& insideToOutside) { using Node = std::decay_t; using Range = typename Node::FiniteElement::Traits::LocalBasisType::Traits::RangeType; std::vector isContinuous(insideNode.size(), true); std::vector insideValues; std::vector outsideValues; insideNode.finiteElement().localBasis().evaluateFunction(intersection.geometryInInside().center(), insideValues); outsideNode.finiteElement().localBasis().evaluateFunction(intersection.geometryInOutside().center(), outsideValues); auto centerLocal = intersection.geometry().local(intersection.geometry().center()); // Check jump against outside basis function or zero. for(std::size_t i=0; i double { return jump.infinity_norm(); }; return localJumpCenterContinuityCheck(jumpNorm, tol_); } }; // Flag to enable a vertex continuity check for checking continuity at the vertices // of an intersection within checkBasisContinuity(). // // For each inside basis function this will compute the jump against // zero or the corresponding inside basis function. The latter is then // checked for being (up to a tolerance) zero in the vertices of mass // of the intersection. struct EnableVertexContinuityCheck: public EnableContinuityCheck { template auto localJumpVertexContinuityCheck(const JumpEvaluator &jumpEvaluator, double tol) const { return [=](const auto &intersection, const auto &treePath, const auto &insideNode, const auto &outsideNode, const auto &insideToOutside) { using Node = std::decay_t; using Range = typename Node::FiniteElement::Traits::LocalBasisType::Traits::RangeType; std::vector isContinuous(insideNode.size(), true); std::vector insideValues; std::vector outsideValues; std::vector::LocalCoordinate> vertices; for (int i = 0; i < intersection.geometry().corners(); ++i) vertices.push_back(intersection.geometry().local(intersection.geometry().corner(i))); for (auto const &vertex : vertices) { insideNode.finiteElement().localBasis().evaluateFunction( intersection.geometryInInside().global(vertex), insideValues); outsideNode.finiteElement().localBasis().evaluateFunction( intersection.geometryInOutside().global(vertex), outsideValues); // Check jump against outside basis function or zero. for (std::size_t i = 0; i < insideNode.size(); ++i) { auto jump = insideValues[i]; if (insideToOutside[i].has_value()) jump -= outsideValues[insideToOutside[i].value()]; isContinuous[i] = isContinuous[i] and (jumpEvaluator(jump, intersection, vertex) < tol); } } return isContinuous; }; } auto localContinuityCheck() const { auto jumpNorm = [](auto &&jump, auto &&intersection, auto &&x) -> double { return jump.infinity_norm(); }; return localJumpVertexContinuityCheck(jumpNorm, tol_); } }; /* * Check if basis functions are continuous across faces. * Continuity is checked by evaluation at a set of quadrature points * from a quadrature rule of given order. * If two basis functions (on neighboring elements) share the same * global index, their values at the quadrature points (located on * their intersection) should coincide up to the given tolerance. * * If a basis function only appears on one side of the intersection, * it should be zero on the intersection. */ template Dune::TestSuite checkBasisContinuity(const Basis& basis, const LocalCheck& localCheck) { Dune::TestSuite test("Global continuity check of basis functions"); auto localView = basis.localView(); auto neighborLocalView = basis.localView(); for (const auto& e : elements(basis.gridView())) { localView.bind(e); for(const auto& intersection : intersections(basis.gridView(), e)) { if (intersection.neighbor()) { neighborLocalView.bind(intersection.outside()); Dune::TypeTree::forEachLeafNode(localView.tree(), [&](const auto& insideNode, auto&& treePath) { if (insideNode.empty()) return; const auto& outsideNode = Dune::TypeTree::child(neighborLocalView.tree(), treePath); std::vector> insideToOutside; insideToOutside.resize(insideNode.size()); // Map all inside DOFs to outside DOFs if possible for(std::size_t i=0; i Dune::TestSuite checkConstBasis(const Basis& basis, Flags... flags) { Dune::TestSuite test("const basis check"); using GridView = typename Basis::GridView; // Check if basis models the GlobalBasis concept. test.check(Dune::models, Basis>(), "global basis concept check") << "type passed to checkBasis() does not model the GlobalBasis concept"; // Perform all local tests. auto localView = basis.localView(); test.check(not localView.bound()) << "Unbound LocalView returns localView.bound()==true"; test.subTest(checkLocalView(basis, localView, flags...)); for (const auto& e : elements(basis.gridView())) { localView.bind(e); test.check(localView.bound()) << "Bound LocalView returns localView.bound()==false"; test.subTest(checkLocalView(basis, localView, flags...)); } // Perform global index tests. test.subTest(checkBasisIndices(basis)); // Perform continuity check. // First capture flags in a tuple in order to iterate. auto flagTuple = std::tie(flags...); Dune::Hybrid::forEach(flagTuple, [&](auto&& flag) { using Flag = std::decay_t; if constexpr (std::is_base_of_v) test.subTest(checkBasisContinuity(basis, flag.localContinuityCheck())); else if constexpr (std::is_base_of_v) test.subTest(checkBasisDifferentiability(basis, flag)); }); return test; } template Dune::TestSuite checkBasis(Basis& basis, Flags... flags) { Dune::TestSuite test("basis check"); // Perform tests for a constant basis test.subTest(checkConstBasis(basis,flags...)); // Check copy-construction / copy-assignable of a basis { Basis copy(basis); test.subTest(checkConstBasis(copy,flags...)); copy = basis; test.subTest(checkConstBasis(copy,flags...)); } // Check update of gridView auto gridView = basis.gridView(); basis.update(gridView); return test; } #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_BASISTEST_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/brezzidouglasmarinibasistest.cc000066400000000000000000000101261513634022200335670ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include using namespace Dune; template void testBrezziDouglasMariniBasis(TestSuite& test, const GridFactory& factory) { auto grid = factory(); auto gridView = grid->leafGridView(); std::cout<<" Testing order: "<< order < basis1(gridView); test.subTest(checkBasis(basis1, EnableNormalContinuityCheck())); // Check BrezziDouglasMariniBasis created using basis builder mechanism using namespace Functions::BasisFactory; auto basis2 = makeBasis(gridView, brezziDouglasMarini()); test.subTest(checkBasis(basis2, EnableNormalContinuityCheck())); // Now modify the grid, and check again. const auto firstEntity = gridView.template begin<0>(); grid->mark(1, *firstEntity); grid->adapt(); auto modifiedGridView = grid->leafGridView(); // Check the NedelecBasis that was created 'manually' basis1.update(modifiedGridView); test.subTest(checkBasis(basis1, EnableNormalContinuityCheck())); // Check the NedelecBasis that was created using the basis builder mechanism basis2.update(modifiedGridView); test.subTest(checkBasis(basis2, EnableNormalContinuityCheck())); } int main (int argc, char* argv[]) { MPIHelper::instance(argc, argv); const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/"; TestSuite test; std::cout<<"Testing NedelecBasis in 2D with simplex grid\n"; auto triangleGridFactory = [&path]() { return GmshReader >::read(path + "curved2d.msh"); }; testBrezziDouglasMariniBasis<1>(test, triangleGridFactory); // TODO: Enable this test! //testBrezziDouglasMariniBasis<2>(test, triangleGridFactory); // Test with grid that only supports cube elements std::cout<<"Testing BrezziDouglasMariniBasis in 2D with cube grid\n"; auto quadGridFactory = []() { return std::make_unique >(FieldVector{1.0, 1.0}, std::array{5,5}); }; testBrezziDouglasMariniBasis<1>(test, quadGridFactory); // TODO: Enable this test! //testBrezziDouglasMariniBasis<2>(test, quadGridFactory); #if 0 // TODO: Enable this test! std::cout<<"Testing BrezziDouglasMariniBasis in 2D with mixed-element grid\n"; auto mixed2dGridFactory = [&path]() { return GmshReader >::read(path + "hybrid-testgrid-2d.msh"); }; testBrezziDouglasMariniBasis<1>(test, mixed2dGridFactory); #endif #if 0 // TODO: Enable this test! std::cout<<"Testing BrezziDouglasMariniBasis in 3D with simplex grid\n"; auto tetraGridFactory = [&path]() { return GmshReader >::read(path + "telescope1storder.msh"); }; testBrezziDouglasMariniBasis<1>(test, tetraGridFactory); #endif #if 0 // TODO: Enable this test! Test with grid that only supports cube elements std::cout<<"Testing BrezziDouglasMariniBasis in 3D with cube grid\n"; auto cubeGridFactory = []() { return std::make_unique >(FieldVector{1.0, 1.0, 1.0}, std::array{5,5,5}); }; testBrezziDouglasMariniBasis<1>(test, cubeGridFactory); #endif #if 0 // TODO: Enable this test! hybrid-testgrid-3d.msh contains pyramids and prisms, which are not implemented std::cout<<"Testing BrezziDouglasMariniBasis in 3D with mixed-element grid\n"; auto mixed3dGridFactory = [&path]() { return GmshReader >::read(path + "hybrid-testgrid-3d.msh"); }; testBrezziDouglasMariniBasis<1>(test, mixed3dGridFactory); #endif return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/bsplinebasistest.cc000066400000000000000000000075171513634022200311510ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include using namespace Dune; template void testForDimension(TestSuite& test) { std::cout << " +++++++++++ Testing on " << dim << "d grid ++++++++++++" << std::endl; // Generate grid for testing typedef YaspGrid GridType; FieldVector l; std::fill(l.begin(), l.end(), 1.0); std::array elements; std::fill(elements.begin(), elements.end(), 2); GridType grid(l,elements); // Test whether function space basis can be instantiated on the leaf view typedef typename GridType::LeafGridView GridView; GridView gridView = grid.leafGridView(); grid.globalRefine(2); // Testing B-spline basis with open knot vectors std::vector knotVector(elements[0]*4+1); for (size_t i=0; i basis(gridView, knotVector, order); if (order>0) test.subTest(checkBasis(basis, AllowZeroBasisFunctions(), EnableContinuityCheck())); else test.subTest(checkBasis(basis, AllowZeroBasisFunctions())); } { // Check basis created via makeBasis using namespace Functions::BasisFactory; auto basis = makeBasis(gridView, bSpline(knotVector, order)); if (order>0) test.subTest(checkBasis(basis, AllowZeroBasisFunctions(), EnableContinuityCheck())); else test.subTest(checkBasis(basis, AllowZeroBasisFunctions())); } { // Check whether a B-Spline basis can be combined with other bases. using namespace Functions::BasisFactory; auto basis = makeBasis(gridView, power<2>( bSpline(knotVector, order), blockedInterleaved() )); if (order>0) test.subTest(checkBasis(basis, AllowZeroBasisFunctions(), EnableContinuityCheck())); else test.subTest(checkBasis(basis, AllowZeroBasisFunctions())); } } // Testing B-spline basis with non-open knot vectors std::cout << " Testing B-spline basis with non-open knot vectors" << std::endl; for (unsigned int order : {0, 1, 2}) { { // Check basis created via its constructor Functions::BSplineBasis bSplineBasis(gridView, knotVector, order, false); if (order>0) test.subTest(checkBasis(bSplineBasis, AllowZeroBasisFunctions(), EnableContinuityCheck())); else test.subTest(checkBasis(bSplineBasis, AllowZeroBasisFunctions())); } { // Check basis created via makeBasis using namespace Functions::BasisFactory; auto basis = makeBasis(gridView, bSpline(knotVector, order, false)); if (order>0) test.subTest(checkBasis(basis, AllowZeroBasisFunctions(), EnableContinuityCheck())); else test.subTest(checkBasis(basis, AllowZeroBasisFunctions())); } } } int main (int argc, char* argv[]) { MPIHelper::instance(argc, argv); TestSuite test; testForDimension<1>(test); testForDimension<2>(test); testForDimension<3>(test); return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/compositebasistest.cc000066400000000000000000000075671513634022200315240ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include using namespace Dune; struct MyLeafBasisNode : Dune::Functions::LeafBasisNode { using Element = Dune::Concept::Archetypes::Entity<2,0>; MyLeafBasisNode () {} }; void testCompositeBasisNode () { MyLeafBasisNode leaf{}; Dune::Functions::CompositeBasisNode compositenode1{}; Dune::Functions::CompositeBasisNode compositenode2{leaf}; // Dune::Functions::CompositeBasisNode compositenode3{leaf}; Dune::Functions::CompositeBasisNode compositenode4{leaf, leaf}; } int main (int argc, char *argv[]) try { // Set up MPI, if available MPIHelper::instance(argc, argv); Dune::TestSuite test; /////////////////////////////////// // Generate the grid /////////////////////////////////// const int dim = 2; typedef YaspGrid GridType; FieldVector l(1); std::array elements = {{4, 4}}; GridType grid(l,elements); typedef GridType::LeafGridView GridView; GridView gridView = grid.leafGridView(); ///////////////////////////////////////////////////////// // Choose a finite element space ///////////////////////////////////////////////////////// using namespace Functions::BasisFactory; { auto basis = makeBasis( gridView, composite( lagrange<1>(), lagrange<1>(), lagrange<1>(), blockedLexicographic() )); test.subTest(checkBasis(basis, EnableContinuityCheck())); } { auto basis = Dune::Functions::DefaultGlobalBasis( gridView, composite( lagrange<1>(), lagrange<1>(), lagrange<1>(), blockedLexicographic() )); test.subTest(checkBasis(basis, EnableContinuityCheck())); } { using namespace Functions; using PreBasis = PowerPreBasis, 2>, PowerPreBasis, 2> >, 2>; auto basis = Dune::Functions::DefaultGlobalBasis(grid.leafGridView()); test.subTest(checkBasis(basis, EnableContinuityCheck())); } { using namespace Functions; using PreBasis = CompositePreBasis>; auto basis = Dune::Functions::DefaultGlobalBasis(grid.leafGridView()); test.subTest(checkBasis(basis, EnableContinuityCheck())); } { using namespace Functions; using PreBasis = CompositePreBasis>; const auto gridView = grid.leafGridView(); auto basis = Dune::Functions::DefaultGlobalBasis(gridView); test.subTest(checkBasis(basis, EnableContinuityCheck())); } { using namespace Functions; using PreBasis = CompositePreBasis>; auto basis = Dune::Functions::DefaultGlobalBasis(gridView); test.subTest(checkBasis(basis, EnableContinuityCheck())); } return test.exit(); } // Error handling catch (Exception& e) { std::cout << e.what() << std::endl; } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/containerdescriptortest.cc000066400000000000000000000264701513634022200325530ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CD = Dune::Functions::ContainerDescriptors; namespace TestImpl { // check at run time whether index is a valid child index template std::true_type checkAccessIndex (Node const& node, Index i) { assert(std::size_t(i) < node.size() && "Child index out of range"); return {}; } // check at compile time whether index is a valid index template std::bool_constant<(i < Node::size())> checkAccessIndex (Node const& node, Dune::index_constant) { static_assert(i < Node::size(), "Child index out of range"); return {}; } // finally return the node itself if no further indices are provided. Break condition // for the recursion over the node children. template decltype(auto) accessImpl (Node&& node) { return std::forward(node); } // recursively call `node[i]` with the given indices template decltype(auto) accessImpl (Node&& node, I0 i0, [[maybe_unused]] I... i) { auto valid = checkAccessIndex(node,i0); if constexpr (valid) return accessImpl(node[i0],i...); else return; } // forward to the impl methods by extracting the indices from the tree path template decltype(auto) access (Tree&& tree, [[maybe_unused]] Dune::TypeTree::TreePath tp, std::index_sequence) { return accessImpl(std::forward(tree), tp[Dune::index_constant{}]...); } // access a tree using a TreePath template decltype(auto) access (Tree&& tree, Dune::TypeTree::TreePath tp) { return access(std::forward(tree),tp,std::index_sequence_for{}); } // convert a TreePath into a ReservedVector template auto sizePrefix (Dune::TypeTree::TreePath tp) { using value_type = typename SizePrefix::value_type; return Dune::unpackIntegerSequence([&](auto... i) { return SizePrefix{value_type(tp[i])...}; }, std::index_sequence_for{}); } } // end namespace TestImpl // check that the sizes of an container descriptor correspond to the sizes provided by the // basis (size-provider) directly. template> void checkSize (Dune::TestSuite& test, const ContainerDescriptor& cd, const SizeProvider& sizeProvider, PrefixPath prefix = {}) { using SizePrefix = typename SizeProvider::SizePrefix; auto size1 = sizeProvider.size(TestImpl::sizePrefix(prefix)); auto size2 = Dune::Hybrid::size(TestImpl::access(cd, prefix)); test.require(std::size_t(size1) == std::size_t(size2), "size1 == size2"); if constexpr(PrefixPath::size() < SizePrefix::max_size()) { Dune::Hybrid::forEach(Dune::range(size2), [&](auto i) { checkSize(test, cd, sizeProvider, push_back(prefix,i)); }); } } // check a specific multi-index by traversing all its components and the container descriptor // simultaneously template void checkMultiIndex (Dune::TestSuite& test, const ContainerDescriptor& cd, const MultiIndex& mi, std::size_t j = 0) { if (j < mi.size()) { test.check(mi[j] < cd.size(), "mi[j] < cd.size"); auto size = Dune::Hybrid::size(cd); Dune::Hybrid::switchCases(Dune::range(size), mi[j], [&](auto jj) { checkMultiIndex(test,cd[jj],mi,j+1); }); } } // check that all multi-indices of a global basis are within the range of the container descriptor template void checkMultiIndices (Dune::TestSuite& test, const ContainerDescriptor& cd, const Basis& basis) { auto localView = basis.localView(); for (auto const& e : elements(basis.gridView())) { localView.bind(e); for (std::size_t i = 0; i < localView.size(); ++i) { auto mi = localView.index(i); checkMultiIndex(test,cd,mi); } } } void checkHierarchic (Dune::TestSuite& test) { using namespace Dune::Indices; CD::Tuple,CD::FlatVector> stokes{ CD::Array{CD::FlatVector{10},CD::FlatVector{10},CD::FlatVector{10}}, CD::FlatVector{5} }; { // check the make-functions auto stokes2 = CD::makeDescriptor( CD::makeDescriptor( CD::makeUniformDescriptor(10, CD::Value{}), CD::makeUniformDescriptor(10, CD::Value{}), CD::makeUniformDescriptor(10, CD::Value{}) ), CD::makeUniformDescriptor(5, CD::Value{}) ); static_assert(std::is_same_v); } CD::Array const& velocity = stokes[_0]; test.check(velocity[0].size() == 10, "v[0].size == 10"); test.check(velocity[1].size() == 10, "v[1].size == 10"); test.check(velocity[2].size() == 10, "v[2].size == 10"); CD::FlatVector const& pressure = stokes[_1]; test.check(pressure.size() == 5, "p.size == 5"); } void checkConstructors (Dune::TestSuite& test) { CD::Unknown cd0{}; CD::Value cd1{}; // the children might have different types CD::Tuple cd2a; CD::Tuple cd2b{cd0,cd1}; auto cd2c = CD::makeDescriptor(cd0,cd1); static_assert(cd2a.size() == 2, "cd2a.size() == 2"); static_assert(cd2b.size() == 2, "cd2b.size() == 2"); static_assert(cd2c.size() == 2, "cd2c.size() == 2"); static_assert(std::is_same_v); // all childresn have the same type, the number of children is static CD::Array cd3a; CD::Array cd3b = Dune::filledArray<3>(cd1); CD::Array cd3c{cd1,cd1,cd1}; auto cd3d = CD::makeDescriptor(cd1,cd1,cd1); static_assert(cd3a.size() == 3, "cd3a.size() == 3"); static_assert(cd3b.size() == 3, "cd3b.size() == 3"); static_assert(cd3c.size() == 3, "cd3c.size() == 3"); static_assert(cd3d.size() == 3, "cd3d.size() == 3"); static_assert(std::is_same_v); static_assert(std::is_same_v); // all childresn have the same type, the number of children is a runtime value CD::Vector cd4a(4,cd1); CD::Vector cd4b{cd1,cd1,cd1,cd1}; CD::Vector cd4c(4,cd1); CD::Vector cd4d{cd1,cd1,cd1,cd1}; test.check(cd4a.size() == 4, "cd4a.size() == 4"); test.check(cd4b.size() == 4, "cd4b.size() == 4"); test.check(cd4c.size() == 4, "cd4c.size() == 4"); test.check(cd4d.size() == 4, "cd4d.size() == 4"); static_assert(std::is_same_v); static_assert(std::is_same_v); // all children are identical, the number of children is static // only a single child is stored CD::UniformArray cd5a; CD::UniformArray cd5b(cd1); auto cd5c = makeUniformDescriptor(std::integral_constant{},cd1); static_assert(cd5a.size() == 5, "cd5a.size() == 5"); static_assert(cd5b.size() == 5, "cd5b.size() == 5"); static_assert(cd5c.size() == 5, "cd5c.size() == 5"); static_assert(std::is_same_v); // shortcut for uniform arrays storing `Value` CD::FlatArray<5> cd5e; static_assert(cd5e.size() == 5, "cd5e.size() == 5"); static_assert(std::is_same_v); // all children are identical, the number of children is a runtime value // only a single child is stored CD::UniformVector cd6a(6); CD::UniformVector cd6b(6,cd1); auto cd6c = makeUniformDescriptor(6,cd1); test.check(cd6a.size() == 6, "cd6a.size() == 6"); test.check(cd6b.size() == 6, "cd6b.size() == 6"); test.check(cd6c.size() == 6, "cd6c.size() == 6"); static_assert(std::is_same_v); static_assert(std::is_same_v); // shortcut for uniform vectors storing `Value` CD::FlatVector cd6d(6); test.check(cd6d.size() == 6, "cd6d.size() == 6"); static_assert(std::is_same_v); } template void checkBasis(Dune::TestSuite& test, const BasisFactory& bf) { using Grid = Dune::YaspGrid<2>; Grid grid({1.0, 1.0}, {2, 2}); auto basis = makeBasis(grid.leafGridView(), bf); checkSize(test, basis.containerDescriptor(), basis); checkMultiIndices(test, basis.containerDescriptor(), basis); } int main (int argc, char *argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; checkConstructors(test); checkHierarchic(test); using namespace Dune::Functions::BasisFactory; // test default index-merging strategies checkBasis(test, lagrange<1>() ); checkBasis(test, power<2>(lagrange<1>(), blockedInterleaved()) ); checkBasis(test, composite(lagrange<1>(),lagrange<2>(), blockedLexicographic()) ); // test all combinations of two nested power bases Dune::Hybrid::forEach(std::tuple{flatLexicographic(), flatInterleaved(), blockedLexicographic(), blockedInterleaved()}, [&](auto outerIMS) { Dune::Hybrid::forEach(std::tuple{flatLexicographic(), flatInterleaved(), blockedLexicographic(), blockedInterleaved()}, [&](auto innerIMS) { checkBasis(test, power<2>(power<3>(lagrange<2>(), innerIMS), outerIMS) ); }); }); // test more complicated bases checkBasis(test, power<2>( composite( power<1>(power<1>(lagrange<1>(), blockedInterleaved()), blockedLexicographic()), power<2>(lagrange<1>(), blockedInterleaved()), power<3>(lagrange<1>(), blockedLexicographic()), blockedLexicographic() ), blockedLexicographic() ) ); checkBasis(test, composite( composite( power<2>(lagrange<1>(), blockedInterleaved()), power<1>(power<2>(lagrange<1>(), blockedLexicographic()), blockedLexicographic()), blockedLexicographic() ), composite( power<1>(power<1>(lagrange<1>(), blockedLexicographic()), blockedInterleaved()), power<2>(lagrange<1>(), blockedInterleaved()), power<3>(lagrange<1>(), blockedLexicographic()), blockedLexicographic() ), blockedLexicographic() ) ); checkBasis(test, composite( composite( power<2>(lagrange<1>(), flatInterleaved()), power<1>(power<2>(lagrange<1>(), flatLexicographic()),flatLexicographic()), flatLexicographic() ), composite( power<1>(power<1>(lagrange<1>(), flatLexicographic()), flatInterleaved()), power<2>(lagrange<1>(), flatInterleaved()), power<3>(lagrange<1>(), flatLexicographic()), flatLexicographic() ), flatLexicographic() ) ); return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/cubichermitetest.cc000066400000000000000000000111461513634022200311270ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; int main(int argc, char *argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test_1d("1d"), test_2d("2d"), test_3d("3d"); using namespace Dune::Functions::BasisFactory; { // 1d std::cout << "CubicHermite test in 1d" << std::endl; auto grid = StructuredGridFactory::createSimplexGrid({0.}, {1.}, {10}); auto gridView = grid->levelGridView(0); { std::cout << "Grid has " << gridView.size(0) << " elements and " << gridView.size(1) << " facets and " << gridView.size(2) << " vertices" << std::endl; auto basis = makeBasis(gridView, cubicHermite()); std::cout << "Basis has " << basis.size() << " dofs" << std::endl; test_1d.subTest(checkBasis(basis, EnableContinuityCheck(), CheckLocalFiniteElementFlag<2>{}, EnableDifferentiabilityCheck(), EnableVertexDifferentiabilityCheck())); } } { // 2d std::cout << "Hermite test in 2d" << std::endl; auto factory = Dune::GridFactory>{}; factory.insertVertex({0,0}); factory.insertVertex({0,1}); factory.insertVertex({2,0}); factory.insertVertex({1,4}); factory.insertElement(Dune::GeometryTypes::simplex(2), {0,1,2}); factory.insertElement(Dune::GeometryTypes::simplex(2), {1,2,3}); auto grid = factory.createGrid(); grid->globalRefine(2); auto gridView = grid->leafGridView(); std::cout << "Grid has " << gridView.size(0) << " elements and " << gridView.size(1) << " facets and " << gridView.size(2) << " vertices" << std::endl; { using namespace Dune::Functions::BasisFactory; auto basis = makeBasis(gridView, cubicHermite()); std::cout << "Basis has " << basis.size() << " dofs" << std::endl; test_2d.subTest(checkBasis(basis, CheckLocalFiniteElementFlag<1>{}, EnableContinuityCheck(), EnableVertexDifferentiabilityCheck())); } } { // 2d reduced std::cout << "reduced CubicHermite test in 2d" << std::endl; auto factory = Dune::GridFactory>{}; factory.insertVertex({0,0}); factory.insertVertex({0,1}); factory.insertVertex({2,0}); factory.insertVertex({1,4}); factory.insertElement(Dune::GeometryTypes::simplex(2), {0,1,2}); factory.insertElement(Dune::GeometryTypes::simplex(2), {1,2,3}); auto grid = factory.createGrid(); grid->globalRefine(2); auto gridView = grid->leafGridView(); std::cout << "Grid has " << gridView.size(0) << " elements and " << gridView.size(1) << " facets and " << gridView.size(2) << " vertices" << std::endl; { using namespace Dune::Functions::BasisFactory; auto basis = makeBasis(gridView, reducedCubicHermite()); std::cout << "Basis has " << basis.size() << " Dofs" << std::endl; test_2d.subTest(checkBasis(basis,CheckLocalFiniteElementFlag<1>{}, EnableContinuityCheck(), EnableVertexDifferentiabilityCheck())); } } { // 3d std::cout << "CubicHermite test in 3d" << std::endl; auto grid = StructuredGridFactory>::createSimplexGrid({0., 0., 0.}, {1., 1., 1.}, {{3, 3, 3}}); auto gridView = grid->leafGridView(); std::cout << "Grid has " << gridView.size(0) << " elements and " << gridView.size(1) << " facets and " << gridView.size(2) << " edges and " << gridView.size(3) << " vertices " << std::endl; { using namespace Dune::Functions::BasisFactory; auto basis = makeBasis(gridView, cubicHermite()); std::cout << "Basis has " << basis.size() << " dofs" << std::endl; test_3d.subTest(checkBasis(basis, EnableContinuityCheck(),CheckLocalFiniteElementFlag<1>{}, EnableVertexDifferentiabilityCheck())); } } return test_1d.exit() + test_2d.exit() + test_3d.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/enabledifferentiabilitycheck.hh000066400000000000000000000240721513634022200334420ustar00rootroot00000000000000#ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_ENABLEDIFFERENTIABILITYCHECK_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_ENABLEDIFFERENTIABILITYCHECK_HH // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include template std::string elementStr(const Element &element, const GridView &gridView); // Flag to enable a local continuity check for checking partial // differentiability across an intersection within checkBasisDifferentiability(). // // For each inside basis function this will compute the jump against // zero or the corresponding inside basis function. The latter is then // checked for being (up to a tolerance) zero on a set of quadrature points. struct EnableDifferentiabilityCheck { std::size_t order_ = 5; double tol_ = 1e-10; std::string checkLocation = "across"; template auto localJumpDifferentiabilityCheck(const JumpEvaluator &jumpEvaluator, const QuadRuleFactoryMethod &quadRuleFactoryMethod, std::size_t order, double tol) const { return [=](const auto &intersection, const auto &treePath, const auto &insideNode, const auto &outsideNode, const auto &insideToOutside) { // using Intersection = std::decay_t; using Node = std::decay_t; std::vector isDifferentiable(insideNode.size(), true); const auto &quadRule = quadRuleFactoryMethod(intersection, order); // using Range = typename Node::FiniteElement::Traits::LocalBasisType::Traits::RangeType; using JacobiRange = typename Node::FiniteElement::Traits::LocalBasisType::Traits::JacobianType; std::vector> insideValues; std::vector> outsideValues; // Evaluate inside and outside basis functions. insideValues.resize(quadRule.size()); outsideValues.resize(quadRule.size()); std::size_t insideNodeSize = insideNode.finiteElement().localBasis().size(); std::size_t outsideNodeSize = outsideNode.finiteElement().localBasis().size(); for (std::size_t k = 0; k < quadRule.size(); ++k) { std::vector insideLocalJacobians, outsideLocalJacobians; insideLocalJacobians.resize(insideNodeSize); outsideLocalJacobians.resize(outsideNodeSize); insideValues[k].resize(insideNodeSize); outsideValues[k].resize(outsideNodeSize); auto insidePoint = intersection.geometryInInside().global(quadRule[k].position()); auto outsidePoint = intersection.geometryInOutside().global(quadRule[k].position()); insideNode.finiteElement().localBasis().evaluateJacobian(insidePoint, insideLocalJacobians); outsideNode.finiteElement().localBasis().evaluateJacobian(outsidePoint, outsideLocalJacobians); auto insideJacobiInverseTransposed = intersection.inside().geometry().jacobianInverseTransposed(insidePoint); auto outsideJacobiInverseTransposed = intersection.outside().geometry().jacobianInverseTransposed(outsidePoint); for (std::size_t i = 0; i < insideNodeSize; ++i) { // TODO make this viable for localJac of type FieldVector (at least) insideValues[k][i] = insideLocalJacobians[i] * transpose(insideJacobiInverseTransposed); if (i < outsideNodeSize) outsideValues[k][i] = outsideLocalJacobians[i] * transpose(outsideJacobiInverseTransposed); } } // Check jump against outside basis function or zero. for (std::size_t i = 0; i < insideNode.size(); ++i) { for (std::size_t k = 0; k < quadRule.size(); ++k) { auto jump = insideValues[k][i]; if (insideToOutside[i].has_value()) jump -= outsideValues[k][insideToOutside[i].value()]; isDifferentiable[i] = isDifferentiable[i] and (jumpEvaluator(jump, intersection, quadRule[k].position()) < tol); } } for (std::size_t k = 0; k < quadRule.size(); ++k) { insideValues[k].clear(); outsideValues[k].clear(); } return isDifferentiable; }; } auto localDifferentiabilityCheck() const { auto jumpNorm = [](auto &&jump, auto &&intersection, auto &&x) -> double { return jump.infinity_norm(); }; auto quadRuleProvider = [](auto &&intersection, auto &&order) { return Dune::QuadratureRules::mydimension>::rule(intersection.type(), order); }; return localJumpDifferentiabilityCheck(jumpNorm, quadRuleProvider, order_, tol_); } }; struct EnableVertexDifferentiabilityCheck: public EnableDifferentiabilityCheck { std::size_t order_ = 5; double tol_ = 1e-10; std::string checkLocation = "at vertices of"; auto localDifferentiabilityCheck() const { auto jumpNorm = [](auto &&jump, auto &&intersection, auto &&x) -> double { return jump.infinity_norm(); }; auto quadRuleProvider = [](auto &&intersection, auto &&order) { using Pt = Dune::QuadraturePoint::mydimension>; std::vector quadRule; for (int i = 0; i < intersection.geometry().corners(); ++i) quadRule.push_back( Pt{intersection.geometry().local(intersection.geometry().corner(i)), 1.}); return quadRule; }; return localJumpDifferentiabilityCheck(jumpNorm, quadRuleProvider, order_, tol_); } }; struct EnableNormalDifferentiabilityAtMidpointsCheck: public EnableDifferentiabilityCheck { std::size_t order_ = 5; double tol_ = 1e-10; std::string checkLocation = "at edge midpoint of"; auto localDifferentiabilityCheck() const { auto jumpNorm = [](auto &&jump, auto &&intersection, auto &&x) -> double { Dune::FieldVector res; jump.mv(intersection.unitOuterNormal(x), res); return res.infinity_norm(); }; auto quadRuleProvider = [](auto &&intersection, auto &&order) { using Pt = Dune::QuadraturePoint::mydimension>; std::vector quadRule; quadRule.push_back(Pt{intersection.geometry().local(intersection.geometry().center()), 1.}); return quadRule; }; return localJumpDifferentiabilityCheck(jumpNorm, quadRuleProvider, order_, tol_); } }; /* * Check if basis functions are differentiable across faces. * Differeniability is checked by evaluation at a set of quadrature points * from a quadrature rule of given order, or at a given set of points, like vertices/ center, etc. * If two basis functions (on neighboring elements) share the same * global index, their derivatives at the quadrature points (located on * their intersection) should coincide up to the given tolerance. * * If a basis function only appears on one side of the intersection, * it should be zero on the intersection. */ template Dune::TestSuite checkBasisDifferentiability(const Basis &basis, const Flag &flag) { Dune::TestSuite test("Global differentiability check of basis functions"); auto const &localCheck = flag.localDifferentiabilityCheck(); auto localView = basis.localView(); auto neighborLocalView = basis.localView(); for (const auto &e : elements(basis.gridView())) { localView.bind(e); for (const auto &intersection : intersections(basis.gridView(), e)) { if (intersection.neighbor()) { neighborLocalView.bind(intersection.outside()); Dune::TypeTree::forEachLeafNode( localView.tree(), [&](const auto &insideNode, auto &&treePath) { const auto &outsideNode = Dune::TypeTree::child(neighborLocalView.tree(), treePath); std::vector> insideToOutside; insideToOutside.resize(insideNode.size()); // Map all inside DOFs to outside DOFs if possible for (std::size_t i = 0; i < insideNode.size(); ++i) { for (std::size_t j = 0; j < outsideNode.size(); ++j) { if (localView.index(insideNode.localIndex(i)) == neighborLocalView.index(outsideNode.localIndex(j))) { // Basis function should only appear once in the neighbor element. test.check(not insideToOutside[i].has_value()) << "Basis function " << localView.index(insideNode.localIndex(i)) << " appears twice in element " << elementStr(neighborLocalView.element(), basis.gridView()); insideToOutside[i] = j; } } } // Apply continuity check on given intersection with given inside/outside DOF node // pair. auto isDifferentiable = localCheck(intersection, treePath, insideNode, outsideNode, insideToOutside); for (std::size_t i = 0; i < insideNode.size(); ++i) { test.check(isDifferentiable[i]) << "Basis function " << localView.index(insideNode.localIndex(i)) << " is not differentiable " << flag.checkLocation << " intersection of elements " << elementStr(localView.element(), basis.gridView()) << " and " << elementStr(neighborLocalView.element(), basis.gridView()); } }); } } } return test; } #endif dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/globalvaluedlfetest.cc000066400000000000000000000063651513634022200316230ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #define DUNE_FUNCTION_HH_SILENCE_DEPRECATION #include #include #include #include #include #include #include #include #include using namespace Dune; template void checkBasisFEs(const Basis& basis) { auto localView = basis.localView(); for (const auto& element : elements(basis.gridView())) { localView.bind(element); testFE(localView.tree().finiteElement(), DisableNone, 1 /* diffOrder */); } } int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); using namespace Functions::BasisFactory; // Check with UGGrid { // Generate grid for testing const int dim = 2; using Grid = UGGrid; const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/"; std::string filename = path + "curved2d.msh"; std::shared_ptr grid = GmshReader::read(filename); auto gridView = grid->leafGridView(); /////////////////////////////////////////////////////////////////////// // Test GlobalValuedLocalFiniteElement for a H(div)-conforming space // We use the Raviart-Thomas basis. /////////////////////////////////////////////////////////////////////// checkBasisFEs(makeBasis(gridView, raviartThomas<0>())); checkBasisFEs(makeBasis(gridView, raviartThomas<1>())); /////////////////////////////////////////////////////////////////////// // Test GlobalValuedLocalFiniteElement for a H(curl)-conforming space // We use the Nedelec basis of the first kind. /////////////////////////////////////////////////////////////////////// checkBasisFEs(makeBasis(gridView, nedelec<1,1,double>())); } // Check with YaspGrid { // Generate grid for testing auto grid = YaspGrid<2>({1.0, 1.0}, {5,5}); auto gridView = grid.leafGridView(); /////////////////////////////////////////////////////////////////////// // Test GlobalValuedLocalFiniteElement for a H(div)-conforming space // We use the Raviart-Thomas basis. /////////////////////////////////////////////////////////////////////// checkBasisFEs(makeBasis(gridView, raviartThomas<0>())); checkBasisFEs(makeBasis(gridView, raviartThomas<1>())); checkBasisFEs(makeBasis(gridView, raviartThomas<2>())); /////////////////////////////////////////////////////////////////////// // Test GlobalValuedLocalFiniteElement for a H(curl)-conforming space // We use the Nedelec basis of the first kind. /////////////////////////////////////////////////////////////////////// checkBasisFEs(makeBasis(gridView, nedelec<1,1,double>())); } } catch (Exception &e) { std::cerr << "Dune reported error: " << e.what() << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/gridviewfunctionspacebasistest.cc000066400000000000000000000312071513634022200341100ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; /** \param disableInterpolate Not all bases can correctly represent the linear function * that we use to test whether integrating over a given function gives the correct * value (e.g. basis without dofs on the boundary). Therefore we allow to disable this test. */ template void testScalarBasisConst(const Basis& feBasis, bool isPartitionOfUnity, bool disableInterpolate = false) { static const int dim = Basis::GridView::dimension; ////////////////////////////////////////////////////////////////////////////////////// // Run the dune-localfunctions test for the LocalFiniteElement of each grid element ////////////////////////////////////////////////////////////////////////////////////// typedef typename Basis::GridView GridView; GridView gridView = feBasis.gridView(); auto localView = feBasis.localView(); ///////////////////////////////////////////////////////////////////////// // Make sure the basis is a partition of unity ///////////////////////////////////////////////////////////////////////// if (isPartitionOfUnity) { for(const auto& e : elements(gridView)) { // Bind the local FE basis view to the current element localView.bind(e); const auto& lFE = localView.tree().finiteElement(); const QuadratureRule& quad = QuadratureRules::rule(e.type(), 3); std::vector > values; for (size_t i=0; i 1e-5) DUNE_THROW(Exception, "Basis is no partition of unity, even though it is supposed to be! Error occurred for geometry type: " << e.type()); } } } /////////////////////////////////////////////////////////////////////////////////// // Check whether the global indices are in the correct range, // and whether each global index appears at least once. /////////////////////////////////////////////////////////////////////////////////// std::vector seen(feBasis.size()); std::fill(seen.begin(), seen.end(), false); // Loop over all leaf elements for (auto it = gridView.template begin<0>(); it!=gridView.template end<0>(); ++it) { // Bind the local FE basis view to the current element localView.bind(*it); for (size_t i=0; i= seen.size()) DUNE_THROW(Exception, "Local index " << i << " is mapped to global index " << localView.index(i) << ", which is larger than allowed"); seen[localView.index(i)[0]] = true; } } for (size_t i=0; i x(feBasis.size()); if (! disableInterpolate) interpolate(feBasis, x, [](FieldVector x){ return x[0]; }); else // dummy values std::fill(x.begin(), x.end(), 0.5); // Objects required in the local context auto localView2 = feBasis.localView(); std::vector localCoefficients(localView.maxSize()); // Loop over elements and integrate over the function double integral = 0; for (const auto& element : elements(gridView)) { localView.bind(element); localView2.bind(element); // paranoia checks assert(&(localView.globalBasis()) == &(feBasis)); assert(localView.size() == localView2.size()); for (size_t i=0; i::rule(element.type(), 1+tree.finiteElement().localBasis().order()); // Loop over all quadrature points for ( size_t pt=0; pt < quad.size(); pt++ ) { // Position of the current quadrature point in the reference element const FieldVector& quadPos = quad[pt].position(); // The multiplicative factor in the integral transformation formula const double integrationElement = element.geometry().integrationElement(quadPos); // Evaluate all shape function values at this point std::vector > shapeFunctionValues; localFiniteElement.localBasis().evaluateFunction(quadPos, shapeFunctionValues); // Actually compute the vector entries for (size_t i=0; i 1e-10) DUNE_THROW(Dune::Exception, "Error: integral value is wrong!"); auto checkResult = checkConstBasis(feBasis); if (not checkResult) { checkResult.report(); DUNE_THROW(Dune::Exception, "checkConstBasis() failed"); } } template void testScalarBasis(Basis& feBasis, GV gv, bool isPartitionOfUnity, bool disableInterpolate = false) { feBasis.update(gv); testScalarBasisConst(feBasis, isPartitionOfUnity, disableInterpolate); } template void testOnStructuredGrid() { std::cout << " +++++++++++ Testing on structured " << dim << "d grids ++++++++++++" << std::endl; // Generate grid for testing typedef YaspGrid GridType; FieldVector l; std::fill(l.begin(), l.end(), 1.0); std::array elements; std::fill(elements.begin(), elements.end(), 2); GridType grid(l,elements); // Test whether function space basis can be instantiated on the leaf view typedef typename GridType::LeafGridView GridView; GridView gridView = grid.leafGridView(); LagrangeBasis pq3Basis(gridView); LagrangeBasis pq4Basis(gridView); LagrangeBasis pq0Basis(gridView); LagrangeDGBasis lagrangeDG1Basis(gridView); LagrangeDGBasis lagrangeDG2Basis(gridView); LagrangeDGBasis lagrangeDG3Basis(gridView); grid.globalRefine(2); // Test updating the basis after the grid has been changed pq3Basis.update(grid.leafGridView()); pq4Basis.update(grid.leafGridView()); pq0Basis.update(grid.leafGridView()); lagrangeDG1Basis.update(grid.leafGridView()); lagrangeDG2Basis.update(grid.leafGridView()); lagrangeDG3Basis.update(grid.leafGridView()); // Test LagrangeBasis for k==3 if (dim<3) // Currently not implemented for dim >= 3 testScalarBasis(pq3Basis, gridView, true); // Test LagrangeBasis for k==4 if (dim<3) // Currently not implemented for dim >= 3 testScalarBasis(pq4Basis, gridView, true); // Test LagrangeBasis for the corner case k == 0 testScalarBasis(pq0Basis, gridView, true); // Test LagrangeDGBasis for k==1 testScalarBasis(lagrangeDG1Basis, gridView, true); // Test LagrangeDGBasis for k==2 testScalarBasis(lagrangeDG2Basis, gridView, true); // Test LagrangeDGBasis for k==3 testScalarBasis(lagrangeDG3Basis, gridView, true); // Testing B-spline basis with open knot vectors std::vector knotVector(elements[0]*4+1); for (size_t i=0; i bSplineBasis(gridView, knotVector, order); testScalarBasisConst(bSplineBasis, true, true); // Don't interpolate a given function and try to integrate over it } // Testing B-spline basis with non-open knot vectors std::cout << " Testing B-spline basis with non-open knot vectors" << std::endl; for (unsigned int order : {0, 1, 2}) { std::cout << " order: " << order << std::endl; BSplineBasis bSplineBasis(gridView, knotVector, order, false); testScalarBasisConst(bSplineBasis, order==0, // only zero-order B-splines for a partition of unity true); // Don't interpolate a given function and try to integrate over it } } template void testOnHybridGrid() { std::cout << " +++++++++++ Testing on hybrid " << dim << "d grid ++++++++++++" << std::endl; // Generate grid for testing typedef UGGrid GridType; const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/"; std::string filename = path + "hybrid-testgrid-" + std::to_string(dim) + "d.msh"; std::shared_ptr grid(GmshReader::read(filename)); // Test whether function space basis can be instantiated on the leaf view typedef typename GridType::LeafGridView GridView; GridView gridView = grid->leafGridView(); // Disable the global interpolation test for 3d grids. // Global interpolation should work for all grids and spaces, but the hard-coded // reference value is wrong. bool disableInterpolate = (dim==3); // Test LagrangeBasis for k==1 LagrangeBasis pq1Basis(gridView); testScalarBasisConst(pq1Basis, true, disableInterpolate); // Test LagrangeBasis for k==3 LagrangeBasis pq3Basis(gridView); if (dim<3) // Currently not implemented for dim >= 3 testScalarBasisConst(pq3Basis, true, disableInterpolate); // Test LagrangeBasis for k==4 LagrangeBasis pq4Basis(gridView); if (dim<3) // Currently not implemented for dim >= 3 testScalarBasisConst(pq4Basis, true, disableInterpolate); // Test LagrangeDGBasis for k==1 LagrangeDGBasis lagrangeDG1Basis(gridView); testScalarBasisConst(lagrangeDG1Basis, true, disableInterpolate); // Test LagrangeDGBasis for k==2 // \todo Enable these tests once pyramid element of order two is bug free // LagrangeDGBasis lagrangeDG2Basis(gridView); // testScalarBasisConst(lagrangeDG2Basis, true, disableInterpolate); // Test LagrangeDGBasis for k==3 // \todo Enable these tests once pyramid element of order three is implemented // LagrangeDGBasis lagrangeDG3Basis(gridView); // testScalarBasisConst(lagrangeDG3Basis, true, disableInterpolate); } int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); testOnStructuredGrid<1>(); testOnStructuredGrid<2>(); testOnStructuredGrid<3>(); testOnHybridGrid<2>(); testOnHybridGrid<3>(); return 0; } catch ( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/hierarchicallagrangebasistest.cc000066400000000000000000000037771513634022200336400ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include using namespace Dune; template void testDim(TestSuite& test) { static const int dim = Grid::dimension; using Factory = StructuredGridFactory; FieldVector lower(0.0), upper(1.0); std::array elems = Dune::filledArray(1<<(5-dim)); auto gridPtr = Factory::createSimplexGrid(lower, upper, elems); using GridView = typename Grid::LeafGridView; auto gridView = gridPtr->leafGridView(); // check HierarchicalBasis created 'manually' { Functions::HierarchicalLagrangeBasis basis1(gridView); test.subTest(checkBasis(basis1, EnableContinuityCheck())); Functions::HierarchicalLagrangeBasis basis2(gridView); test.subTest(checkBasis(basis2, EnableContinuityCheck())); } // check HierarchicalBasis created using basis builder mechanism { using namespace Functions::BasisFactory; auto basis1 = makeBasis(gridView, hierarchicalLagrange<1>()); test.subTest(checkBasis(basis1, EnableContinuityCheck())); auto basis2 = makeBasis(gridView, hierarchicalLagrange<2>()); test.subTest(checkBasis(basis2, EnableContinuityCheck())); } } int main (int argc, char* argv[]) { MPIHelper::instance(argc, argv); TestSuite test; testDim(test); testDim>(test); testDim>(test); return test.exit(); } hierarchicallagrangewithelementbubblebasistest.cc000066400000000000000000000052101513634022200371630ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include using namespace Dune; template TestSuite checkDimension (const GridView& gridView) { TestSuite test("Check dimension " + std::to_string(GridView::dimension)); { // check HierarchicalBasis created 'manually' Functions::HierarchicalLagrangeWithElementBubbleBasis basis1(gridView); test.subTest(checkBasis(basis1, EnableContinuityCheck())); Functions::HierarchicalLagrangeWithElementBubbleBasis basis2(gridView); test.subTest(checkBasis(basis2, EnableContinuityCheck())); } { // check HierarchicalBasis created using basis builder mechanism using namespace Functions::BasisFactory; auto basis1 = makeBasis(gridView, hierarchicalLagrangeWithElementBubble<1>()); test.subTest(checkBasis(basis1, EnableContinuityCheck())); auto basis2 = makeBasis(gridView, hierarchicalLagrangeWithElementBubble<2>()); test.subTest(checkBasis(basis2, EnableContinuityCheck())); } { // check HierarchicalBasis created using basis builder mechanism using namespace Functions::BasisFactory; auto basis1 = makeBasis(gridView, hierarchicalP1B()); test.subTest(checkBasis(basis1, EnableContinuityCheck())); auto basis2 = makeBasis(gridView, hierarchicalP2B()); test.subTest(checkBasis(basis2, EnableContinuityCheck())); } return test; } int main (int argc, char* argv[]) { MPIHelper::instance(argc, argv); TestSuite test; { // 1d using Factory = StructuredGridFactory; auto grid = Factory::createSimplexGrid({0.0}, {1.0}, {4u}); test.subTest(checkDimension(grid->leafGridView())); } { // 2d using Factory = StructuredGridFactory>; auto grid = Factory::createSimplexGrid({0.0,0.0}, {1.0,1.0}, {4u,4u}); test.subTest(checkDimension(grid->leafGridView())); } { // 3d using Factory = StructuredGridFactory>; auto grid = Factory::createSimplexGrid({0.0,0.0,0.0}, {1.0,1.0,1.0}, {4u,4u,4u}); test.subTest(checkDimension(grid->leafGridView())); } return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/interpolatetest.hh000066400000000000000000000037361513634022200310320ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_INTERPOLATETEST_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_INTERPOLATETEST_HH #include #include #include #include #include #include #include double infinityDiff(const double& x, const double& y) { return std::fabs(x-y); } double infinityDiff(const bool& x, const bool& y) { return std::fabs(x-y); } template double infinityDiff(const X& x, const Y& y) { if (x.size() != y.size()) return false; double diff = 0; Dune::Hybrid::forEach(Dune::range(Dune::Hybrid::size(x)), [&](auto i) { auto&& xi = Dune::Hybrid::elementAt(x, i); auto&& yi = Dune::Hybrid::elementAt(y, i); diff = std::max(diff, infinityDiff(xi, yi)); }); return diff; } template Dune::TestSuite checkInterpolateConsistency(Basis basis, C&& x) { using Coefficients = std::decay_t; Dune::TestSuite suite("interpolate consistency check"); double coeffTol = 1e-10; // generate a discrete function auto fGridFunction = Dune::Functions::makeDiscreteGlobalBasisFunction(basis, x); // Check both functions { const auto& f = fGridFunction; Coefficients y; Dune::Functions::interpolate(basis, y, f); suite.check(infinityDiff(x, y) < coeffTol) << "Interpolation of DiscreteGlobalBasisFunction via local operator() differs from original coefficient vector" << std::endl; } return suite; } #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_INTERPOLATETEST_HH dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/lagrangebasistest.cc000066400000000000000000000052661513634022200312740ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; int main (int argc, char* argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; using namespace Dune::Functions::BasisFactory; { const int dim = 2; using Grid = Dune::UGGrid; Dune::GridFactory factory; factory.insertVertex({0,0}); factory.insertVertex({0,1}); factory.insertVertex({1,0}); factory.insertVertex({1,1}); factory.insertElement(Dune::GeometryTypes::simplex(2), {0,1,2}); factory.insertElement(Dune::GeometryTypes::simplex(2), {1,2,3}); std::unique_ptr grid(factory.createGrid()); grid->globalRefine(2); auto gridView = grid->leafGridView(); auto basis = makeBasis(gridView, lagrange<3>()); test.subTest(checkBasis(basis, EnableContinuityCheck())); std::vector v; v.resize(basis.size(), 0); v[5] = 1; auto v_f = Dune::Functions::makeDiscreteGlobalBasisFunction(basis,v); SubsamplingVTKWriter vtkWriter(gridView, Dune::refinementLevels(5)); vtkWriter.addVertexData(v_f, VTK::FieldInfo("lambda_5", VTK::FieldInfo::Type::scalar, 1)); vtkWriter.write("debug"); } { std::unique_ptr grid = StructuredGridFactory::createCubeGrid({0}, {1}, {10}); auto gridView = grid->levelGridView(0); { auto basis = makeBasis(gridView, lagrange<3>()); test.subTest(checkBasis(basis, EnableContinuityCheck())); } { auto basis = makeBasis(gridView, lagrange(2)); test.subTest(checkBasis(basis, EnableContinuityCheck())); } { auto basis = makeBasis(gridView, lagrange<3,float>()); test.subTest(checkBasis(basis, EnableContinuityCheck())); } { auto basis = makeBasis(gridView, lagrange(2)); test.subTest(checkBasis(basis, EnableContinuityCheck())); } } return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/lagrangedgbasistest.cc000066400000000000000000000036221513634022200316010ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; int main (int argc, char* argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; // Generate grid for testing const int dim = 2; typedef YaspGrid GridType; FieldVector l(1); std::array elements = {{10, 10}}; GridType grid(l,elements); // check LagrangeDGBasis with static order created 'manually' { typedef GridType::LeafGridView GridView; const GridView& gridView = grid.leafGridView(); LagrangeDGBasis basis(gridView); test.subTest(checkBasis(basis)); } // check LagrangeDGBasis with dynamic order created 'manually' { typedef GridType::LeafGridView GridView; const GridView& gridView = grid.leafGridView(); LagrangeDGBasis basis(gridView, 2); test.subTest(checkBasis(basis)); } // check LagrangeDGBasis with static order created using basis builder mechanism { using namespace Functions::BasisFactory; auto basis = makeBasis(grid.leafGridView(), lagrangeDG<2>()); test.subTest(checkBasis(basis)); } // check LagrangeDGBasis with dynamic order created using basis builder mechanism { using namespace Functions::BasisFactory; auto basis = makeBasis(grid.leafGridView(), lagrangeDG(2)); test.subTest(checkBasis(basis)); } return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/lfebasistest.cc000066400000000000000000000054441513634022200302600ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; // define a pre-basis based on the LFEPreBasisMixin template class RefinedP0PreBasis : public Dune::Functions::LFEPreBasisMixin> { using LFE = Dune::RefinedP0LocalFiniteElement; using Base = LFEPreBasisMixin; static const int dim = GV::dimension; public: RefinedP0PreBasis (const GV& gv) : Base(gv, [](Dune::GeometryType gt, int) { return (gt.dim() == dim) ? (1 << dim) : 0; }) {} }; int main (int argc, char* argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; using namespace Dune::Functions::BasisFactory; { const int dim = 2; using Grid = Dune::UGGrid; using Factory = Dune::StructuredGridFactory; auto gridPtr = Factory::createSimplexGrid({0.0,0.0},{1.0,1.0},{1u,1u}); gridPtr->globalRefine(2); auto gridView = gridPtr->leafGridView(); using GridView = decltype(gridView); // compare two bases that should be exactly identical, but that are implemented differently auto basis0 = makeBasis(gridView, refinedLagrange<0>()); auto basis1 = DefaultGlobalBasis>(gridView); test.subTest(checkBasis(basis0)); test.subTest(checkBasis(basis1)); auto localView0 = basis0.localView(); auto localView1 = basis1.localView(); test.check(localView0.maxSize() == localView1.maxSize(), "maxSize"); for (auto const& e : elements(gridView)) { localView0.bind(e); localView1.bind(e); test.check(localView0.size() == localView1.size(), "size"); for (std::size_t i = 0; i < localView0.size(); ++i) { test.check(localView0.index(i) == localView1.index(i), "index"); } auto const& lfe0 = localView0.tree().finiteElement(); auto const& lfe1 = localView1.tree().finiteElement(); using LFE0 = std::decay_t; using LFE1 = std::decay_t; static_assert(std::is_same_v); } } return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/makebasistest.cc000066400000000000000000000135271513634022200304300ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // This wrapper will hide the derivative() of an existing GridFunction // making it non-differentiable for testing. template class NonDifferentiableGridFunction : protected GlobalBase { using LocalBase = std::decay_t()))>; class LocalFunction : private LocalBase { public: LocalFunction(const LocalBase& base) : LocalBase(base) {} LocalFunction(LocalBase&& base) : LocalBase(base) {} using LocalBase::operator(); using LocalBase::bind; using LocalBase::unbind; using LocalBase::bound; using LocalBase::localContext; }; public: NonDifferentiableGridFunction(GlobalBase&& base) : GlobalBase(base) {} using GlobalBase::operator(); using GlobalBase::entitySet; friend LocalFunction localFunction(const NonDifferentiableGridFunction& f) { return LocalFunction(localFunction(static_cast(f))); } }; template Dune::TestSuite checkMakeBasis(const GridView& gridView) { Dune::TestSuite test; using namespace Dune::Functions::BasisFactory; const int N = 10; const int M = 10; auto basis = makeBasis(gridView, power( // static power node power( // dynamic power node composite( lagrange<3>(), // lagrange basis with static order lagrange(1), // lagrange basis with dynamic order flatLexicographic()), M, flatLexicographic()), blockedInterleaved()) ); test.subTest(checkBasis(basis, EnableContinuityCheck())); { [[maybe_unused]] auto& firstLagrangeFactor = basis.preBasis().subPreBasis().subPreBasis().subPreBasis(Dune::Indices::_0); [[maybe_unused]] auto& secondLagrangeFactor = basis.preBasis().subPreBasis().subPreBasis().template subPreBasis<1>(); } using Vector = std::vector>; Vector x; auto f = [](const auto& x){ std::array, M>, N> y; for(auto& yi : y) for(auto& yij : yi) yij = 1.0; return y; }; Dune::Functions::interpolate(basis, x, f); for(const auto& xi : x) for(const auto& xij : xi) test.require(std::abs(xij - 1.0) < 1e-10) << "Coefficient of interpolated 1-function does not match"; // Now check the same but provide f as non-differentiable GridFunction auto nonDiffF = NonDifferentiableGridFunction(Dune::Functions::makeAnalyticGridViewFunction(f, basis.gridView())); Dune::Functions::interpolate(basis, x, nonDiffF); for(const auto& xi : x) for(const auto& xij : xi) test.require(std::abs(xij - 1.0) < 1e-10) << "Coefficient of interpolated 1-function does not match"; { auto basis = makeBasis(gridView, power<2>( composite( power<1>(power<1>(lagrange<1>(), blockedInterleaved()), blockedInterleaved()), power<2>(lagrange<1>(), blockedInterleaved()), power<3>(lagrange<1>(), blockedInterleaved()), blockedLexicographic() ), flatInterleaved() ) ); test.subTest(checkBasis(basis, EnableContinuityCheck())); } { auto basis = makeBasis(gridView, power<2>( composite( power<1>(power<1>(lagrange<1>(), blockedInterleaved()), blockedInterleaved()), power<2>(lagrange<1>(), blockedInterleaved()), power<3>(lagrange<1>(), blockedInterleaved()), blockedLexicographic() ), flatLexicographic() ) ); test.subTest(checkBasis(basis, EnableContinuityCheck())); } return test; } int main (int argc, char* argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; // Check with YaspGrid { const int dim = 2; using Grid = Dune::YaspGrid; Dune::FieldVector l(1); std::array elements = {{10, 10}}; Grid grid(l,elements); auto gridView = grid.leafGridView(); test.subTest(checkMakeBasis(gridView)); } // Check with mixed UGGrid { using Grid = Dune::UGGrid<2>; auto factory = Dune::GridFactory(); for(unsigned int k : Dune::range(9)) factory.insertVertex({0.5*(k%3), 0.5*(k/3)}); factory.insertElement(Dune::GeometryTypes::cube(2), {0, 1, 3, 4}); factory.insertElement(Dune::GeometryTypes::cube(2), {1, 2, 4, 5}); factory.insertElement(Dune::GeometryTypes::simplex(2), {3, 4, 6}); factory.insertElement(Dune::GeometryTypes::simplex(2), {4, 7, 6}); factory.insertElement(Dune::GeometryTypes::simplex(2), {4, 5, 7}); factory.insertElement(Dune::GeometryTypes::simplex(2), {5, 8, 7}); auto gridPtr = std::unique_ptr(factory.createGrid()); auto& grid = *gridPtr; auto gridView = grid.leafGridView(); test.subTest(checkMakeBasis(gridView)); } return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/morleytest.cc000066400000000000000000000032531513634022200277730ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; int main(int argc, char *argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test("morley"); using namespace Dune::Functions::BasisFactory; { // 2d using Grid = UGGrid<2>; // using Grid = YaspGrid<2>; auto grid = StructuredGridFactory::createSimplexGrid({0., 0.}, {1., 1.}, {{3, 3}}); auto gridView = grid->leafGridView(); std::cout << "Grid has " << gridView.size(0) << " elementes and " << gridView.size(1) << " facettes and " << gridView.size(2) << " vertices" << std::endl; { using namespace Dune::Functions::BasisFactory; auto basis = makeBasis(gridView, morley()); std::cout << "Basis has " << basis.size() << " Dofs" << std::endl; test.subTest(checkBasis(basis, EnableVertexContinuityCheck(), EnableNormalDifferentiabilityAtMidpointsCheck(), CheckLocalFiniteElementFlag())); } } return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/nedelecbasistest.cc000066400000000000000000000073061513634022200311100ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include using namespace Dune; template void testNedelecBasis(TestSuite& test, const GridFactory& factory) { auto grid = factory(); auto gridView = grid->leafGridView(); std::cout<<" Testing order: "<< order < basis1(gridView); test.subTest(checkBasis(basis1, EnableTangentialContinuityCheck())); // Check NedelecBasis created using basis builder mechanism using namespace Functions::BasisFactory; auto basis2 = makeBasis(gridView, nedelec()); test.subTest(checkBasis(basis2, EnableTangentialContinuityCheck())); // Now modify the grid, and check again. const auto firstEntity = gridView.template begin<0>(); grid->mark(1, *firstEntity); grid->adapt(); auto modifiedGridView = grid->leafGridView(); // Check the NedelecBasis that was created 'manually' basis1.update(modifiedGridView); test.subTest(checkBasis(basis1, EnableTangentialContinuityCheck())); // Check the NedelecBasis that was created using the basis builder mechanism basis2.update(modifiedGridView); test.subTest(checkBasis(basis2, EnableTangentialContinuityCheck())); } int main (int argc, char* argv[]) { MPIHelper::instance(argc, argv); const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/"; TestSuite test; std::cout<<"Testing NedelecBasis in 2D with simplex grid\n"; auto triangleGridFactory = [&path]() { return GmshReader >::read(path + "curved2d.msh"); }; testNedelecBasis<1, 1>(test, triangleGridFactory); // Test with grid that only supports cube elements std::cout<<"Testing NedelecBasis in 2D with cube grid\n"; auto quadGridFactory = []() { return std::make_unique >(FieldVector{1.0, 1.0}, std::array{5,5}); }; testNedelecBasis<1, 1>(test, quadGridFactory); std::cout<<"Testing NedelecBasis in 2D with mixed-element grid\n"; auto mixed2dGridFactory = [&path]() { return GmshReader >::read(path + "hybrid-testgrid-2d.msh"); }; testNedelecBasis<1, 1>(test, mixed2dGridFactory); std::cout<<"Testing NedelecBasis in 3D with simplex grid\n"; auto tetraGridFactory = [&path]() { return GmshReader >::read(path + "telescope1storder.msh"); }; testNedelecBasis<1, 1>(test, tetraGridFactory); // Test with grid that only supports cube elements std::cout<<"Testing NedelecBasis in 3D with cube grid\n"; auto cubeGridFactory = []() { return std::make_unique >(FieldVector{1.0, 1.0, 1.0}, std::array{5,5,5}); }; testNedelecBasis<1, 1>(test, cubeGridFactory); // hybrid-testgrid-3d.msh contains pyramids and prisms, which are not implemented // std::cout<<"Testing NedelecBasis in 3D with mixed-element grid\n"; // auto mixed3dGridFactory = [&path]() { // return GmshReader >::read(path + "hybrid-testgrid-3d.msh"); // }; // //testNedelecBasis<1, 1>(test, mixed3dGridFactory); return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/periodicbasistest.cc000066400000000000000000000104041513634022200313000ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include using namespace Dune; int main (int argc, char *argv[]) try { // Set up MPI, if available MPIHelper::instance(argc, argv); Dune::TestSuite test; /////////////////////////////////// // Generate the grid /////////////////////////////////// const int dim = 2; using Grid = YaspGrid; FieldVector l(1); std::array elements = {{4, 4}}; Grid grid(l,elements); using GridView = Grid::LeafGridView; GridView gridView = grid.leafGridView(); std::cout << "Host grid has " << gridView.size(dim) << " vertices." << std::endl; ////////////////////////////////////////////////// // Infrastructure for handling periodicity ////////////////////////////////////////////////// // Check whether two points are equal on R/Z x R/Z auto equivalent = [](const FieldVector& x, const FieldVector& y) { return ( (FloatCmp::eq(x[0],y[0]) or FloatCmp::eq(x[0]+1,y[0]) or FloatCmp::eq(x[0]-1,y[0])) and (FloatCmp::eq(x[1],y[1]) or FloatCmp::eq(x[1]+1,y[1]) or FloatCmp::eq(x[1]-1,y[1])) ); }; ///////////////////////////////////////////////////////// // Test a PeriodicBasis all by itself ///////////////////////////////////////////////////////// using namespace Functions::BasisFactory; using namespace Functions::BasisFactory::Experimental; { PeriodicIndexSet periodicIndices; // Don't do the following in real life: It has quadratic run-time in the number of vertices. for (const auto& v1 : vertices(gridView)) for (const auto& v2 : vertices(gridView)) if (equivalent(v1.geometry().corner(0), v2.geometry().corner(0))) periodicIndices.unifyIndexPair(gridView.indexSet().index(v1), gridView.indexSet().index(v2)); auto basis = makeBasis(gridView, lagrange<1>()); { auto periodicBasis = makeBasis(gridView, periodic(lagrange<1>(), periodicIndices)); std::cout << "Solitary periodic basis has " << periodicBasis.dimension() << " degrees of freedom." << std::endl; test.subTest(checkBasis(periodicBasis, EnableContinuityCheck())); } { auto periodicBasis = makeBasis(gridView, periodic(basis.preBasis(), periodicIndices)); std::cout << "Solitary periodic basis has " << periodicBasis.dimension() << " degrees of freedom." << std::endl; test.subTest(checkBasis(periodicBasis, EnableContinuityCheck())); } { auto periodicBasis = makeBasis(gridView, periodic(basis, periodicIndices)); std::cout << "Solitary periodic basis has " << periodicBasis.dimension() << " degrees of freedom." << std::endl; test.subTest(checkBasis(periodicBasis, EnableContinuityCheck())); } } ///////////////////////////////////////////////////////// // Test a PeriodicBasis in a power basis ///////////////////////////////////////////////////////// { PeriodicIndexSet periodicIndices; // Don't do the following in real life: It has quadratic run-time in the number of vertices. for (const auto& v1 : vertices(gridView)) for (const auto& v2 : vertices(gridView)) if (equivalent(v1.geometry().corner(0), v2.geometry().corner(0))) periodicIndices.unifyIndexPair(gridView.indexSet().index(v1), gridView.indexSet().index(v2)); // Check whether power does at least compile auto periodicBasis = makeBasis( gridView, power( periodic(lagrange<1>(), periodicIndices), blockedInterleaved() )); std::cout << "power basis has " << periodicBasis.dimension() << " degrees of freedom" << std::endl; test.subTest(checkBasis(periodicBasis, EnableContinuityCheck())); } } catch (Exception& e) { std::cout << e.what() << std::endl; } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/rannacherturekbasistest.cc000066400000000000000000000113361513634022200325230ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; template void testRannacherTurekBasis(TestSuite& test) { /////////////////////////// ///// Simplex grids ///// /////////////////////////// // Check RannacherTurekBasis created 'manually' // Use grid with unknown-at-compile-time element type { using Grid = UGGrid; const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/"; auto grid = (dim==2) ? GmshReader::read(path + "curved2d.msh") : GmshReader::read(path + "telescope1storder.msh"); Functions::RannacherTurekBasis basis(grid->leafGridView()); test.subTest(checkBasis(basis, EnableCenterContinuityCheck())); } // Check RannacherTurekBasis created using basis builder mechanism // Use grid with unknown-at-compile-time element type { using Grid = UGGrid; const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/"; auto grid = (dim==2) ? GmshReader::read(path + "curved2d.msh") : GmshReader::read(path + "telescope1storder.msh"); using namespace Functions::BasisFactory; auto basis = makeBasis(grid->leafGridView(), rannacherTurek()); test.subTest(checkBasis(basis, EnableCenterContinuityCheck())); } /////////////////////////// ///// Cube grids ///// /////////////////////////// // Check RannacherTurekBasis created 'manually' // Use grid with known-at-compile-time element type { using Grid = YaspGrid; Dune::FieldVector one(1); std::array elems; elems.fill(5); Grid grid(one, elems); Functions::RannacherTurekBasis basis(grid.leafGridView()); test.subTest(checkBasis(basis, EnableCenterContinuityCheck())); } // Check RannacherTurekBasis created using basis builder mechanism // Use grid with known-at-compile-time element type { using Grid = YaspGrid; Dune::FieldVector one(1); std::array elems; elems.fill(5); Grid grid(one, elems); using namespace Functions::BasisFactory; auto basis = makeBasis(grid.leafGridView(), rannacherTurek()); test.subTest(checkBasis(basis, EnableCenterContinuityCheck())); } /////////////////////////// ///// Hybrid grids ///// /////////////////////////// // only for dim = 2 // hybrid-testgrid-3d.msh contains pyramids and prisms, which are not implemented if (dim ==2) { // Check RannacherTurekBasis created 'manually' // Use grid with unknown-at-compile-time element type { using Grid = UGGrid; const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/"; auto grid = (dim==2) ? GmshReader::read(path + "hybrid-testgrid-2d.msh") : GmshReader::read(path + "hybrid-testgrid-3d.msh"); Functions::RannacherTurekBasis basis(grid->leafGridView()); test.subTest(checkBasis(basis, EnableCenterContinuityCheck())); } // Check RannacherTurekBasis created using basis builder mechanism // Use grid with unknown-at-compile-time element type { using Grid = UGGrid; const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/"; auto grid = (dim==2) ? GmshReader::read(path + "hybrid-testgrid-2d.msh") : GmshReader::read(path + "hybrid-testgrid-3d.msh"); using namespace Functions::BasisFactory; auto basis = makeBasis(grid->leafGridView(), rannacherTurek()); test.subTest(checkBasis(basis, EnableCenterContinuityCheck())); } } } int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; testRannacherTurekBasis<2>(test); testRannacherTurekBasis<3>(test); return test.exit(); } catch ( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/raviartthomasbasistest.cc000066400000000000000000000101261513634022200323670ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include using namespace Dune; template void testRaviartThomasBasis(TestSuite& test, const GridFactory& factory) { auto grid = factory(); auto gridView = grid->leafGridView(); std::cout<<" Testing order: "<< k < basis1(gridView); test.subTest(checkBasis(basis1, EnableNormalContinuityCheck())); // Check RaviartThomasBasis created using basis builder mechanism using namespace Functions::BasisFactory; auto basis2 = makeBasis(gridView, raviartThomas()); test.subTest(checkBasis(basis2, EnableNormalContinuityCheck())); // Now modify the grid, and check again. const auto firstEntity = gridView.template begin<0>(); grid->mark(1, *firstEntity); grid->adapt(); auto modifiedGridView = grid->leafGridView(); // Check the RaviartThomasBasis that was created 'manually' basis1.update(modifiedGridView); test.subTest(checkBasis(basis1, EnableNormalContinuityCheck())); // Check the RaviartThomasBasis that was created using the basis builder mechanism basis2.update(modifiedGridView); test.subTest(checkBasis(basis2, EnableNormalContinuityCheck())); } int main (int argc, char* argv[]) { MPIHelper::instance(argc, argv); const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/"; TestSuite test; // Test with grid that only supports cube elements // (Grids with only a single element type receive special treatment by RaviartThomasBasis) std::cout<<"Testing RaviartThomasBasis in 2D with cube grids\n"; auto quadGridFactory = []() { return std::make_unique >(FieldVector{1.0, 1.0}, std::array{5,5}); }; testRaviartThomasBasis<0>(test, quadGridFactory); testRaviartThomasBasis<1>(test, quadGridFactory); testRaviartThomasBasis<2>(test, quadGridFactory); std::cout<<"Testing RaviartThomasBasis in 3D with cube grids\n"; auto hexaGridFactory = []() { return std::make_unique >(FieldVector{1.0, 1.0, 1.0}, std::array{4,4,4}); }; testRaviartThomasBasis<0>(test, hexaGridFactory); testRaviartThomasBasis<1>(test, hexaGridFactory); // Test with pure simplex grid // (Unfortunately there is no grid implementation available that only supports simplices.) std::cout<<"Testing RaviartThomasBasis in 2D with simplex grid\n"; auto triangleGridFactory = [&path]() { return GmshReader >::read(path + "curved2d.msh"); }; testRaviartThomasBasis<0>(test, triangleGridFactory); testRaviartThomasBasis<1>(test, triangleGridFactory); std::cout<<"Testing RaviartThomasBasis in 3D with simplex grid\n"; auto tetraGridFactory = [&path]() { return GmshReader >::read(path + "telescope.msh"); }; testRaviartThomasBasis<0>(test, tetraGridFactory); // Test with mixed-element 2d grid std::cout<<"Testing RaviartThomasBasis in 2D with mixed-element grid\n"; auto mixed2dGridFactory = [&path]() { return GmshReader >::read(path + "hybrid-testgrid-2d.msh"); }; testRaviartThomasBasis<0>(test, mixed2dGridFactory); //testRaviartThomasBasis<1>(test, mixed2dGridFactory); // Test with mixed-element 3d grid std::cout<<"Testing RaviartThomasBasis in 3D with mixed-element grid\n"; auto mixed3dGridFactory = [&path]() { return GmshReader >::read(path + "hybrid-testgrid-3d.msh"); }; testRaviartThomasBasis<0>(test, mixed3dGridFactory); return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/refinedlagrangebasistest.cc000066400000000000000000000053601513634022200326240ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; int main (int argc, char* argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; using namespace Dune::Functions::BasisFactory; { const int dim = 2; using Grid = Dune::AlbertaGrid; std::unique_ptr grid = Dune::StructuredGridFactory::createSimplexGrid({0.0,0.0},{1.0,1.0},{1u,1u}); grid->globalRefine(2); auto gridView = grid->leafGridView(); auto basis0 = makeBasis(gridView, refinedLagrange<0>()); auto basis1 = makeBasis(gridView, refinedLagrange<1>()); test.subTest(checkBasis(basis0)); test.subTest(checkBasis(basis1, EnableContinuityCheck())); std::vector v0,v1; v0.resize(basis0.size(), 0); v1.resize(basis1.size(), 0); v0[2] = 1; v1[5] = 1; auto v0_f = Dune::Functions::makeDiscreteGlobalBasisFunction(basis0,v0); auto v1_f = Dune::Functions::makeDiscreteGlobalBasisFunction(basis1,v1); SubsamplingVTKWriter vtkWriter(gridView, Dune::refinementLevels(5)); vtkWriter.addVertexData(v0_f, VTK::FieldInfo("refinedP0", VTK::FieldInfo::Type::scalar, 1)); vtkWriter.addVertexData(v1_f, VTK::FieldInfo("refinedP1", VTK::FieldInfo::Type::scalar, 1)); vtkWriter.write("debug"); } { std::unique_ptr grid = StructuredGridFactory::createSimplexGrid({0}, {1}, {10}); auto gridView = grid->levelGridView(0); { auto basis0 = makeBasis(gridView, refinedLagrange<0>()); test.subTest(checkBasis(basis0)); auto basis1 = makeBasis(gridView, refinedLagrange<1>()); test.subTest(checkBasis(basis1, EnableContinuityCheck())); } { auto basis0 = makeBasis(gridView, refinedLagrange<0,float>()); test.subTest(checkBasis(basis0)); auto basis1 = makeBasis(gridView, refinedLagrange<1,float>()); test.subTest(checkBasis(basis1, EnableContinuityCheck())); } } return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/restrictedprebasistest.cc000066400000000000000000000166561513634022200324000ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main (int argc, char* argv[]) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; // Create a Grid for testing const int dim = 2; using Grid = Dune::UGGrid; Dune::GridFactory factory; factory.insertVertex({0,0}); factory.insertVertex({0,1}); factory.insertVertex({1,0}); factory.insertVertex({1,1}); factory.insertElement(Dune::GeometryTypes::simplex(2), {0,1,2}); factory.insertElement(Dune::GeometryTypes::simplex(2), {1,2,3}); auto grid = factory.createGrid(); grid->globalRefine(3); auto gridView = grid->leafGridView(); using namespace Dune::Functions::BasisFactory; using namespace Dune::Functions::Experimental::BasisFactory; using namespace Dune::Indices; using Dune::Functions::Experimental::SubDomain; using Dune::Functions::interpolate; // Indicator functions for four square subdomains covering the four quadrants // and one overlapping square subdomain in the center auto subDomainAIndicator = [](auto x) { return (x[0] <= 0.5) and (x[1] <= 0.5); }; auto subDomainBIndicator = [](auto x) { return (x[0] > 0.5) and (x[1] <= 0.5); }; auto subDomainCIndicator = [](auto x) { return (x[0] <= 0.5) and (x[1] > 0.5); }; auto subDomainDIndicator = [](auto x) { return (x[0] > 0.5) and (x[1] > 0.5); }; auto subDomainEIndicator = [&](auto x) { return (std::fabs(x[0] - 0.5) <= 0.25) and (std::fabs(x[1] - 0.5) <= 0.25); }; // Create corresponding SubDomain objects auto subDomainA = SubDomain(gridView); for(auto&& element : elements(gridView)) if (subDomainAIndicator(element.geometry().center())) subDomainA.insertElement(element); auto subDomainB = SubDomain(gridView); for(auto&& element : elements(gridView)) if (subDomainBIndicator(element.geometry().center())) subDomainB.insertElement(element); auto subDomainC = SubDomain(gridView); for(auto&& element : elements(gridView)) if (subDomainCIndicator(element.geometry().center())) subDomainC.insertElement(element); auto subDomainD = SubDomain(gridView); for(auto&& element : elements(gridView)) if (subDomainDIndicator(element.geometry().center())) subDomainD.insertElement(element); auto subDomainE = SubDomain(gridView); for(auto&& element : elements(gridView)) if (subDomainEIndicator(element.geometry().center())) subDomainE.insertElement(element); // Create a basis auto basis = makeBasis(gridView, composite( restrict(lagrange<3>(), subDomainA), restrict( composite( power(lagrange<2>(),flatInterleaved()), lagrange<1>(), flatLexicographic()), subDomainB), restrict(rannacherTurek(), subDomainC), restrict(lagrangeDG<5>(), subDomainD), lagrange<2>(), restrict(lagrange<5>(), subDomainE), blockedLexicographic() ) ); // Run basis check test.subTest(checkBasis(basis)); // Now test grid functions on the composed basis: // We interpolate the sinsin function in the four quadrants // and once globally. Furthermore we interpolate a quartic bubble // on the center subdomain. // auto sinsin = [](auto x) { return std::sin(x[0]*2*std::numbers::pi)*std::sin(x[1]*2*std::numbers::pi); }; auto bubble = [&] (auto x) { return 256*(x[0]-0.25)*(0.75-x[0])*(x[1]-0.25)*(0.75-x[1]); }; auto c = Dune::Functions::makeISTLVector(basis.preBasis().containerDescriptor()); auto c_backend = Dune::Functions::istlVectorBackend(c); interpolate(subspaceBasis(basis, _0), c_backend, sinsin); interpolate(subspaceBasis(basis, _1, _1), c_backend, sinsin); interpolate(subspaceBasis(basis, _2), c_backend, sinsin); interpolate(subspaceBasis(basis, _3), c_backend, sinsin); interpolate(subspaceBasis(basis, _4), c_backend, sinsin); interpolate(subspaceBasis(basis, _5), c_backend, bubble); // Now we add the functions from all subdomains and subtract // the global sinsin interpolation. The result should be // (up to mismatch of the FE spaces) the bubble again. using Range = Dune::TupleVector, double>, double, double, double, double, double>; auto c_gf = Dune::Functions::makeDiscreteGlobalBasisFunction(basis, c_backend); auto bubble_gf = Dune::Functions::makeComposedGridFunction([](auto y) { return y[_0] + y[_1][_1] + y[_2] + y[_3] - y[_4] + y[_5]; }, c_gf); // Finally we write all grid functions to vtk auto c0_gf = Dune::Functions::makeDiscreteGlobalBasisFunction(subspaceBasis(basis, _0), c_backend); auto c1_gf = Dune::Functions::makeDiscreteGlobalBasisFunction(subspaceBasis(basis, _1,_1), c_backend); auto c2_gf = Dune::Functions::makeDiscreteGlobalBasisFunction(subspaceBasis(basis, _2), c_backend); auto c3_gf = Dune::Functions::makeDiscreteGlobalBasisFunction(subspaceBasis(basis, _3), c_backend); auto c4_gf = Dune::Functions::makeDiscreteGlobalBasisFunction(subspaceBasis(basis, _4), c_backend); auto c5_gf = Dune::Functions::makeDiscreteGlobalBasisFunction(subspaceBasis(basis, _5), c_backend); Dune::SubsamplingVTKWriter vtkWriter(gridView, Dune::refinementLevels(5)); vtkWriter.addVertexData(c0_gf, Dune::VTK::FieldInfo("c0", Dune::VTK::FieldInfo::Type::scalar, 1)); vtkWriter.addVertexData(c1_gf, Dune::VTK::FieldInfo("c1", Dune::VTK::FieldInfo::Type::scalar, 1)); vtkWriter.addVertexData(c2_gf, Dune::VTK::FieldInfo("c2", Dune::VTK::FieldInfo::Type::scalar, 1)); vtkWriter.addVertexData(c3_gf, Dune::VTK::FieldInfo("c3", Dune::VTK::FieldInfo::Type::scalar, 1)); vtkWriter.addVertexData(c4_gf, Dune::VTK::FieldInfo("c4", Dune::VTK::FieldInfo::Type::scalar, 1)); vtkWriter.addVertexData(c5_gf, Dune::VTK::FieldInfo("c5", Dune::VTK::FieldInfo::Type::scalar, 1)); vtkWriter.addVertexData(bubble_gf, Dune::VTK::FieldInfo("bubble", Dune::VTK::FieldInfo::Type::scalar, 1)); vtkWriter.write("restrictedprebasistest"); return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/subspacebasistest.cc000066400000000000000000000067121513634022200313160ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; auto grid2d = [](){ const int dim = 2; using Grid = Dune::UGGrid; Dune::GridFactory factory; factory.insertVertex({0,0}); factory.insertVertex({0,1}); factory.insertVertex({1,0}); factory.insertVertex({1,1}); factory.insertElement(Dune::GeometryTypes::simplex(2), {0,1,2}); factory.insertElement(Dune::GeometryTypes::simplex(2), {1,2,3}); auto grid = factory.createGrid(); grid->globalRefine(2); return grid; }(); auto sameSubspace = [](const auto& sb1, const auto& sb2) { return (((void*)&sb1.rootBasis())==((void*)&sb2.rootBasis())) and (sb1.prefixPath()==sb2.prefixPath()); }; using namespace Dune; using namespace Dune::Functions; using namespace Dune::Functions::BasisFactory; using namespace Dune::Indices; { auto basis = makeBasis(grid2d->leafGridView(), power<3>(lagrange<3>(), blockedInterleaved())); auto tp = Dune::TypeTree::treePath(_0); auto sb0_a = subspaceBasis(basis, tp); auto sb0_b = subspaceBasis(basis, _0); test.check(sameSubspace(sb0_a, sb0_b)) << "SubspaceBasis created from treepath and treepath indices are different"; } { auto basis = makeBasis(grid2d->leafGridView(), composite(power<3>(lagrange<3>(), blockedInterleaved()), lagrange<1>(), blockedLexicographic())); auto sb0 = subspaceBasis(basis, _0); auto sb01_a = subspaceBasis(sb0, 1); auto sb01_b = subspaceBasis(basis, _0, 1); test.check(sameSubspace(sb01_a, sb01_b)) << "subspaceBasis(subspaceBasis(b,tp1),tp2) not equals subspaceBasis(b,(tp1,tp2))"; } { auto basis = makeBasis(grid2d->leafGridView(), composite(power<3>(lagrange<3>(), blockedInterleaved()), lagrange<1>(), blockedLexicographic())); auto sb0 = SubspaceBasis(basis, Dune::TypeTree::treePath(_0)); auto sb01_a = SubspaceBasis(sb0, Dune::TypeTree::treePath(1)); auto sb01_b = SubspaceBasis(basis, Dune::TypeTree::treePath(_0, 1)); test.check(sameSubspace(sb01_a, sb01_b)) << "SubspaceBasis(SubspaceBasis(b,tp1),tp2) not equals SubspaceBasis(b,(tp1,tp2))"; } { auto basis = makeBasis(grid2d->leafGridView(), composite(power<3>(lagrange<3>(), blockedInterleaved()), lagrange<1>(), blockedLexicographic())); auto sb0 = SubspaceBasis(basis, Dune::TypeTree::treePath(_0)); auto sb01_a = SubspaceBasis(sb0, Dune::TypeTree::treePath(1)); auto sb01_b = subspaceBasis(basis, _0, 1); test.check(sameSubspace(sb01_a, sb01_b)) << "SubspaceBasis(SubspaceBasis(b,tp1),tp2) not equals subspaceBasis(b,(tp1,tp2))"; } return test.exit(); } catch (Dune::Exception& e) { std::cout << e.what() << std::endl; } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/taylorhoodbasistest.cc000066400000000000000000000114121513634022200316660ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; // Generate grid for testing const int dim = 2; typedef YaspGrid GridType; FieldVector l(1); std::array elements = {{10, 10}}; GridType grid(l,elements); typedef GridType::LeafGridView GridView; typedef TaylorHoodBasis Basis; const GridView& gridView = grid.leafGridView(); Basis feBasis(gridView); test.subTest(checkBasis(feBasis, EnableContinuityCheck())); typedef Basis::MultiIndex MultiIndex; // Sample the function f(x,y) = x on the grid vertices // If we use that as the coefficients of a finite element function, // we know its integral and can check whether quadrature returns // the correct result std::array,dim> x; for (unsigned int i = 0; i(); it != gridView.end(); ++it) x[1][gridView.indexSet().index(*it)] = it->geometry().corner(0)[0]; // Objects required in the local context auto localView = feBasis.localView(); std::vector coefficients(localView.maxSize()); // Loop over elements and integrate over the function double integral = 0; for (auto it = gridView.begin<0>(); it != gridView.end<0>(); ++it) { localView.bind(*it); // paranoia checks assert(&(localView.globalBasis()) == &(feBasis)); // copy data from global vector coefficients.resize(localView.size()); for (size_t i=0; i& quad = QuadratureRules::rule(it->type(), 1); // Loop over all quadrature points for ( size_t pt=0; pt < quad.size(); pt++ ) { // Position of the current quadrature point in the reference element const FieldVector& quadPos = quad[pt].position(); // The multiplicative factor in the integral transformation formula const double integrationElement = it->geometry().integrationElement(quadPos); // Evaluate all shape function values at this point std::vector > shapeFunctionValues; localFiniteElement.localBasis().evaluateFunction(quadPos, shapeFunctionValues); // Actually compute the vector entries for (size_t i=0; i basis(gridView); test.subTest(checkBasis(basis, EnableContinuityCheck())); } // check RaviartThomasBasis created using basis builder mechanism { using namespace Functions::BasisFactory; auto basis = makeBasis(grid.leafGridView(), taylorHood()); test.subTest(checkBasis(basis, EnableContinuityCheck())); } return test.exit(); } catch ( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/test/testboundlocalfe.hh000066400000000000000000000340421513634022200311330ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_BOUND_TEST_LOCALFE_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TEST_BOUND_TEST_LOCALFE_HH /** \file \brief Unit tests for GlobalValuedLocalFiniteElement objects that utilize * global(!)derivative degrees of freedom * * \note This file is an adapted copy from * dune-localfunctions/dune/localfunctions/test/test-localfe.hh The tests in this file do not * replace the tests in the file mentioned above, but apply them on the local finite element bound * to a grid element. For the same reason this file is part of dune-functions rather than dune- * localfunctions. * * \note This header is not part of the official Dune API and might be subject to * change. You can use this header to test external finite element implementations, but be warned * that your tests might break with future Dune versions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include // Shapefunctions need to have a derivative template class ShapeFunctionDerivativeAsCallable; // This class wraps one shape function of a local finite element as a callable with derivative() method // that can be fed to the LocalInterpolation::interpolate method, even if this evaluates derivatives. // implements Dune::Functions::DifferentiableFunction template class ShapeFunctionAsCallableWithDerivative { public: using DomainType = typename FE::Traits::LocalBasisType::Traits::DomainType ; using RangeType = typename FE::Traits::LocalBasisType::Traits::RangeType ; using RangeFieldType = typename FE::Traits::LocalBasisType::Traits::RangeFieldType ; static constexpr int dim = FE::Traits::LocalBasisType::Traits::dimDomain; static int constexpr dimRange = FE::Traits::LocalBasisType::Traits::dimRange; ShapeFunctionAsCallableWithDerivative(const FE &fe, int shapeFunction, Element const &element) : fe_(fe) , shapeFunction_(shapeFunction) , e_(element) {} RangeType operator()(DomainType x) const { std::vector yy; fe_.localBasis().evaluateFunction(x, yy); return yy[shapeFunction_]; } friend ShapeFunctionDerivativeAsCallable derivative(ShapeFunctionAsCallableWithDerivative const &t) { return ShapeFunctionDerivativeAsCallable(t); } public: const FE &fe_; int shapeFunction_; Element const &e_; }; // Test whether a Finite Element has an `evaluateHessian(...)` method and exports `HessianType`. template struct hasEvaluateHessian{ static constexpr bool value = false; }; template struct hasEvaluateHessian().evaluateHessian(FE::Traits::LocalBasisType::Traits::DomainType, FE::Traits::LocalBasisType::Traits::HessianType))>> { static constexpr bool value = true; }; // The derivative of one shape function // implements Dune::Functions::DifferentiableFunction template class ShapeFunctionDerivativeAsCallable : public ShapeFunctionAsCallableWithDerivative { using Base = ShapeFunctionAsCallableWithDerivative; typedef typename FE::Traits::LocalBasisType::Traits::JacobianType JacobianType; public: ShapeFunctionDerivativeAsCallable(Base const& base) :Base(base) { } JacobianType operator()(typename Base::DomainType const &x) const { std::vector yy; Base::fe_.localBasis().evaluateJacobian(x, yy); return yy[Base::shapeFunction_] * Base::e_.geometry().jacobianInverse(x); } auto friend derivative(ShapeFunctionDerivativeAsCallable const &t) { if constexpr (hasEvaluateHessian::value){ return [t](typename Base::DomainType const& x){ std::array hessian; std::vector referenceHessians; t.fe_.localBasis().evaluateHessian(x, referenceHessians); const auto geometryJacobianInverse = t.e_.geometry().jacobianInverse(x); for (std::size_t k = 0; k < Base::dimRange; ++k) hessian[k] = transpose(geometryJacobianInverse) * referenceHessians[k] * geometryJacobianInverse; return hessian; }; } else { return [t](typename Base::DomainType const &x) { std::array, Base::dimRange> referenceHessian, hessian; std::vector partials; std::array dir; for (std::size_t i = 0; i < Base::dim; ++i) for (std::size_t j = 0; j < Base::dim; ++j) { dir = {}; dir[i]++; dir[j]++; t.fe_.localBasis().partial(dir, x, partials); for (std::size_t k = 0; k < Base::dimRange; ++k) { hessian[k][i][j] = 0.0; referenceHessian[k][i][j] = partials[t.shapeFunction_][k]; } } const auto geometryJacobianInverse = t.e_.geometry().jacobianInverse(x); for (std::size_t k = 0; k < Base::dimRange; ++k) hessian[k] = transpose(geometryJacobianInverse) * referenceHessian[k] * geometryJacobianInverse; return hessian; }; } } }; // Check whether the degrees of freedom computed by LocalInterpolation // are dual to the shape functions. See Ciarlet, "The Finite Element Method // for Elliptic Problems", 1978, for details. template bool testLocalInterpolation(const FE &fe, Element const &element) { bool ret = true; std::vector::RangeFieldType> coeff; for (size_t i = 0; i < fe.size(); ++i) { ////////////////////////////////////////////////////////////////////////////// // Feed the shape functions to the 'interpolate' method in form in form of a callable. ////////////////////////////////////////////////////////////////////////////// // The i-th shape function as a function that 'interpolate' can deal with ShapeFunctionAsCallableWithDerivative sfAsCallable(fe, i, element); // Compute degrees of freedom for that shape function // We expect the result to be the i-th unit vector fe.localInterpolation().interpolate(sfAsCallable, coeff); // Check size of weight vector if (coeff.size() != fe.localBasis().size()) { std::cout << "Bug in LocalInterpolation for finite element type " << Dune::className(fe) << std::endl; std::cout << " Interpolation produces " << coeff.size() << " degrees of freedom" << std::endl; std::cout << " Basis has size " << fe.localBasis().size() << std::endl; std::cout << std::endl; return false; } // Check if interpolation weights are equal to coefficients for (std::size_t j = 0; j < coeff.size(); ++j) { using std::abs; if (abs(coeff[j] - (i == j)) > TOL) { std::cout << std::setprecision(16); std::cout << "Bug in LocalInterpolation for finite element type " << Dune::className(fe) << std::endl; std::cout << " Degree of freedom " << j << " applied to shape function " << i << " yields value " << coeff[j] << ", not the expected value " << (i == j) << std::endl; std::cout << std::endl; ret = false; } } } return ret; } // Function representing a constant function // implements Dune::Functions::DifferentiableFunction template class Constant { public: Constant(double val) : value(val) {} Range operator()(Domain const &x) const { return value; } friend Constant::Range> derivative(Constant const &t) { return {0.}; } private: Range value; }; // Check whether the space spanned by the shape functions // contains the constant functions template bool testCanRepresentDifferentiableConstants(const FE &fe, unsigned order = 5) { typedef typename FE::Traits::LocalBasisType LB; using RangeType = typename LB::Traits::RangeType; bool success = true; // Construct the constant '1' function Constant constantOne(1.); // Project the constant function onto the FE space std::vector coefficients; fe.localInterpolation().interpolate(constantOne, coefficients); // A set of test points const auto &quad = Dune::QuadratureRules::rule(fe.type(), order); // Loop over all quadrature points for (size_t i = 0; i < quad.size(); i++) { // Get a test point const auto &testPoint = quad[i].position(); // Compute value of the representation of constantOne at the test point std::vector values; fe.localBasis().evaluateFunction(testPoint, values); RangeType sum(0); for (size_t j = 0; j < values.size(); j++) sum += coefficients[j] * values[j]; if ((RangeType(1.0)-sum).two_norm() > TOL) { std::cout << "Finite element type " << Dune::className(fe) << " cannot represent constant functions!" << std::endl; std::cout << " At position: " << testPoint << "," << std::endl; std::cout << " discrete approximation of the '1' function has value " << sum << std::endl; std::cout << std::endl; success = false; } } // Loop over all quadrature points return success; } /** \brief Call tests for given finite element on a grid element * \relates testFE * \param derivativePointSkip This is a small predicate class that allows to skip certain * points when testing the derivative implementations. It exists because some * finite elements are not everywhere differentiable, but we still want to run * the tests for derivatives. Rather than constructing special sets of test * points that avoid the problematic parts of the domain, we simply skip * all test points that happen to be somewhere where the shape functions are * not differentiable. */ template bool testBoundFE(const FE &fe, Element const &element, char disabledTests = DisableNone, const std::function derivativePointSkip = nullptr) { // Order of the quadrature rule used to generate test points unsigned int quadOrder = 2; bool success = true; if (FE::Traits::LocalBasisType::Traits::dimDomain != fe.type().dim()) { std::cout << "Bug in type() for finite element type " << Dune::className(fe) << std::endl; std::cout << " Coordinate dimension is " << FE::Traits::LocalBasisType::Traits::dimDomain << std::endl; std::cout << " but GeometryType is " << fe.type() << " with dimension " << fe.type().dim() << std::endl; success = false; } if (fe.size() != fe.localBasis().size()) { std::cout << "Bug in finite element type " << Dune::className(fe) << std::endl; std::cout << " Size reported by LocalFiniteElement is " << fe.size() << std::endl; std::cout << " but size reported by LocalBasis is " << fe.localBasis().size() << std::endl; success = false; } // Make sure evaluateFunction returns the correct number of values std::vector values; fe.localBasis().evaluateFunction( Dune::ReferenceElements::general( fe.type()) .position(0, 0), values); if (values.size() != fe.size()) { std::cout << "Bug in finite element type " << Dune::className(fe) << std::endl; std::cout << " LocalFiniteElement.size() returns " << fe.size() << "," << std::endl; std::cout << " but LocalBasis::evaluateFunction returns " << values.size() << " values!" << std::endl; success = false; } if (fe.size() != fe.localCoefficients().size()) { std::cout << "Bug in finite element type " << Dune::className(fe) << std::endl; std::cout << " Size reported by LocalFiniteElement is " << fe.size() << std::endl; std::cout << " but size reported by LocalCoefficients is " << fe.localCoefficients().size() << std::endl; success = false; } const auto &lc = fe.localCoefficients(); for (size_t i = 0; i < lc.size(); i++) { const auto &lk = lc.localKey(i); if (lk.codim() > fe.type().dim()) { std::cout << "Bug in finite element type " << Dune::className(fe) << std::endl; std::cout << " Codimension reported by localKey(" << i << ") is " << lk.codim() << std::endl; std::cout << " but geometry is " << fe.type() << " with dimension " << fe.type().dim() << std::endl; success = false; } } if (not(disabledTests & DisableLocalInterpolation)) { success = testLocalInterpolation(fe, element) and success; } if (not (disabledTests & DisableRepresentConstants)) { success = testCanRepresentDifferentiableConstants(fe) and success; } if (not(disabledTests & DisableJacobian)) { success = testJacobian(fe, quadOrder, derivativePointSkip) and success; } else { // make sure diffOrder is 0 success = (diffOrder == 0) and success; } if (not(disabledTests & DisableEvaluate)) { success = TestPartial::test(fe, TOL, jacobianTOL, diffOrder, quadOrder, derivativePointSkip) and success; } return success; } #endif dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/transformedfiniteelementmixin.hh000066400000000000000000000130731513634022200327620ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TRANSFORMEDFINITEELEMENTMIXIN_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TRANSFORMEDFINITEELEMENTMIXIN_HH #include #include #include #include namespace Dune::Functions::Impl { /** * \brief Implementation of a dune-localfunctions LocalBasis that applies a * linear basis transformation * * \tparam FEImplementation The finite element implementation * \tparam ReferenceLocalBasisTraits LocalBasisTraits of the reference local basis */ template class TransformedLocalBasis { public: using Traits = ReferenceLocalBasisTraits; TransformedLocalBasis(FEImplementation const& feImpl) : feImpl_(&feImpl) {} public: /** * \brief Number of shape functions * This need not to be equal to the size of the reference local basis */ auto size() const { return feImpl_->size(); } //! \brief Evaluate all shape functions void evaluateFunction(const typename Traits::DomainType &x, std::vector &out) const { feImpl_->referenceLocalBasis().evaluateFunction(x, rangeBuffer_); out.resize(size()); feImpl_->transform(rangeBuffer_, out); } /** * \brief Evaluate Jacobian of all shape functions * * \param x Point in the reference element where to evaluation the Jacobians * \param[out] out The Jacobians of all shape functions at the point x */ void evaluateJacobian(const typename Traits::DomainType &x, std::vector &out) const { feImpl_->referenceLocalBasis().evaluateJacobian(x, jacobianBuffer_); out.resize(size()); feImpl_->transform(jacobianBuffer_, out); } /** * \brief Evaluate Hessian of all shape functions * * \note SFINA protected: this method is only available, if the wrapped LocalBasis * 1. exports a and * 2. provides a evaluateHessian() method with a corresponding signature * * \param x Point in the reference element where to evaluation the Hessians * \param[out] out The Hessians of all shape functions at the point x */ template, int> = 0> void evaluateHessian(const typename Traits::DomainType &x, std::vector &out) const { feImpl_->referenceLocalBasis().evaluateHessian(x, hessianBuffer_); out.resize(size()); feImpl_->transform(hessianBuffer_, out); } /** * \brief Evaluate partial derivatives of any order of all shape functions * * \param order Order of the partial derivatives, in the classic multi-index notation * \param in Position where to evaluate the derivatives * \param[out] out The desired partial derivatives */ void partial(std::array const &order, const typename Traits::DomainType &x, std::vector &out) const { feImpl_->referenceLocalBasis().partial(order, x, rangeBuffer_); out.resize(size()); feImpl_->transform(rangeBuffer_, out); } //! \brief Polynomial order of the shape functions auto order() const { return feImpl_->referenceLocalBasis().order(); } private: FEImplementation const* feImpl_; mutable std::vector rangeBuffer_; mutable std::vector jacobianBuffer_; mutable std::vector hessianBuffer_; }; /** * \brief A mixin for implementing a LocalFiniteElement using a * linear basis transformation * * \tparam FEImplementation The finite element implementation * \tparam ReferenceLocalBasisTraits LocalBasisTraits of the reference local basis * * The derived class should implement localCoefficients() and localInterpolation() * manually and provide transform() and referenceLocalBasis() as below: * * \code{.cpp} * template * class TransformedFEExample : TransformedFiniteElementMixin, ReferenceLocalBasisTraits> * { * friend class TransformedLocalBasis; * * protected: * * auto const& referenceLocalBasis(); * * template * void transform(InputValues const &inValues, OutputValues &outValues) const; * * public: * auto localCoefficients(); * auto localInterpolation(); * auto size(); * * }; * \endcode */ template class TransformedFiniteElementMixin { public: TransformedFiniteElementMixin() : tlb_(this->asImpl()) {} TransformedFiniteElementMixin(TransformedFiniteElementMixin const& other) : TransformedFiniteElementMixin() {} const FEImplementation& asImpl() const { return *(static_cast(this)); } auto const& localBasis() const { return tlb_; } protected: TransformedLocalBasis tlb_; }; } // end namespace Dune::Functions::Impl #endif dune-functions-2.11.0+dfsg/dune/functions/functionspacebases/transformedindexbasis.hh000066400000000000000000000252171513634022200312210ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TRANSFORMEDINDEXBASIS_HH #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TRANSFORMEDINDEXBASIS_HH #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { namespace Experimental { // ***************************************************************************** // ***************************************************************************** /** * \brief A pre-basis transforming multi-indices * * \ingroup FunctionSpaceBasesImplementations * * \warning This is experimental and may be removed or * modified in a non-compatible way. When using this * functionality take care to follow the dune-functions * development to be aware of possible changes. * * This pre-basis wraps another pre-basis and transforms its global * multi-indices. * * \tparam RPB Raw PreBasis to be wrapped * \tparam T Class of the index transformation */ template class TransformedIndexPreBasis { using Transformation = T; using This = TransformedIndexPreBasis; public: using RawPreBasis = RPB; //! The grid view that the FE basis is defined on using GridView = typename RawPreBasis::GridView; //! Type used for indices and size information using size_type = std::size_t; //! Template mapping root tree path to type of created tree node using Node = typename RawPreBasis::Node; static constexpr size_type maxMultiIndexSize = Transformation::maxIndexSize; static constexpr size_type minMultiIndexSize = Transformation::minIndexSize; static constexpr size_type multiIndexBufferSize = std::max(RawPreBasis::multiIndexBufferSize, maxMultiIndexSize); /** * \brief Constructor for given child pre-basis objects * * The child pre-basis will be stored as copies */ template TransformedIndexPreBasis(RPB_R&& rawPreBasis, T_R&& transformation) : rawPreBasis_(std::forward(rawPreBasis)), transformation_(std::forward(transformation)) {} //! Initialize the global indices void initializeIndices() { rawPreBasis_.initializeIndices(); } //! Obtain the grid view that the basis is defined on const GridView& gridView() const { return rawPreBasis_.gridView(); } //! Update the stored grid view, to be called if the grid has changed void update(const GridView& gv) { rawPreBasis_.update(gv); } /** * \brief Create tree node */ Node makeNode() const { return rawPreBasis_.makeNode(); } //! Same as size(prefix) with empty prefix size_type size() const { return size(Dune::ReservedVector{}); } //! Return number of possible values for next position in multi index template size_type size(const SizePrefix& prefix) const { return transformation_.size(prefix, rawPreBasis_); } //! Return the container descriptor of the transformed pre-basis auto containerDescriptor() const { return transformation_.containerDescriptor(rawPreBasis_); } //! Get the total dimension of the space spanned by this basis size_type dimension() const { return transformation_.dimension(rawPreBasis_); } //! Get the maximal number of DOFs associated to node for any element size_type maxNodeSize() const { return rawPreBasis_.maxNodeSize(); } const RawPreBasis& rawPreBasis() const { return rawPreBasis_; } RawPreBasis& rawPreBasis() { return rawPreBasis_; } template void transformIndex(MultiIndex& multiIndex) const { transformation_.transformIndex(multiIndex, rawPreBasis_); } template It indices(const Node& node, It it) const { rawPreBasis().indices(node, it); for(std::size_t i=0; i TransformedIndexPreBasis(RPB&&, T&&) -> TransformedIndexPreBasis, std::decay_t>; } // end namespace Experimental namespace BasisFactory { namespace Experimental { /** * \brief Create a TransformedIndexPreBasisFactory * * \warning This is experimental and may be removed or * modified in a non-compatible way. When using this * functionality take care to follow the dune-functions * development to be aware of possible changes. * * \param preBasisFactory A PreBasisFactory creating the wrapped pre-basis * \param transformation The transformation object */ template auto transformIndices( RawPreBasisFactory&& preBasisFactory, Transformation&& transformation) { return [ preBasisFactory=std::forward(preBasisFactory), transformation =std::forward(transformation) ](const auto& gridView) { return Dune::Functions::Experimental::TransformedIndexPreBasis(preBasisFactory(gridView), std::move(transformation)); }; } /** * \brief A generic implementation of a transformation * * \warning This is experimental and may be removed or * modified in a non-compatible way. When using this * functionality take care to follow the dune-functions * development to be aware of possible changes. * * This implements the transformation based on two callbacks: One transforms an * existing multi-index inplace, the other implements the size() method of the * pre-basis for a given prefix. Both are passed the wrapped pre-basis as second * argument. * * \tparam IndexTransformation Callback type for transforming multi-indices * \tparam SizeImplementation Callback type for implementation of size(prefix) * \tparam minIS Minimal multi-index size * \tparam maxIS Maximal multi-index size. Notice that this has to large enough to also store the untransformed indices. */ template class GenericIndexingTransformation { public: static constexpr std::size_t minIndexSize = minIS; static constexpr std::size_t maxIndexSize = maxIS; template GenericIndexingTransformation(IT_R&& indexTransformation, SI_R&& sizeImplementation, CD_R&& containerDescriptorImplementation) : indexTransformation_(std::forward(indexTransformation)), sizeImplementation_(std::forward(sizeImplementation)), containerDescriptorImplementation_(std::forward(containerDescriptorImplementation)) {} template void transformIndex(MultiIndex& multiIndex, const PreBasis& preBasis) const { indexTransformation_(multiIndex, preBasis); } template auto size(const Prefix& prefix, const PreBasis& preBasis) const { return sizeImplementation_(prefix, preBasis); } template auto dimension(const PreBasis& preBasis) const { return preBasis.dimension(); } template auto containerDescriptor(const PreBasis& preBasis) const { return containerDescriptorImplementation_(preBasis); } private: IndexTransformation indexTransformation_; SizeImplementation sizeImplementation_; ContainerDescriptorImplementation containerDescriptorImplementation_; }; /** * \brief A generic implementation of a transformation * * \warning This is experimental and may be removed or * modified in a non-compatible way. When using this * functionality take care to follow the dune-functions * development to be aware of possible changes. * * This implements an index-transformation based on two callbacks: One transforms an * existing multi-index inplace, the other implements the size() method of the * pre-basis for a given prefix. Both are passed the wrapped pre-basis as second * argument. * * \tparam IndexTransformation Callback type for transforming multi-indices * \tparam SizeImplementation Callback type for implementation of size(prefix) * \tparam ContainerDescriptorImplementation Callback type for implementation of containerDescriptor() * \tparam minIS Minimal multi-index size * \tparam maxIS Maximal multi-index size. Notice that this has to be large enough to also store the untransformed indices. */ template auto indexTransformation(IndexTransformation&& indexTransformation, SizeImplementation&& sizeImplementation, ContainerDescriptorImplementation&& containerDescriptorImplementation, Dune::index_constant, Dune::index_constant) { return GenericIndexingTransformation< std::decay_t, std::decay_t, std::decay_t, minIndexSize, maxIndexSize>( std::forward(indexTransformation), std::forward(sizeImplementation), std::forward(containerDescriptorImplementation)); } //! Fallback implementation if no container descriptor argument is given. template auto indexTransformation(IndexTransformation&& indexTrafo, SizeImplementation&& sizeImpl, Dune::index_constant minSize, Dune::index_constant maxSize) { return indexTransformation(indexTrafo, sizeImpl, [](auto&&) { return Dune::Functions::ContainerDescriptors::Unknown{}; }, minSize, maxSize); } } // end namespace Experimental } // end namespace BasisFactory } // end namespace Functions } // end namespace Dune #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_TRANSFORMEDINDEXBASIS_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/000077500000000000000000000000001513634022200232725ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/CMakeLists.txt000066400000000000000000000011621513634022200260320ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory("test") install(FILES analyticgridviewfunction.hh composedgridfunction.hh coarsefunctiononfinegridview.hh discreteglobalbasisfunction.hh finefunctiononcoarsegridview.hh gridfunction.hh gridfunction_imp.hh gridviewentityset.hh gridviewfunction.hh localderivativetraits.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/functions/gridfunctions) dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/analyticgridviewfunction.hh000066400000000000000000000205401513634022200307270ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_ANALYTICGRIDVIEWFUNCTION_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_ANALYTICGRIDVIEWFUNCTION_HH #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { namespace Imp { template class DerivativeTraits=DefaultDerivativeTraits> class LocalAnalyticGridViewFunction; template class DerivativeTraits> class LocalAnalyticGridViewFunction { public: using Signature = Range(LocalDomain); using RawSignature = typename SignatureTraits::RawSignature; using DerivativeSignature = typename DerivativeTraits::Range(LocalDomain); using GridView = GV; using EntitySet = GridViewEntitySet; using Element = typename EntitySet::Element; // using Geometry = typename Element::Geometry; using Geometry = std::decay_t; // Use the indirection via derivativeIfImplemented to also support // function types F that do not implement derivative. In this case // the interface type DifferentiableFunction is using a dummy for // the derivative type using DerivativeDummy = DifferentiableFunction; using GlobalRawDerivative = decltype(Imp::derivativeIfImplemented(std::declval())); using LocalDerivative = LocalAnalyticGridViewFunction; //! Create the local-function by storing the mapping `f` by value template = 0> LocalAnalyticGridViewFunction(FT&& f) : f_(std::forward(f)) {} //! Constructor that copies the state of the passed element and geometry template LocalAnalyticGridViewFunction(FT&& f, const Element& element, const std::optional& geometry) : f_(std::forward(f)), element_(element), geometry_(geometry) {} /** * \brief Bind the local-function to an `element`. * * Stores a copy of the `element` and of its geometry in the class. * * \b Ensures: * - Local-function is bound to the `element`. **/ void bind(const Element& element) { element_ = element; geometry_.emplace(element_.geometry()); } //! Release the bound geometry void unbind() { geometry_.reset(); } /** \brief Return if the local function is bound to a grid element */ bool bound() const { return static_cast(geometry_); } /** * \brief Evaluate the stored function `f` in global coordinates mapped by the geometry. * * \b Expects: * - The local-function is bound to an element in bind(). * * \param x Local coordinate in the bound element * \return Evaluation of `f` in global coordinates `x` **/ Range operator()(const LocalDomain& x) const { assert(!!geometry_); return (*f_)(geometry_->global(x)); } //! Return the bound element const Element& localContext() const { assert(!!geometry_); return element_; } /** * \brief Return a local-function representing the derivative. * * This function computes the derivative of the wrapped function `f`, if * available, otherwise use a dummy representation. If the local-function * was bound to an element so is its derivative. Otherwise it must be bound * before it can be evaluated. **/ friend LocalDerivative derivative(const LocalAnalyticGridViewFunction& t) { return LocalDerivative(Imp::derivativeIfImplemented(*t.f_), t.element_, t.geometry_); } private: // Wrap the function into CopyableOptional to make it copy-assignable CopyableOptional f_; Element element_; std::optional geometry_ = std::nullopt; }; } // end namespace Imp template class DerivativeTraits=DefaultDerivativeTraits> class AnalyticGridViewFunction; /** * \brief Class wrapping any differentiable function as grid function. * * \ingroup FunctionImplementations */ template class DerivativeTraits> class AnalyticGridViewFunction { public: using Signature = Range(Domain); using RawSignature = typename SignatureTraits::RawSignature; using DerivativeSignature = typename DerivativeTraits::Range(Domain); using GridView = GV; using EntitySet = GridViewEntitySet; using Element = typename EntitySet::Element; using Geometry = typename Element::Geometry; // Use the indirection via derivativeIfImplemented to also support // function types F that do not implement derivative. In this case // the interface type DifferentiableFunction is used a dummy for // the derivative type using DerivativeDummy = DifferentiableFunction; using GlobalRawDerivative = decltype(Imp::derivativeIfImplemented(std::declval())); using Derivative = AnalyticGridViewFunction; using LocalDomain = typename EntitySet::LocalCoordinate; using LocalFunction = typename Imp::LocalAnalyticGridViewFunction::template Traits>; //! Create the grid-function by wrapping a function `f` and create a GridViewEntitySet. template AnalyticGridViewFunction(FT&& f, const GridView& gridView) : f_(std::forward(f)), entitySet_(gridView) {} //! Evaluate the wrapped function `f` directly in global coordinates `x`. Range operator()(const Domain& x) const { return (*f_)(x); } //! Create a derivative grid-function by wrapping the derivative of `f`. friend Derivative derivative(const AnalyticGridViewFunction& t) { return Derivative(Imp::derivativeIfImplemented(*t.f_), t.entitySet_.gridView()); } //! Construct the associated local-function. friend LocalFunction localFunction(const AnalyticGridViewFunction& t) { return LocalFunction(*t.f_); } //! Return the set of entities this local-function can be bound to. const EntitySet& entitySet() const { return entitySet_; } private: // Wrap the function into CopyableOptional to make it copy-assignable CopyableOptional f_; EntitySet entitySet_; }; // deduction guides template::Geometry::GlobalCoordinate, class Range = std::invoke_result_t> AnalyticGridViewFunction(const F&, const GridView&) -> AnalyticGridViewFunction; /** * \brief Create an AnalyticGridViewFunction from a function and a grid view. * * \ingroup FunctionImplementations * * The returned function supports `localFunction()` and stores a copy of the * original function. * * \param f A function object supporting evaluation with global coordinates * of the passed `gridView`. * \param gridView The GridView the function should act on. * * \returns A function that models the GridFunction interface. * * \relatesalso AnalyticGridViewFunction */ template auto makeAnalyticGridViewFunction(F&& f, const GridView& gridView) { using Domain = typename GridView::template Codim<0>::Geometry::GlobalCoordinate; using Range = std::invoke_result_t; using FRaw = std::decay_t; return AnalyticGridViewFunction(std::forward(f), gridView); } }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_ANALYTICGRIDVIEWFUNCTION_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/coarsefunctiononfinegridview.hh000066400000000000000000000156121513634022200316020ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_COARSEFUNCTIONONFINEGRIDVIEW_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_COARSEFUNCTIONONFINEGRIDVIEW_HH #include #include #include #include #include #include #include #include namespace Dune::Functions { /** * \brief A wrapper representing a coarse grid function on a fine gridview * * \ingroup FunctionImplementations * * \tparam GridFunction Type of the wrapped grid function * \tparam GV Type of the target grid view this function should act on * * This wraps a grid function such that it can be used as a `GridViewFunction` * on a user-provided `GridView` under the following assumptions: * 1. The grid function's entity set and the `GridView` belong to the same grid. * 2. The entity set is coarser than the `GridView` in the sense that any * element from the `GridView` has an ancestor in the entity set. */ template class DerivativeTraits=Dune::Functions::DefaultDerivativeTraits> class CoarseFunctionOnFineGridView { using RawGridFunction = Dune::ResolveRef_t; auto&& rawFunction() const { return Dune::resolveRef(function_); } public: using GridView = GV; using EntitySet = Dune::Functions::GridViewEntitySet; using Element = typename EntitySet::Element; using Domain = typename EntitySet::GlobalCoordinate; using LocalDomain = typename EntitySet::LocalCoordinate; using Range = std::decay_t()(std::declval()))>; private: using CoarseEntitySet = std::decay_t().entitySet())>; using GeometryInAncestor = Dune::Functions::GeometryInAncestor; using Traits = Dune::Functions::Imp::GridFunctionTraits; class CoarseLocalFunctionOnFineGridView { using Traits = typename CoarseFunctionOnFineGridView::Traits::LocalFunctionTraits; public: using Derivative = decltype(localFunction(derivative(std::declval()))); using RawLocalFunction = std::decay_t()))>; /** * \brief Construct the LocalFunction * * The LocalFunction is created from the global CoarseFunctionOnFineGridView. */ CoarseLocalFunctionOnFineGridView(RawLocalFunction&& localFunction, const CoarseEntitySet& coarseEntitySet) : element_() , localFunction_(localFunction) , coarseEntitySet_(coarseEntitySet) , geometryInAncestor_() {} /** * \brief Construct the LocalFunction * * The LocalFunction is created from the global CoarseFunctionOnFineGridView. */ CoarseLocalFunctionOnFineGridView( RawLocalFunction&& localFunction, const CoarseEntitySet& coarseEntitySet, const GeometryInAncestor& geometryInAncestor, const std::optional& element ) : element_(element) , localFunction_(localFunction) , coarseEntitySet_(coarseEntitySet) , geometryInAncestor_(geometryInAncestor, *element_) {} //! Bind to an element from the GridView void bind(const Element& element) { element_ = element; geometryInAncestor_.bind(*element_, [&](const auto& e) { return not coarseEntitySet_.contains(e); }); localFunction_.bind(geometryInAncestor_.coarseElement()); } //! \brief Unbind void unbind() { element_.reset(); } //! Return if the local function is bound to an element of the GridView bool bound() const { return static_cast(element_); } //! Obtain the grid element this function is bound to const Element& localContext() const { return *element_; } //! Obtain local derivative of this function friend auto derivative(const CoarseLocalFunctionOnFineGridView& f) { if constexpr(requires{ derivative(f.localFunction_); }) return Derivative(derivative(f.localFunction_), f.coarseEntitySet_, f.geometryInAncestor_, f.element_); else return typename Traits::DerivativeInterface{}; } //! Evaluate function in local coordinates Range operator()(LocalDomain x) const { return localFunction_(geometryInAncestor_.global(x)); } private: std::optional element_; RawLocalFunction localFunction_; const CoarseEntitySet& coarseEntitySet_; GeometryInAncestor geometryInAncestor_; }; public: using LocalFunction = CoarseLocalFunctionOnFineGridView; /** * \brief Create CoarseFunctionOnFineGridView from GridFunction and GridView * * \param function The GridFunction that should be represented on gridView * \param gridView The GridFunction should be represented on this gridView */ CoarseFunctionOnFineGridView(const GridFunction& function, const GridView& gridView) : function_(function) , entitySet_(gridView) {} /** * \brief Create CoarseFunctionOnFineGridView from GridFunction and GridView * * \param function The GridFunction that should be represented on gridView * \param gridView The GridFunction should be represented on this gridView */ CoarseFunctionOnFineGridView(GridFunction&& function, const GridView& gridView) : function_(std::move(function)) , entitySet_(gridView) {} //! Evaluate function in global coordinates Range operator()(const Domain& x) const { return function_(x); } //! Obtain global derivative of this function friend auto derivative(const CoarseFunctionOnFineGridView& f) { if constexpr(requires{ derivative(f.rawFunction()); }) { using RawDerivative = std::decay_t; return CoarseFunctionOnFineGridView(derivative(f.rawFunction()), f.entitySet_.gridView()); } else return typename Traits::DerivativeInterface{}; } //! Create a LocalFunction for evaluation in local coordinates friend LocalFunction localFunction(const CoarseFunctionOnFineGridView& f) { return LocalFunction(localFunction(f.rawFunction()), f.rawFunction().entitySet()); } //! Return the EntitySet associated to this GridViewFunction const EntitySet& entitySet() const { return entitySet_; } protected: GridFunction function_; EntitySet entitySet_; }; } // namespace Dune::Functions #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_COARSEFUNCTIONONFINEGRIDVIEW_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/composedgridfunction.hh000066400000000000000000000204131513634022200300400ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_COMPOSEDGRIDFUNCTION_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_COMPOSEDGRIDFUNCTION_HH #include #include #include #include #include #include #include namespace Dune { namespace Functions { /** * \brief Composition of grid functions with another function. * * \ingroup FunctionImplementations * * For given inner grid functions `g0, ..., gn` and an * outer function `f` this creates a grid function * representing `f(g0(x), ..., gn(x))`. The only assumption * made, is that the range types of the inner functions * can be passed to the outer ones, and that all grid * functions are defined on the same EntitySet. * * Notice that all functions are captured by value. * To store references you can pass `std::ref()`. * * \tparam OF Type of outer function. std::reference_wrapper is supported. * \tparam IF Types of inner outer functions. `std::reference_wrapper` is supported. */ template class ComposedGridFunction { using InnerFunctions = std::tuple; using InnerLocalFunctions = std::tuple())))...>; template using InnerFunction = std::decay_t>>; using OuterFunction = OF; public: using EntitySet = typename InnerFunction<0>::EntitySet; using Element = typename EntitySet::Element; using Domain = typename EntitySet::GlobalCoordinate; using LocalDomain = typename EntitySet::LocalCoordinate; using Range = decltype(std::declval()(localFunction(std::declval())(std::declval())...)); private: using Traits = Imp::GridFunctionTraits; class LocalFunction { public: /** * \brief Construct the local-function. * * The local-functions is created from the outer-function of the * grid-function and the local-functions of the stored inner-functions. **/ LocalFunction(const ComposedGridFunction& globalFunction) : globalFunction_(globalFunction), innerLocalFunctions_(globalFunction.innerLocalFunctions()) {} /** * \brief Bind the inner local-functions to an `element`. * * \b Expects: * - The `element` is in the entitySet of all inner local-functions. * * \b Ensures: * - All inner local-functions are bound to the same `element`. **/ void bind(const Element& element) { std::apply([&](auto&... innerFunction) { (innerFunction.bind(element),...); }, innerLocalFunctions_); } //! \brief Unbind the inner local-functions. void unbind() { std::apply([&](auto&... innerFunction) { (innerFunction.unbind(),...); }, innerLocalFunctions_); } /** \brief Return if the local function is bound to a grid element */ bool bound() const { return std::apply([](const auto&... innerFunction) { return (innerFunction.bound() && ...); }, innerLocalFunctions_); } /** * \brief Evaluation of the composed local-function. * * Returns the outer-function evaluated with the evaluated inner * local-functions. The functions are evaluated in the local coordinates `x`. * * \b Expects: * - All inner local-functions are bound to the same element. **/ Range operator()(const LocalDomain& x) const { return std::apply([&](const auto&... innerFunction) { return globalFunction_.outerFunction_(innerFunction(x)...); }, innerLocalFunctions_); } /** * \brief Return the local context all inner local-functions are bound to. * * Since all inner local-functions are bound to the same element, return * the local context of either of the inner local-functions. * * \b Requirements: * - Number of inner local-functions > 0 **/ const Element& localContext() const { return std::get<0>(innerLocalFunctions_).localContext(); } //! Not implemented friend typename Traits::LocalFunctionTraits::DerivativeInterface derivative(const LocalFunction& t) { DUNE_THROW(NotImplemented,"not implemented"); } private: const ComposedGridFunction& globalFunction_; InnerLocalFunctions innerLocalFunctions_; }; public: /** * \brief Create ComposedGridFunction. * * Outer and inner functions will be captured by value. * To store references you can pass `std::ref()`. * * \param outerFunction The outer function to be composed with the grid functions. * \param innerFunctions The inner grid functions */ template = 0, std::enable_if_t<(sizeof...(IFT) > 0), int> = 0> ComposedGridFunction(OFT&& outerFunction, IFT&&... innerFunctions) : outerFunction_(std::forward(outerFunction)), innerFunctions_(std::forward(innerFunctions)...) {} //! Evaluation of the composed grid function in coordinates `x` Range operator()(const Domain& x) const { return std::apply([&](const auto&... innerFunction) { return outerFunction_(innerFunction(x)...); }, innerFunctions_); } //! Not implemented. friend typename Traits::DerivativeInterface derivative(const ComposedGridFunction& t) { DUNE_THROW(NotImplemented,"not implemented"); } /** * \brief Create a local-function of this composed grid-function. * * The LocalFunction is defined by composition of the outer-function with * the corresponding local-functions of the inner-functions. **/ friend LocalFunction localFunction(const ComposedGridFunction& cgf) { return LocalFunction(cgf); } /** * \brief Return the EntitySet associated to this composed grid-function. * * It is implicitly assumed that all inner-functions can be bound to the * same set of entities. Thus, the EntitySet is either of the EntitySets * of the inner-function, e.g., the one from the first inner-function. * * \b Requirements: * - Number of inner-functions > 0 **/ const EntitySet& entitySet() const { return resolveRef(std::get<0>(innerFunctions_)).entitySet(); } protected: InnerLocalFunctions innerLocalFunctions() const { return std::apply([&](const auto&... innerFunction) { return std::make_tuple(localFunction(resolveRef(innerFunction))...); }, innerFunctions_); } OuterFunction outerFunction_; InnerFunctions innerFunctions_; }; // deduction guides template ComposedGridFunction(const OF&, const IF&...) -> ComposedGridFunction; /** * \brief Create a ComposedGridFunction that composes grid-functions with another function. * * \ingroup FunctionImplementations * * For given inner grid-functions `g0, ..., gn` and an * outer-function `f` this creates a grid-function * representing `f(g0(x), ..., gn(x))`. The only assumption * made, is that the range types of the inner-functions * can be passed to the outer ones, and that all grid-functions * are defined on the same `EntitySet`. * * Notice that all functions are captured by value. * To store references you can pass `std::ref()`. * * \param outerFunction The outer-function to be composed with the grid-functions. * \param innerFunction The inner grid-functions * * \returns A grid-function defined on the same `EntitySet` as the input-functions. * * \relatesalso ComposedGridFunction */ template auto makeComposedGridFunction(OF&& outerFunction, IF&&... innerFunction) { using ComposedGridFunctionType = ComposedGridFunction, std::decay_t...>; return ComposedGridFunctionType(std::forward(outerFunction), std::forward(innerFunction)...); } }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_COMPOSEDGRIDFUNCTION_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/discreteglobalbasisfunction.hh000066400000000000000000000566351513634022200314050ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_DISCRETEGLOBALBASISFUNCTIONS_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_DISCRETEGLOBALBASISFUNCTIONS_HH #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { namespace ImplDoc { template class DiscreteGlobalBasisFunctionBase { public: using Basis = B; using Vector = V; // In order to make the cache work for proxy-references // we have to use AutonomousValue instead of std::decay_t using Coefficient = Dune::AutonomousValue()[std::declval()])>; using GridView = typename Basis::GridView; using EntitySet = GridViewEntitySet; using Tree = typename Basis::LocalView::Tree; using NodeToRangeEntry = NTRE; using Domain = typename EntitySet::GlobalCoordinate; using LocalDomain = typename EntitySet::LocalCoordinate; using Element = typename EntitySet::Element; protected: // This collects all data that is shared by all related // global and local functions. This way we don't need to // keep track of it individually. struct Data { EntitySet entitySet; std::shared_ptr basis; std::shared_ptr coefficients; std::shared_ptr nodeToRangeEntry; }; public: class LocalFunctionBase { using LocalView = typename Basis::LocalView; using size_type = typename Tree::size_type; public: using Domain = LocalDomain; using Element = typename EntitySet::Element; protected: LocalFunctionBase(const std::shared_ptr& data) : data_(data) , localView_(data_->basis->localView()) { localDoFs_.reserve(localView_.maxSize()); } /** * \brief Copy-construct the local-function. * * This copy-constructor copies the cached local DOFs only * if the `other` local-function is bound to an element. **/ LocalFunctionBase(const LocalFunctionBase& other) : data_(other.data_) , localView_(other.localView_) { localDoFs_.reserve(localView_.maxSize()); if (bound()) localDoFs_ = other.localDoFs_; } /** * \brief Copy-assignment of the local-function. * * Assign all members from `other` to `this`, except the * local DOFs. Those are copied only if the `other` * local-function is bound to an element. **/ LocalFunctionBase& operator=(const LocalFunctionBase& other) { data_ = other.data_; localView_ = other.localView_; if (bound()) localDoFs_ = other.localDoFs_; return *this; } public: /** * \brief Bind LocalFunction to grid element. * * You must call this method before `operator()` * and after changes to the coefficient vector. */ void bind(const Element& element) { localView_.bind(element); // Use cache of full local view size. For a subspace basis, // this may be larger than the number of local DOFs in the // tree. In this case only cache entries associated to local // DOFs in the subspace are filled. Cache entries associated // to local DOFs which are not contained in the subspace will // not be touched. // // Alternatively one could use a cache that exactly fits // the size of the tree. However, this would require to // subtract an offset from localIndex(i) on each cache // access in operator(). localDoFs_.resize(localView_.size()); const auto& dofs = *data_->coefficients; for (size_type i = 0; i < localView_.tree().size(); ++i) { // For a subspace basis the index-within-tree i // is not the same as the localIndex within the // full local view. size_t localIndex = localView_.tree().localIndex(i); localDoFs_[localIndex] = dofs[localView_.index(localIndex)]; } } //! Unbind the local-function. void unbind() { localView_.unbind(); } //! Check if LocalFunction is already bound to an element. bool bound() const { return localView_.bound(); } //! Return the element the local-function is bound to. const Element& localContext() const { return localView_.element(); } protected: template void assignWith(To& to, const From& from) const { auto from_flat = flatVectorView(from); auto to_flat = flatVectorView(to); assert(from_flat.size() == to_flat.size()); for (size_type i = 0; i < to_flat.size(); ++i) to_flat[i] = from_flat[i]; } template decltype(auto) nodeToRangeEntry(const Node& node, const TreePath& treePath, Range& y) const { return (*data_->nodeToRangeEntry)(node, treePath, y); } std::shared_ptr data_; LocalView localView_; std::vector localDoFs_; }; protected: DiscreteGlobalBasisFunctionBase(const std::shared_ptr& data) : data_(data) { /* Nothing. */ } public: //! Return a const reference to the stored basis. const Basis& basis() const { return *data_->basis; } //! Return the coefficients of this discrete function by reference. const Vector& dofs() const { return *data_->coefficients; } //! Return the stored node-to-range map. const NodeToRangeEntry& nodeToRangeEntry() const { return *data_->nodeToRangeEntry; } //! Get associated set of entities the local-function can be bound to. const EntitySet& entitySet() const { return data_->entitySet; } protected: std::shared_ptr data_; }; } // namespace ImplDoc template class DiscreteGlobalBasisFunctionDerivative; /** * \brief A grid function induced by a global basis and a coefficient vector. * * \ingroup FunctionImplementations * * This implements the grid function interface by combining a given global * basis and a coefficient vector. * * This class supports mapping of subtrees to multi-component ranges, * vector-valued shape functions, and implicit product spaces given * by vector-valued coefficients. The mapping of these to the range * type is done via the following multistage procedure: * * 1.Each leaf node in the local ansatz subtree is associated to an * entry `RE` of the range-type via the given node-to-range-entry-map. * Based on this mapping each node is processed independently in the * following way: * * 2.Now let the coefficients type `C` per basis function be `dim_C`-dimensional. * Then we compute the dim(C) linear combinations (one for each coefficient * index) of the shape function values with type `V` independently storing them in * a `std::array`. * * 3.Finally the resulting array of function values is assigned to the * nodal range entry `RE`. Since both types may be different their entries * are mapped to one another via `flatVectorView()`. This will recursive * enumerate the entries of the types in lexicographic order (unless * `flatVectorView` is specialized differently for a certain type). * * As a consequence the nodal range entry is required to have a total * dimension `dim_RE = dim_C * dim_V` and to be compatible with `flatVectorView()`. * * \tparam B Type of global basis * \tparam V Type of coefficient vectors * \tparam NTRE Type of node-to-range-entry-map that associates each leaf node in the local ansatz subtree with an entry in the range type * \tparam R Range type of this function * * \b Requirements: * - Type `R` is default constructible * - default constructed `R` has correct "shape" to hold the components of the range and * to be accessible using the node-to-range-entry map `NTRE`. * - This requirements applies recursively to the derivative ranges. */ template class DiscreteGlobalBasisFunction : public ImplDoc::DiscreteGlobalBasisFunctionBase { using Base = ImplDoc::DiscreteGlobalBasisFunctionBase; using Data = typename Base::Data; public: using Basis = typename Base::Basis; using Vector = typename Base::Vector; using Domain = typename Base::Domain; using Range = R; using Traits = Imp::GridFunctionTraits; private: template using LocalBasisRange = typename Node::FiniteElement::Traits::LocalBasisType::Traits::RangeType; template using NodeData = typename std::vector>; using PerNodeEvaluationBuffer = typename TypeTree::TreeContainer; public: class LocalFunction : public Base::LocalFunctionBase { using LocalBase = typename Base::LocalFunctionBase; using size_type = typename Base::Tree::size_type; using LocalBase::nodeToRangeEntry; public: using GlobalFunction = DiscreteGlobalBasisFunction; using Domain = typename LocalBase::Domain; using Range = GlobalFunction::Range; using Element = typename LocalBase::Element; //! Create a local-function from the associated grid-function LocalFunction(const DiscreteGlobalBasisFunction& globalFunction) : LocalBase(globalFunction.data_) , evaluationBuffer_(this->localView_.tree()) { /* Nothing. */ } /** * \brief Evaluate this local-function in coordinates `x` in the bound element. * * The result of this method is undefined if you did * not call bind() beforehand or changed the coefficient * vector after the last call to bind(). In the latter case * you have to call bind() again in order to make operator() * usable. */ Range operator()(const Domain& x) const { Range y; istlVectorBackend(y) = 0; TypeTree::forEachLeafNode(this->localView_.tree(), [&](auto&& node, auto&& treePath) { if (node.empty()) return; const auto& fe = node.finiteElement(); const auto& localBasis = fe.localBasis(); auto& shapeFunctionValues = evaluationBuffer_[treePath]; localBasis.evaluateFunction(x, shapeFunctionValues); // Compute linear combinations of basis function jacobian. // Non-scalar coefficients of dimension coeffDim are handled by // processing the coeffDim linear combinations independently // and storing them as entries of an array. using Value = LocalBasisRange< std::decay_t >; static constexpr auto coeffDim = decltype(flatVectorView(this->localDoFs_[node.localIndex(0)]).size())::value; auto values = std::array{}; istlVectorBackend(values) = 0; for (size_type i = 0; i < localBasis.size(); ++i) { auto c = flatVectorView(this->localDoFs_[node.localIndex(i)]); for (std::size_t j = 0; j < coeffDim; ++j) values[j].axpy(c[j], shapeFunctionValues[i]); } // Assign computed values to node entry of range. // Types are matched using the lexicographic ordering provided by flatVectorView. LocalBase::assignWith(nodeToRangeEntry(node, treePath, y), values); }); return y; } //! Local function of the derivative friend typename DiscreteGlobalBasisFunctionDerivative::LocalFunction derivative(const LocalFunction& lf) { auto dlf = localFunction(DiscreteGlobalBasisFunctionDerivative(lf.data_)); if (lf.bound()) dlf.bind(lf.localContext()); return dlf; } private: mutable PerNodeEvaluationBuffer evaluationBuffer_; }; //! Create a grid-function, by wrapping the arguments in `std::shared_ptr`. template DiscreteGlobalBasisFunction(B_T && basis, V_T && coefficients, NTRE_T&& nodeToRangeEntry) : Base(std::make_shared(Data{{basis.gridView()}, wrap_or_move(std::forward(basis)), wrap_or_move(std::forward(coefficients)), wrap_or_move(std::forward(nodeToRangeEntry))})) {} //! Create a grid-function, by moving the arguments in `std::shared_ptr`. DiscreteGlobalBasisFunction(std::shared_ptr basis, std::shared_ptr coefficients, std::shared_ptr nodeToRangeEntry) : Base(std::make_shared(Data{{basis->gridView()}, basis, coefficients, nodeToRangeEntry})) {} /** \brief Evaluate at a point given in world coordinates * * \warning This has to find the element that the evaluation point is in. * It is therefore very slow. */ Range operator() (const Domain& x) const { HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); const auto e = search.findEntity(x); auto localThis = localFunction(*this); localThis.bind(e); return localThis(e.geometry().local(x)); } //! Derivative of the `DiscreteGlobalBasisFunction` friend DiscreteGlobalBasisFunctionDerivative derivative(const DiscreteGlobalBasisFunction& f) { return DiscreteGlobalBasisFunctionDerivative(f.data_); } /** * \brief Construct local function from a DiscreteGlobalBasisFunction. * * The obtained local function satisfies the concept * `Dune::Functions::Concept::LocalFunction`. It must be bound * to an entity from the entity set of the DiscreteGlobalBasisFunction * before it can be used. */ friend LocalFunction localFunction(const DiscreteGlobalBasisFunction& t) { return LocalFunction(t); } }; /** * \brief Generate a DiscreteGlobalBasisFunction. * * \ingroup FunctionImplementations * * Create a new DiscreteGlobalBasisFunction by wrapping the vector in a * VectorBackend that allows the hierarchic resize and multi-index access in * the DiscreteGlobalBasisFunction, if the vector does not yet fulfill the * \ref Concept::ConstVectorBackend concept. * * \tparam R The range type this grid-function should represent when seen as * a mapping `R(Domain)` with `Domain` the global coordinates of the * associated GridView. This must be compatible with the basis and * coefficients. See the documentation of \ref DiscreteGlobalBasisFunction * for more details. The type `R` must be default constructible and * by this default construction provides a well resized container to be * filled in the evaluation method of the grid function. * * \param basis The global basis or subspace basis associated with this * grid-function * \param vector The coefficient vector to use in combination with the `basis`. * * \relatesalso DiscreteGlobalBasisFunction **/ template auto makeDiscreteGlobalBasisFunction(B&& basis, V&& vector) { using Basis = std::decay_t; using NTREM = HierarchicNodeToRangeMap; // Small helper functions to wrap vectors using istlVectorBackend // if they do not already satisfy the VectorBackend interface. auto toConstVectorBackend = [&](auto&& v) -> decltype(auto) { if constexpr (models, decltype(v)>()) { return std::forward(v); } else { return istlVectorBackend(v); } }; using Vector = std::decay_t(vector)))>; return DiscreteGlobalBasisFunction( std::forward(basis), toConstVectorBackend(std::forward(vector)), HierarchicNodeToRangeMap()); } /** * \brief Derivative of a `DiscreteGlobalBasisFunction` * * Function returning the derivative of the given `DiscreteGlobalBasisFunction` * with respect to global coordinates. * * The function handles the mapping of coefficient blocks and basis function values * to range entries analogously to the `DiscreteGlobalBasisFunction`. This mapping * is implemented with the same algorithm but with values replace by jacobian. * * \ingroup FunctionImplementations * * \tparam DGBF instance of the `DiscreteGlobalBasisFunction` this is a derivative of */ template class DiscreteGlobalBasisFunctionDerivative : public ImplDoc::DiscreteGlobalBasisFunctionBase { using Base = ImplDoc::DiscreteGlobalBasisFunctionBase; using Data = typename Base::Data; public: using DiscreteGlobalBasisFunction = DGBF; using Basis = typename Base::Basis; using Vector = typename Base::Vector; using Domain = typename Base::Domain; using Range = typename SignatureTraits::Range; using Traits = Imp::GridFunctionTraits; private: template using LocalBasisRange = typename Node::FiniteElement::Traits::LocalBasisType::Traits::JacobianType; template using NodeData = typename std::vector< LocalBasisRange >; using PerNodeEvaluationBuffer = typename TypeTree::TreeContainer; public: /** * \brief local function evaluating the derivative in reference coordinates * * Note that the function returns the derivative with respect to global * coordinates even when the point is given in reference coordinates on * an element. */ class LocalFunction : public Base::LocalFunctionBase { using LocalBase = typename Base::LocalFunctionBase; using size_type = typename Base::Tree::size_type; using LocalBase::nodeToRangeEntry; public: using GlobalFunction = DiscreteGlobalBasisFunctionDerivative; using Domain = typename LocalBase::Domain; using Range = GlobalFunction::Range; using Element = typename LocalBase::Element; //! Create a local function from the associated grid function LocalFunction(const GlobalFunction& globalFunction) : LocalBase(globalFunction.data_) , evaluationBuffer_(this->localView_.tree()) { /* Nothing. */ } /** * \brief Bind LocalFunction to grid element. * * You must call this method before `operator()` * and after changes to the coefficient vector. */ void bind(const Element& element) { LocalBase::bind(element); geometry_.emplace(element.geometry()); } //! Unbind the local-function. void unbind() { geometry_.reset(); LocalBase::unbind(); } /** * \brief Evaluate this local-function in coordinates `x` in the bound element. * * The result of this method is undefined if you did * not call bind() beforehand or changed the coefficient * vector after the last call to bind(). In the latter case * you have to call bind() again in order to make operator() * usable. * * Note that the function returns the derivative with respect to global * coordinates even when the point is given in reference coordinates on * an element. */ Range operator()(const Domain& x) const { Range y; istlVectorBackend(y) = 0; const auto& jacobianInverse = geometry_->jacobianInverse(x); TypeTree::forEachLeafNode(this->localView_.tree(), [&](auto&& node, auto&& treePath) { if (node.empty()) return; const auto& fe = node.finiteElement(); const auto& localBasis = fe.localBasis(); auto& shapeFunctionJacobians = evaluationBuffer_[treePath]; localBasis.evaluateJacobian(x, shapeFunctionJacobians); // Compute linear combinations of basis function jacobian. // Non-scalar coefficients of dimension coeffDim are handled by // processing the coeffDim linear combinations independently // and storing them as entries of an array. using RefJacobian = LocalBasisRange< std::decay_t >; static constexpr auto coeffDim = decltype(flatVectorView(this->localDoFs_[node.localIndex(0)]).size())::value; auto refJacobians = std::array{}; istlVectorBackend(refJacobians) = 0; for (size_type i = 0; i < localBasis.size(); ++i) { auto c = flatVectorView(this->localDoFs_[node.localIndex(i)]); for (std::size_t j = 0; j < coeffDim; ++j) refJacobians[j].axpy(c[j], shapeFunctionJacobians[i]); } // Transform Jacobians form local to global coordinates. using Jacobian = decltype(refJacobians[0] * jacobianInverse); auto jacobians = std::array{}; std::transform( refJacobians.begin(), refJacobians.end(), jacobians.begin(), [&](const auto& refJacobian) { return refJacobian * jacobianInverse; }); // Assign computed Jacobians to node entry of range. // Types are matched using the lexicographic ordering provided by flatVectorView. LocalBase::assignWith(nodeToRangeEntry(node, treePath, y), jacobians); }); return y; } //! Not implemented friend typename Traits::LocalFunctionTraits::DerivativeInterface derivative(const LocalFunction&) { DUNE_THROW(NotImplemented, "derivative of derivative is not implemented"); } private: mutable PerNodeEvaluationBuffer evaluationBuffer_; std::optional geometry_; }; /** * \brief create object from `DiscreateGlobalBasisFunction` data * * Please call `derivative(discreteGlobalBasisFunction)` to create an instance * of this class. */ DiscreteGlobalBasisFunctionDerivative(const std::shared_ptr& data) : Base(data) { /* Nothing. */ } /** \brief Evaluate the discrete grid-function derivative in global coordinates * * \warning This has to find the element that the evaluation point is in. * It is therefore very slow. */ Range operator()(const Domain& x) const { HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); const auto e = search.findEntity(x); auto localThis = localFunction(*this); localThis.bind(e); return localThis(e.geometry().local(x)); } friend typename Traits::DerivativeInterface derivative(const DiscreteGlobalBasisFunctionDerivative& f) { DUNE_THROW(NotImplemented, "derivative of derivative is not implemented"); } //! Construct local function from a `DiscreteGlobalBasisFunctionDerivative` friend LocalFunction localFunction(const DiscreteGlobalBasisFunctionDerivative& f) { return LocalFunction(f); } }; } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_DISCRETEGLOBALBASISFUNCTIONS_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/facenormalgridfunction.hh000066400000000000000000000124761513634022200303500ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_FACENORMALGRIDFUNCTION_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_FACENORMALGRIDFUNCTION_HH #include #include #include #include #include #include #include #include namespace Dune::Functions { namespace Impl { // Compute closest face to point template auto closestFaceIndex(const ReferenceElement& re, const Coordinate& x) { auto closestFaceIndex = decltype(re.subEntity(0,1,0,1)){}; double closestFaceDistance = std::numeric_limits::max(); for(auto&& faceIndex : Dune::range(re.size(1))) { // For a face unit outer normal consider the orthogonal projection // Px = x + *n into the face. Then the distance to the face // is given by |x-Px| = |||n| = . auto normal = re.integrationOuterNormal(faceIndex); normal /= normal.two_norm(); auto c = re.position(faceIndex,1); c -= x; auto faceDistance = (c*normal); if (faceDistance class FaceNormalGridFunction { public: using GridView = GV; using EntitySet = GridViewEntitySet; using Element = typename EntitySet::Element; using LocalDomain = typename EntitySet::LocalCoordinate; using Domain = typename EntitySet::GlobalCoordinate; using Range = typename EntitySet::GlobalCoordinate; private: using Traits = Imp::GridFunctionTraits; class LocalFunction { using Geometry = typename Element::Geometry; static const int dimension = GV::dimension; public: /** * \brief Bind the local-function to the passed element. * * This function stores a copy of `element` and its geometry. * * \b Expects: * - The `element` is in the entitySet of the grid-fuction. * * \b Ensures: * - The local-function is bound the the `element`. **/ void bind(const Element& element) { element_ = element; geometry_.emplace(element_.geometry()); } void unbind() { geometry_.reset(); } /** \brief Return if the local function is bound to a grid element */ bool bound() const { return static_cast(geometry_); } /** * \brief Evaluate the local-function in local coordinates `x`. * * This function computes the unit outward normal vector of the * face closest to the point `x` in the bound element. * * \b Expects: * - The local-function is bound to an element. **/ Range operator()(const LocalDomain& x) const { auto&& re = Dune::referenceElement(*geometry_); // Compute reference normal of closest face to given point auto face = Impl::closestFaceIndex(re, x); auto localNormal = re.integrationOuterNormal(face); // Transform reference normal into global unit outer normal using // covariant Piola transformation auto normal = Range{}; geometry_->jacobianInverseTransposed(x).mv(localNormal, normal); normal /= normal.two_norm(); return normal; } //! Return the bound element stored as copy in the \ref bind function. const Element& localContext() const { return element_; } //! Not implemented. friend typename Traits::LocalFunctionTraits::DerivativeInterface derivative(const LocalFunction& t) { DUNE_THROW(NotImplemented,"not implemented"); } private: std::optional geometry_; Element element_; }; public: //! Construct the FaceNormalGridFunction. FaceNormalGridFunction(const GridView& gridView) : entitySet_(gridView) {} //! Not implemented. Range operator()(const Domain& x) const { DUNE_THROW(NotImplemented,"not implemented"); } //! Not implemented. friend typename Traits::DerivativeInterface derivative(const FaceNormalGridFunction& t) { DUNE_THROW(NotImplemented,"not implemented"); } //! Return a local-function associated to FaceNormalGridFunction. friend LocalFunction localFunction(const FaceNormalGridFunction& t) { return LocalFunction{}; } //! Return the stored GridViewEntitySet. const EntitySet& entitySet() const { return entitySet_; } private: EntitySet entitySet_; }; } // namespace Dune::Functions #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_FACENORMALGRIDFUNCTION_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/finefunctiononcoarsegridview.hh000066400000000000000000000233351513634022200316030ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_FINEFUNCTIONONCOARSEGRIDVIEW_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_FINEFUNCTIONONCOARSEGRIDVIEW_HH #include #include #include #include #include #include #include #include #include #include #include namespace Dune::Functions { namespace Impl { namespace ReferenceElementUtilities { // Compute the l1-distance of x to the reference element identified // by the topology id and dimension. template()[0])> > FT distance(unsigned int topologyId, int dim, X x, FT scaleFactor = FT(1)) { using std::abs; using std::max; using std::min; auto dist_x_last = max(max(x[dim-1]-scaleFactor, -x[dim-1]), FT(0)); if (dim > 1) { if (Dune::Impl::isPyramid(topologyId, dim)) scaleFactor -= max(min(x[dim-1], scaleFactor), FT(0)); return distance(Dune::Impl::baseTopologyId(topologyId, dim), dim-1, x, scaleFactor) + dist_x_last; } if (dim == 1) return dist_x_last; return FT(0); } // Check if the l1-distance of x to the reference element identified // by the topology id and dimension is less than a tolerance. This // implementation is significantly faster than checking if // distance(...) <= tolerance. It is almost as fast as checkInside(...) // of the refenece element, but the latter does not reflect the // distance wrt any norm while we use the l1-norm here. template()[0])> > bool checkInside(unsigned int topologyId, int dim, X x, FT tolerance, FT scaleFactor = FT(1)) { using std::abs; using std::max; using std::min; if (dim > 0) { auto dist_x_last = max(x[dim-1]-scaleFactor, -x[dim-1]); if (dist_x_last <= tolerance) { if (Dune::Impl::isPyramid(topologyId, dim)) scaleFactor -= max(min(x[dim-1], scaleFactor), FT(0)); return checkInside(Dune::Impl::baseTopologyId(topologyId, dim), dim-1, x, tolerance - max(dist_x_last, FT(0)), scaleFactor); } return false; } return true; } } // namespace ReferenceElementUtilities } // namespace Impl /** * \brief A wrapper representing a fine grid function on a gridview * * \ingroup FunctionImplementations * * \tparam GridFunction Type of the wrapped grid function * \tparam GV Type of the target grid view this function should act on * * This wraps a grid function such that it can be used as a `GridViewFunction` * on a user-provided `GridView` under the following assumptions: * 1. The grid function's entity set and the `GridView` belong to the same grid. * 2. The entity set is finer than the `GridView` in the sense that any * element from the entity has an ancestor in the `GridView`. */ template class DerivativeTraits=Dune::Functions::DefaultDerivativeTraits> class FineFunctionOnCoarseGridView { using RawGridFunction = Dune::ResolveRef_t; auto&& rawFunction() const { return Dune::resolveRef(function_); } static constexpr auto dim = GV::Grid::dimension; public: using GridView = GV; using EntitySet = Dune::Functions::GridViewEntitySet; using Element = typename EntitySet::Element; using Domain = typename EntitySet::GlobalCoordinate; using LocalDomain = typename EntitySet::LocalCoordinate; using Range = std::decay_t()(std::declval()))>; private: using FineEntitySet = std::decay_t().entitySet())>; using Traits = Dune::Functions::Imp::GridFunctionTraits; class FineLocalFunctionOnCoarseGridView { using Traits = typename FineFunctionOnCoarseGridView::Traits::LocalFunctionTraits; public: using Derivative = decltype(localFunction(derivative(std::declval()))); using RawLocalFunction = std::decay_t()))>; /** * \brief Construct the LocalFunction * * The LocalFunction is created from the global FineFunctionOnCoarseGridView. **/ FineLocalFunctionOnCoarseGridView(RawLocalFunction&& localFunction, const FineEntitySet& fineEntitySet) : element_() , localFunction_(localFunction) , fineEntitySet_(fineEntitySet) , forwardToFineFunction_(false) {} /** * \brief Construct the LocalFunction * * The LocalFunction is created from the global FineFunctionOnCoarseGridView. **/ FineLocalFunctionOnCoarseGridView( RawLocalFunction&& localFunction, const FineEntitySet& fineEntitySet, bool forwardToFineFunction, const std::optional& element ) : element_(element) , localFunction_(localFunction) , fineEntitySet_(fineEntitySet) , forwardToFineFunction_(forwardToFineFunction) {} //! Bind to an element from the GridView void bind(const Element& element) { element_ = element; forwardToFineFunction_ = fineEntitySet_.contains(*element_); if (forwardToFineFunction_) localFunction_.bind(element); } //! \brief Unbind the inner local-functions. void unbind() { element_.reset(); } //! Return if the local function is bound to an element of the GridView bool bound() const { return static_cast(element_); } //! Obtain the grid element this function is bound to const Element& localContext() const { return *element_; } //! Obtain local derivative of this function friend auto derivative(const FineLocalFunctionOnCoarseGridView& f) { if constexpr(requires{ derivative(f.localFunction_); }) return Derivative(derivative(f.localFunction_), f.fineEntitySet_, f.forwardToFineFunction_, f.element_); else return typename Traits::DerivativeInterface{}; } //! Evaluate function in local coordinates Range operator()(LocalDomain x) const { if (forwardToFineFunction_) return localFunction_(x); return evaluateInDescendent(*element_, x); } private: // Find a child containing the point and evaluate there recursively Range evaluateInDescendent(const Element& element, LocalDomain x) const { Element closestChild; LocalDomain xInClosestChild; double distanceToClosestChild = std::numeric_limits::max(); for(const auto& child : descendantElements(element, element.level()+1)) { auto&& geometry = child.geometryInFather(); auto xInChild = geometry.local(x); auto dist = Impl::ReferenceElementUtilities::distance(child.type().id(), dim, xInChild); if (dist < distanceToClosestChild) { closestChild = child; distanceToClosestChild = dist; xInClosestChild = xInChild; if (distanceToClosestChild==0) break; } } if (fineEntitySet_.contains(closestChild)) { localFunction_.bind(closestChild); return localFunction_(xInClosestChild); } else return evaluateInDescendent(closestChild, xInClosestChild); } std::optional element_; mutable RawLocalFunction localFunction_; const FineEntitySet& fineEntitySet_; bool forwardToFineFunction_ = false; }; public: using LocalFunction = FineLocalFunctionOnCoarseGridView; /** * \brief Create FineFunctionOnCoarseGridView from GridFunction and GridView * * \param function The GridFunction that should be represented on gridView * \param gridView The GridFunction should be represented on this gridView */ FineFunctionOnCoarseGridView(const GridFunction& function, const GridView& gridView) : function_(function) , entitySet_(gridView) {} /** * \brief Create FineFunctionOnCoarseGridView from GridFunction and GridView * * \param function The GridFunction that should be represented on gridView * \param gridView The GridFunction should be represented on this gridView */ FineFunctionOnCoarseGridView(GridFunction&& function, const GridView& gridView) : function_(std::move(function)) , entitySet_(gridView) {} //! Evaluate function in global coordinates Range operator()(const Domain& x) const { return function_(x); } //! Obtain global derivative of this function friend auto derivative(const FineFunctionOnCoarseGridView& f) { if constexpr(requires{ derivative(f.rawFunction()); }) { using RawDerivative = std::decay_t; return FineFunctionOnCoarseGridView(derivative(f.rawFunction()), f.entitySet_.gridView()); } else return typename Traits::DerivativeInterface{}; } //! Create a LocalFunction for evaluation in local coordinates friend LocalFunction localFunction(const FineFunctionOnCoarseGridView& f) { return LocalFunction(localFunction(f.rawFunction()), f.rawFunction().entitySet()); } //! Return the EntitySet associated to this GridViewFunction const EntitySet& entitySet() const { return entitySet_; } protected: GridFunction function_; EntitySet entitySet_; }; } // namespace Dune::Functions #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_FINEFUNCTIONONCOARSEGRIDVIEW_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/gridfunction.hh000066400000000000000000000137261513634022200263170ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_GRID_FUNCTION_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_GRID_FUNCTION_HH #include #include #include #include #include #include #include #include #include namespace Dune { namespace Functions { /* * Default implementation is empty * The actual implementation is only given if Signature is an type * describing a function signature as Range(Domain). */ template class DerivativeTraits=DefaultDerivativeTraits, size_t bufferSize=56> class GridFunction {}; namespace Imp { //! Traits class providing type information for DifferentiableFunction template class DerivativeTraits, size_t bufferSize> struct GridFunctionTraits : DifferentiableFunctionTraits { protected: using Base=DifferentiableFunctionTraits; public: //! EntitySet the GridFunction lives on using EntitySet = ES; //! Element type of EntitySet using Element = typename EntitySet::Element; //! Signature of the derivative using DerivativeSignature = typename Base::DerivativeSignature; //! Interface type of the derivative using DerivativeInterface = GridFunction; //! Signature of the derivative using LocalSignature = typename Base::Range(typename EntitySet::LocalCoordinate); //! Traits for derivatives associated with this type template using LocalDerivativeTraits = typename Dune::Functions::LocalDerivativeTraits::template Traits; //! LocalFunctionTraits associated with this type using LocalFunctionTraits = typename Dune::Functions::Imp::LocalFunctionTraits; //! Interface type of the local function using LocalFunctionInterface = LocalFunction; //! Internal concept type for type erasure using Concept = GridFunctionWrapperInterface; //! Internal model template for type erasure template using Model = GridFunctionWrapperImplementation; }; } /** * \brief Wrapper class for functions defined on a Grid. * * \ingroup FunctionInterface * * Being defined on a grid means in particular that you can evaluate the function * in local coordinates of a given entities of the grid. The set of the entities * this function is defined on is given by an EntitySet. * * This models the \ref Concept::GridFunction concept. */ template class DerivativeTraits, size_t bufferSize> class GridFunction : public TypeErasureBase< typename Imp::GridFunctionTraits::Concept, Imp::GridFunctionTraits::template Model> { using Traits = Imp::GridFunctionTraits; using Base = TypeErasureBase; using DerivativeInterface = typename Traits::DerivativeInterface; using LocalFunctionInterface = typename Traits::LocalFunctionInterface; using EntitySet = typename Traits::EntitySet; public: /** * \brief Construct from function. * * \tparam F Function type * * \param f Function of type F * * \b Requirements: * - The passed function `f` must be a module of the GridFunction concept, * see \ref Concept::GridFunction. */ template = 0 > GridFunction(F&& f) : Base(std::forward(f)) { static_assert(Dune::Functions::Concept::isGridFunction(), "Trying to construct a GridFunction from type that does not model the GridFunction concept"); } GridFunction() = default; /** * \brief Evaluation of wrapped function. * * Evaluate the wrapped function in global coordinates `x`. */ Range operator() (const Domain& x) const { return this->asInterface().operator()(x); } /** * \brief Get derivative of wrapped function. * * This is a free function that will be found by ADL. * * The derivative is returned as GridFunction of a * type specified in the `GridFunctionTraits`. */ friend DerivativeInterface derivative(const GridFunction& t) { return t.asInterface().derivative(); } /** * \brief Get local function of wrapped function. * * This is a free function, to be found by ADL. * * Notice that the returned LocalFunction can * only be used after it has been bound to a * proper local context. */ friend LocalFunctionInterface localFunction(const GridFunction& t) { return t.asInterface().wrappedLocalFunction(); } /** * \brief Get associated EntitySet. * * The `EntitySet` is a range of grid entities * the associated `LocalFunction` can be bound to. */ const EntitySet& entitySet() const { return this->asInterface().wrappedEntitySet(); } }; }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_GRID_FUNCTION_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/gridfunction_imp.hh000066400000000000000000000040311513634022200271510ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_GRID_FUNCTION_IMP_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_GRID_FUNCTION_IMP_HH #include #include #include namespace Dune { namespace Functions { namespace Imp { /** * A concept describing types that have a localFunction() method found by ADL */ struct HasFreeLocalFunction { template auto require(F&& f) -> decltype( localFunction(f) ); }; // Interface of type erasure wrapper // // Notice that the basic interface of polymorphic classes (destructor, clone, ...) // will be added by the type erasure foundation classes. template class GridFunctionWrapperInterface : public DifferentiableFunctionWrapperInterface { public: virtual LocalFunctionInterface wrappedLocalFunction() const = 0; virtual const EntitySet& wrappedEntitySet() const = 0; }; // Implementation of type erasure wrapper template class GridFunctionWrapperImplementation : public DifferentiableFunctionWrapperImplementation { using Base = DifferentiableFunctionWrapperImplementation; public: using Base::Base; virtual LocalFunctionInterface wrappedLocalFunction() const { return localFunction(this->get()); } virtual const EntitySet& wrappedEntitySet() const { return this->get().entitySet(); } }; }}} // namespace Dune::Functions::Imp #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_GRID_FUNCTION_IMP_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/gridviewentityset.hh000066400000000000000000000040601513634022200274040ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_GRIDVIEWENTITYSET_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_GRIDVIEWENTITYSET_HH #include namespace Dune { namespace Functions { /** * \brief An entity set for all entities of given codim in a grid view. * * \ingroup FunctionUtility * * This implements the \ref Concept::EntitySet concept. */ template class GridViewEntitySet { public: typedef GV GridView; enum { codim = cd }; //! Type of Elements contained in this EntitySet typedef typename GridView::template Codim::Entity Element; //! Type of local coordinates with respect to the Element typedef typename Element::Geometry::LocalCoordinate LocalCoordinate; typedef typename Element::Geometry::GlobalCoordinate GlobalCoordinate; typedef Element value_type; //! A forward iterator typedef typename GridView::template Codim::Iterator const_iterator; //! Same as const_iterator typedef const_iterator iterator; //! Construct GridViewEntitySet for a GridView. GridViewEntitySet(const GridView& gv) : gv_(gv) {} //! Return true if `e` is contained in the EntitySet. bool contains(const Element& e) const { return gv_.contains(e); } //! Return number of Elements visited by an iterator. size_t size() const { return gv_.size(codim); } //! Create a begin iterator. const_iterator begin() const { return gv_.template begin(); } //! Create an end iterator. const_iterator end() const { return gv_.template end(); } //! Return the associated GridView. const GridView& gridView() const { return gv_; } private: GridView gv_; }; } // end of namespace Dune::Functions } // end of namespace Dune #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_GRIDVIEWENTITYSET_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/gridviewfunction.hh000066400000000000000000000066321513634022200272100ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_GRIDVIEWFUNCTION_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_GRIDVIEWFUNCTION_HH #include #include #include #include #include namespace Dune { namespace Functions { template class DerivativeTraits=DefaultDerivativeTraits, size_t bufferSize=56> class GridViewFunction {}; /** * \brief Wrapper class for functions defined on a GridView. * * \ingroup FunctionInterface * * Being defined on a grid view means in particular that you can evaluate the function * in local coordinates of a given element of the grid view. * * This models the \ref Concept::GridViewFunction concept. * * \tparam GV The GridView that the function is defined on * \tparam Domain The domain type used for function arguments * \tparam Range The range type used for function values */ template class DerivativeTraits, size_t bufferSize> class GridViewFunction : public GridFunction, DerivativeTraits, bufferSize> { using Base = GridFunction, DerivativeTraits, bufferSize>; public: using GridView = GV; using Base::Base; }; /** * \brief Construct a function modeling GridViewFunction from function and grid view. * * This specialization is used for functions that already * support `localFunction()`. It will simply return a copy of `f`. * * \param f A function object supporting argument compatible with global coordinates * \param gridView The GridView the function should act on. * * \returns A function that models the GridViewFunction interface. */ template() , int> = 0> std::decay_t makeGridViewFunction(F&& f, const GridView& gridView) { return std::forward(f); } /** * \brief Construct a function modeling GridViewFunction from function and grid view. * * This specialization is used for functions that do not * support `localFunction()` themselves. It will forward * to `makeAnalyticGridViewFunction()`. * * Notice that the returned function will store a copy of * the original function and the GridView. * * \param f A function object supporting argument compatible with global coordinates * \param gridView The GridView the function should act on. * * \returns A function that models the GridFunction interface. */ template()) , int> = 0> auto makeGridViewFunction(F&& f, const GridView& gridView) -> decltype(makeAnalyticGridViewFunction(std::forward(f), gridView)) { return makeAnalyticGridViewFunction(std::forward(f), gridView); } } // end of namespace Dune::Functions } // end of namespace Dune #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_GRIDVIEWFUNCTION_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/localderivativetraits.hh000066400000000000000000000026531513634022200302250ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_LOCALDERIVATIVE_TRAITS_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_LOCALDERIVATIVE_TRAITS_HH #include namespace Dune { namespace Functions { /** * \brief Derivative traits for local functions. * * \ingroup FunctionUtility * * This provides derivative traits to be used by local functions with * given `EntitySet` and global `DerivativeTraits`. * * The reason why this is needed is that local functions return * derivatives wrt to global coordinates although the arguments * for these derivatives are local coordinates. */ template class DerivativeTraits=DefaultDerivativeTraits> struct LocalDerivativeTraits { using LocalDomain = typename EntitySet::LocalCoordinate; using Domain = typename EntitySet::GlobalCoordinate; template struct Traits { typedef InvalidRange Range; }; template struct Traits { using Range = typename DerivativeTraits::Range; }; }; }} // namespace Dune::Functions #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_LOCALDERIVATIVE_TRAITS_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/000077500000000000000000000000001513634022200242515ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/.gitignore000066400000000000000000000004501513634022200262400ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # temporary files generated by the test system *.log *.trs # individual tests discretescalarglobalbasisfunctiontest dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/CMakeLists.txt000066400000000000000000000014371513634022200270160ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later # tests that should build and run successfully link_libraries(Dune::Functions) dune_add_test(SOURCES analyticgridviewfunctiontest.cc LABELS quick) dune_add_test(SOURCES coarsefinegridfunctiontest.cc LABELS quick) dune_add_test(SOURCES composedgridfunctiontest.cc LABELS quick) dune_add_test(SOURCES discreteglobalbasisfunctiontest.cc LABELS quick) dune_add_test(SOURCES discreteglobalbasisfunctionderivativetest.cc LABELS quick) dune_add_test(SOURCES facenormalgridfunctiontest.cc LABELS quick) dune_add_test(SOURCES gridfunctiontest.cc LABELS quick) dune_add_test(SOURCES localfunctioncopytest.cc LABELS quick) dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/analyticgridviewfunctiontest.cc000066400000000000000000000132731513634022200326010ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; using namespace Dune::Functions::Test; double x_component_A(const Dune::FieldVector & x) { return x[0]; } template double x_component_B(const Coord & x) { return x[0]; } template void checkCopyable(const F& f) { F f2 = f; F f3 = std::move(f2); f2 = f3; f3 = std::move(f2); #if DUNE_ENABLE_CONCEPTS static_assert(std::copyable); #endif } int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); // Generate grid for testing const int dim = 2; typedef YaspGrid GridType; FieldVector l(1); std::array elements = {{10, 10}}; GridType grid(l,elements); using GridView = typename GridType::LeafGridView; const GridView& gridView = grid.leafGridView(); double exactIntegral = 0.5; bool passed = true; using Domain = GridView::Codim<0>::Geometry::GlobalCoordinate; std::cout << "Testing manual construction of AnalyticGridViewFunction with range type double" << std::endl; { using Range = double; auto f = [](const Domain& x) {return x[0];}; AnalyticGridViewFunction fGVF(f, gridView); passed = passed and Dune::Functions::Test::checkGridViewFunction(gridView, fGVF, exactIntegral); } std::cout << "Testing deduction guide of AnalyticGridViewFunction with range type double" << std::endl; { auto f = [](const Domain& x) {return x[0];}; passed = passed and Dune::Functions::Test::checkGridViewFunction(gridView, AnalyticGridViewFunction{f, gridView}, exactIntegral); } std::cout << "Testing makeAnalyticGridViewFunction with range type double" << std::endl; { auto f = [](const Domain& x) {return x[0];}; passed = passed and Dune::Functions::Test::checkGridViewFunction(gridView, makeAnalyticGridViewFunction(f, gridView), exactIntegral); } std::cout << "Testing makeGridViewFunction with range type double" << std::endl; { auto f = [](const Domain& x) {return x[0];}; passed = passed and Dune::Functions::Test::checkGridViewFunction(gridView, makeGridViewFunction(f, gridView), exactIntegral); } std::cout << "Testing makeAnalyticGridViewFunction with free function" << std::endl; { auto f = x_component_A; passed = passed and Dune::Functions::Test::checkGridViewFunction(gridView, makeAnalyticGridViewFunction(f, gridView), exactIntegral); } std::cout << "Testing makeAnalyticGridViewFunction with free template function" << std::endl; { auto f = x_component_B; passed = passed and Dune::Functions::Test::checkGridViewFunction(gridView, makeAnalyticGridViewFunction(f, gridView), exactIntegral); } std::cout << "Testing makeAnalyticGridViewFunction with derivate" << std::endl; { using Range = double; auto f = [](Domain){ return 1.0; }; auto df = [](Domain){ return FieldVector({0.0, 0.0}); }; auto af = makeDifferentiableFunctionFromCallables(Dune::Functions::SignatureTag(), f, df); auto gf = makeAnalyticGridViewFunction(af, gridView); checkCopyable(gf); passed = passed and Dune::Functions::Test::checkGridViewFunction(gridView, gf, 1.0); auto ep = gridView.template begin<0>(); { std::cout << "Checking evaluation of function and derivative" << std::endl; auto _lf = localFunction(gf); checkCopyable(_lf); _lf.bind(*ep); auto _df = derivative(gf); checkCopyable(_df); auto _ldf = localFunction(_df); checkCopyable(_ldf); _ldf.bind(*ep); passed = passed and gf({0.0,0.0}) == 1.0; passed = passed and _lf({0.0,0.0}) == 1.0; passed = passed and _df({0.0,0.0}) == Domain(0.0); passed = passed and _ldf({0.0,0.0}) == Domain(0.0); } { std::cout << "Checking evaluation of function and derivative via Interface class" << std::endl; GridViewFunction _gf = gf; auto _lf = localFunction(_gf); checkCopyable(_lf); _lf.bind(*ep); auto _df = derivative(_gf); checkCopyable(_df); auto _ldf = localFunction(_df); checkCopyable(_ldf); _ldf.bind(*ep); passed = passed and gf({0.0,0.0}) == 1.0; passed = passed and _lf({0.0,0.0}) == 1.0; passed = passed and _df({0.0,0.0}) == Domain(0.0); passed = passed and _ldf({0.0,0.0}) == Domain(0.0); } { std::cout << "Checking evaluation of local-function and local-derivative" << std::endl; auto lf = localFunction(gf); checkCopyable(lf); lf.bind(*ep); auto dlf = derivative(lf); checkCopyable(dlf); passed = passed and gf({0.0,0.0}) == 1.0; passed = passed and lf({0.0,0.0}) == 1.0; passed = passed and dlf({0.0,0.0}) == Domain(0.0); } } if (passed) std::cout << "All tests passed" << std::endl; return passed ? 0: 1; } catch ( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/coarsefinegridfunctiontest.cc000066400000000000000000000346731513634022200322270ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include template requires Dune::IsNumber::value auto rangeDist(X x1, X x2) { return std::abs(x1-x2); } template auto rangeDist(Dune::FieldVector x1, Dune::FieldVector x2) { x1 -= x2; return x1.infinity_norm(); } template auto rangeDist(Dune::FieldMatrix x1, Dune::FieldMatrix x2) { x1 -= x2; return x1.infinity_norm(); } // This is used as a drop-in replacement for Dune::QuadratureRules // except for pyramids. For the latter, the standard Dune rules provide // points on the diagonal. Unfortunately the compile time order Lagrange // elements on pyramids are piecewise polynomial with gradients jumping // across the diagonal. This utility defines pyramid rules by splitting // them into two tetrahedra. Since the pyramid order-k shape functions // are bi-order-k polynomials double the order for those. template struct QuadratureRules { using QuadratureRule = Dune::QuadratureRule; using QuadraturePoint = Dune::QuadraturePoint; using Coordinate = typename QuadraturePoint::Vector; static const auto& rule(const Dune::GeometryType& type, size_t order) { static auto pyramidRules = std::vector(); if (type == Dune::GeometryTypes::pyramid) { if (pyramidRules.size() < order) for(auto k : Dune::range(pyramidRules.size(), std::size_t(order+1))) { const auto& simplexRule = Dune::QuadratureRules::rule(Dune::GeometryTypes::tetrahedron, 2*k); auto pyramidRule = QuadratureRule(); for(auto [position, weight] : simplexRule) { pyramidRule.emplace_back(Coordinate({position[0], position[0]+position[1], position[2]}), weight); pyramidRule.emplace_back(Coordinate({position[0]+position[1], position[1], position[2]}), weight); } pyramidRules.push_back(pyramidRule); } return pyramidRules[order]; } else return Dune::QuadratureRules::rule(type, order); } }; // Check difference of local functions on element template double maxNormDistanceOnElement(const Element& element, const F& f_local, const G& g_local, bool checkCorners, std::size_t sampleQuadratureOrder = 5) { constexpr auto dimension = Element::Geometry::mydimension; const auto& re = referenceElement(element); double diff = 0.0; const auto& quad = QuadratureRules::rule(element.type(), sampleQuadratureOrder); for (const auto& [x, weight] : quad) { auto f_x = f_local(x); auto g_x = g_local(x); diff = std::max(diff, rangeDist(f_x, g_x)); } if (checkCorners) for (const auto& k : Dune::range(re.size(dimension))) { auto x = re.position(k, dimension); auto f_x = f_local(x); auto g_x = g_local(x); diff = std::max(diff, rangeDist(f_x, g_x)); } return diff; } // Check difference of grid functions on grid view template double maxNormDistanceOnGridView(const GridView& gridView, const F& f, const G& g, bool checkVertices, std::size_t sampleQuadratureOrder = 5) { double diff = 0.0; auto f_local = localFunction(f); auto g_local = localFunction(g); for(const auto& element : elements(gridView)) { f_local.bind(element); g_local.bind(element); diff = std::max(diff, maxNormDistanceOnElement(element, f_local, g_local, checkVertices, sampleQuadratureOrder)); } return diff; } template double maxNormDerivativeDistanceOnGridView(const GridView& gridView, const F& f, const G& g, bool checkVertices, std::size_t sampleQuadratureOrder = 5) { double diff = 0.0; // First check by obtaining the derivative globally diff = maxNormDistanceOnGridView(gridView, derivative(f), derivative(g), checkVertices, sampleQuadratureOrder); // Now check by obtaining the derivative locally auto f_local = localFunction(f); auto g_local = localFunction(g); for(const auto& element : elements(gridView)) { f_local.bind(element); g_local.bind(element); auto df_local = derivative(f_local); auto dg_local = derivative(g_local); diff = std::max(diff, maxNormDistanceOnElement(element, df_local, dg_local, checkVertices, sampleQuadratureOrder)); } return diff; } template Dune::TestSuite checkOnGrid(const Grid& grid, std::string name) { auto testSuite = Dune::TestSuite(name); using Dune::Functions::Concept::isGridViewFunction; using Element = typename Grid::template Codim<0>::Entity; using Domain = typename Element::Geometry::GlobalCoordinate; using Range = Dune::FieldVector; double TOL = 1e-14; double Gradient_TOL = TOL*std::pow(2., double(grid.maxLevel())); std::cout << "Checking grid with " << (grid.maxLevel()+1) << " levels with tolerance " << TOL << std::endl; auto g = [](auto x) { auto y = Range(); y[0] = 1.0; for(auto xi : x) y[0] *= std::sin(xi); y[1] = 1.0; for(auto xi : x) y[1] *= std::cos(xi); return y; }; using namespace Dune::Functions::BasisFactory; auto preBasisFactory = power<2>(lagrange<2>(), flatInterleaved()); auto gridView_f = grid.leafGridView(); auto basis_f = makeBasis(gridView_f, preBasisFactory); std::vector coeff_f; Dune::Functions::interpolate(basis_f, coeff_f, g); auto g_f = Dune::Functions::makeDiscreteGlobalBasisFunction(basis_f, coeff_f); // Check with any level grid view as coarse grid view for(auto level : Dune::range(grid.maxLevel()+1)) { auto gridView_c = grid.levelGridView(level); // ******************************************************************************** // First check different combinations of mapping the fine interpolation across grid views. // ******************************************************************************** // Map fine function to coarse level grid view auto g_fc = Dune::Functions::FineFunctionOnCoarseGridView(g_f, gridView_c); testSuite.check(isGridViewFunction()); // Remap coarse function to fine leaf grid view auto g_fcf = Dune::Functions::CoarseFunctionOnFineGridView(g_fc, gridView_f); testSuite.check(isGridViewFunction()); // Compare values on fine level { auto dist = maxNormDistanceOnGridView(gridView_f, g_fcf, g_f, true); testSuite.check(dist < TOL) << "Values of mapped fine->coarse->fine function differ from fine function by " << dist; } // Compare jacobians on fine level // Since the sample points are in the interiour of fine elements // they are in the interiour of coarse elements, too. Hence we // should not get problems due to the fact that the derivative // is discontinuous across fine elements. However, we should not // check the element corners. { auto dist = maxNormDerivativeDistanceOnGridView(gridView_f, g_fcf, g_f, false); testSuite.check(dist < Gradient_TOL) << "Jacobians of mapped fine->coarse->fine function differ from fine function by " << dist; } // Remap fine function to coarse level grid view auto g_fcfc = Dune::Functions::FineFunctionOnCoarseGridView(g_fcf, gridView_c); testSuite.check(isGridViewFunction()); // Compare values on coarse level { auto dist = maxNormDistanceOnGridView(gridView_c, g_fcfc, g_fc, true); testSuite.check(dist < TOL) << "Values of mapped fine->coarse->fine->coarse function differ from fine->coarse function by " << dist; } // Do not compare jacobians on coarse level // We do not compare the derivatives of g_fc anfd g_fcfc on coarse // elements. Since the derivatives are discontinuous across fine // elements, the sample points within coarse elements may be on // a discontinuity such that both evaluate in different fine // elements leading to different results. Since the interpolated // function is C^2, the error will scale by a power of the fine // mesh size. // // ******************************************************************************** // Now check different combinations of mapping the coarse interpolation across grid views. // ******************************************************************************** auto basis_c = makeBasis(gridView_c, preBasisFactory); std::vector coeff_c; Dune::Functions::interpolate(basis_c, coeff_c, g); auto g_c = Dune::Functions::makeDiscreteGlobalBasisFunction(basis_c, coeff_c); // Map coarse function to fine leaf grid view auto g_cf = Dune::Functions::CoarseFunctionOnFineGridView(g_c, gridView_f); testSuite.check(isGridViewFunction()); // Remap fine function to coarse level grid view auto g_cfc = Dune::Functions::FineFunctionOnCoarseGridView(g_cf, gridView_c); testSuite.check(isGridViewFunction()); // Compare values on coarse level { auto dist = maxNormDistanceOnGridView(gridView_c, g_cfc, g_c, true); testSuite.check(dist < TOL) << "Values of mapped coarse->fine->coarse function differ from coarse function by " << dist; } // Compare jacobians on coarse level // Since the coarse interpolates derivative is continuous within // the coarse elements, this is safe. Since we only search children // of the coares element, we can even check the corners. { auto dist = maxNormDerivativeDistanceOnGridView(gridView_c, g_cfc, g_c, true); testSuite.check(dist < Gradient_TOL) << "Jacobians of mapped coarse->fine->coarse function differ from coarse function by " << dist; } } return testSuite; } template void refineLocalNearOrigin(Grid& grid, std::size_t refinements) { constexpr auto dimension = Grid::dimension; auto origin = Dune::FieldVector(); for([[maybe_unused]] auto k : Dune::range(refinements)) { for(const auto& element : elements(grid.leafGridView())) { const auto& re = referenceElement(element); const auto& geometry = element.geometry(); if (re.checkInside(geometry.local(origin))) grid.mark(1, element); } grid.preAdapt(); grid.adapt(); grid.postAdapt(); } } int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite testSuite; { using Grid = Dune::YaspGrid<1>; auto grid = Dune::StructuredGridFactory::createCubeGrid({{0}}, {{1}}, {{2}}); grid->globalRefine(2); testSuite.subTest(checkOnGrid(*grid, "YaspGrid<1>")); } { using Grid = Dune::YaspGrid<2>; auto grid = Dune::StructuredGridFactory::createCubeGrid({{0,0}}, {{1,1}}, {{2,2}}); grid->globalRefine(2); testSuite.subTest(checkOnGrid(*grid, "YaspGrid<2>")); } { using Grid = Dune::YaspGrid<3>; auto grid = Dune::StructuredGridFactory::createCubeGrid({{0,0,0}}, {{1,1,1}}, {{2,2,2}}); grid->globalRefine(2); testSuite.subTest(checkOnGrid(*grid, "YaspGrid<3>")); } // We use UGGrid with RefinementType=copy to ensure that intermediate levels cover the whole domain { using Grid = Dune::UGGrid<2>; auto grid = Dune::StructuredGridFactory::createSimplexGrid({{0,0}}, {{1,1}}, {{1,1}}); grid->setRefinementType(Grid::RefinementType::COPY); refineLocalNearOrigin(*grid, 15); testSuite.subTest(checkOnGrid(*grid, "UGGrid<2> (triangles)")); } { using Grid = Dune::UGGrid<2>; auto factory = Dune::GridFactory(); factory.insertVertex({0,0}); factory.insertVertex({0,1}); factory.insertVertex({1,0}); factory.insertVertex({2,2}); factory.insertElement(Dune::GeometryTypes::cube(2), {0,1,2,3}); auto grid = factory.createGrid(); grid->setRefinementType(Grid::RefinementType::COPY); refineLocalNearOrigin(*grid, 15); testSuite.subTest(checkOnGrid(*grid, "UGGrid<2> (nonaffine rectangles)")); } { using Grid = Dune::UGGrid<3>; auto grid = Dune::StructuredGridFactory::createSimplexGrid({{0,0,0}}, {{1,1,1}}, {{1,1,1}}); grid->setRefinementType(Grid::RefinementType::COPY); refineLocalNearOrigin(*grid, 4); testSuite.subTest(checkOnGrid(*grid, "UGGrid<2> (triangles)")); } { using Grid = Dune::UGGrid<3>; auto factory = Dune::GridFactory(); factory.insertVertex({0,0,0}); factory.insertVertex({1,0,0}); factory.insertVertex({0,1,0}); factory.insertVertex({1,1,0}); factory.insertVertex({0,0,1}); factory.insertVertex({1,0,1}); factory.insertVertex({0,1,1}); factory.insertVertex({2,2,2}); factory.insertElement(Dune::GeometryTypes::cube(3), {0,1,2,3,4,5,6,7}); auto grid = factory.createGrid(); grid->setRefinementType(Grid::RefinementType::COPY); refineLocalNearOrigin(*grid, 4); testSuite.subTest(checkOnGrid(*grid, "UGGrid<3> (nonaffine cubes)")); } return testSuite.exit(); } catch ( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/composedgridfunctiontest.cc000066400000000000000000000111541513634022200317070ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; using namespace Dune::Functions::Test; int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); TestSuite suite; // Generate grid for testing const int dim = 2; using Grid = Dune::YaspGrid; auto l = FieldVector{1, 1}; auto elements = std::array{{10, 10}}; auto grid = Grid(l, elements); auto gridView = grid.leafGridView(); using namespace Functions::BasisFactory; { using Range = FieldVector; // Inner test function f is a polynomial of degree 2. auto f = [](const auto& x){ Range y; for (typename Range::size_type i=0; i(lagrange<2>(), blockedInterleaved())); Dune::BlockVector c; // Interpolate f wrt basis. interpolate(basis, c, f); // Create grid functions from coefficients of individual // components. Each is the piecewise Q2 interpolation of f_i. // They should be exact, since f is P2. auto basis_0 = Dune::Functions::subspaceBasis(basis, 0); auto basis_1 = Dune::Functions::subspaceBasis(basis, 1); auto f0_gridfunction = Dune::Functions::makeDiscreteGlobalBasisFunction(basis_0, c); auto f1_gridfunction = Dune::Functions::makeDiscreteGlobalBasisFunction(basis_1, c); // Now compose on top of individual component GridFunctions and check. { // Check with capture by std::ref auto gf_gridfunction = makeComposedGridFunction(g, std::ref(f0_gridfunction), std::ref(f1_gridfunction)); auto gf_gridfunction_= ComposedGridFunction(g, std::ref(f0_gridfunction), std::ref(f1_gridfunction)); static_assert(std::is_same_v); suite.check( checkGridViewFunction(gridView, gf_gridfunction, integral, 4), "Check if ComposedGridFunction has correct integral (capture with std::ref)"); } { // Check with capture by std::cref auto gf_gridfunction = makeComposedGridFunction(g, std::cref(f0_gridfunction), std::cref(f1_gridfunction)); auto gf_gridfunction_= ComposedGridFunction(g, std::cref(f0_gridfunction), std::cref(f1_gridfunction)); static_assert(std::is_same_v); suite.check( checkGridViewFunction(gridView, gf_gridfunction, integral, 4), "Check if ComposedGridFunction has correct integral (capture with std::cref)"); } { // Check with capture by value auto gf_gridfunction = makeComposedGridFunction(g, f0_gridfunction, f1_gridfunction); auto gf_gridfunction_= ComposedGridFunction(g, f0_gridfunction, f1_gridfunction); static_assert(std::is_same_v); suite.check( checkGridViewFunction(gridView, gf_gridfunction, integral, 4), "Check if ComposedGridFunction has correct integral (capture by value)"); } } return suite.exit(); } catch ( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } discreteglobalbasisfunctionderivativetest.cc000066400000000000000000000131211513634022200352350ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include struct Difference2 { template double operator()(Dune::FieldVector x, Dune::FieldVector y) const { return (x -= y).two_norm2(); } template double operator()(Dune::FieldMatrix x, Dune::FieldMatrix y) const { return (x -= y).frobenius_norm2(); } }; template Dune::TestSuite compare(const Function& function, const Reference& reference, const int quadOrder, const double tol) { Dune::TestSuite test; double err2 = 0.0; double err3 = 0.0; const auto& gridView = function.basis().gridView(); const int dim = std::decay_t::dimension; const auto f = Dune::Functions::makeComposedGridFunction( Difference2(), function, reference ); auto flocal = localFunction(f); for (const auto& e : elements(gridView)) { const auto geometry = e.geometry(); const auto& quad = Dune::QuadratureRules::rule(e.type(), quadOrder); flocal.bind(e); for (const auto& qp : quad) { const auto x = qp.position(); const auto integrationElement = geometry.integrationElement(x); err2 += flocal(x) * qp.weight() * integrationElement; // evaluate the functions in global coordinates const auto X = geometry.global(x); err3 += f(X) * qp.weight() * integrationElement; } } const double err = 0.5*(std::sqrt(err2) + std::sqrt(err3)); std::cout << "err = " << err << "\n"; test.check(err <= tol); return test; } int main(int argc, char** argv) { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite test; const int dim = 2; const int order = 3; auto grid = Dune::YaspGrid({1., 1.}, {10, 42}); const auto gridView = leafGridView(grid); using namespace Dune::Functions::BasisFactory; // scalar Lagrange basis with scalar coefficients { const auto f = [](auto&& x) -> double { return 42. * x[0] * x[0] + 13. * x[1] * x[1] + 7 * x[0] * x[1]; }; const auto fprime = Dune::Functions::makeAnalyticGridViewFunction( [](auto&& x) -> Dune::FieldVector { return { 84. * x[0] + 7 * x[1], 26. * x[1] + 7 * x[0] }; }, gridView); const auto basis = makeBasis(gridView, lagrange()); auto coefficients = std::vector(); Dune::Functions::interpolate(basis, coefficients, f); auto f2 = Dune::Functions::makeDiscreteGlobalBasisFunction(basis, coefficients); auto f2prime = derivative(f2); static_assert(Dune::Functions::Concept::isDifferentiableGridViewFunction< decltype(f2), double(Dune::FieldVector), std::decay_t>()); static_assert(Dune::Functions::Concept::isDifferentiableGridViewFunction< decltype(f2prime), Dune::FieldVector(Dune::FieldVector), std::decay_t>()); // `order` should be enough; `order+1` is more than enough. // The tolerance is ~100 times the error observed when writing this test. test.subTest(compare(f2prime, fprime, order+1, 6.6e-11)); } // scalar Lagrange basis with vector coefficients { const auto f = [](auto&& x) -> Dune::FieldVector { return { x[0] * x[0] * x[0] + 23. * x[0] * x[1], 6. * x[0] + 9000. * x[1], 9001. * x[0] * x[0] + 17. * x[1] }; }; const auto fprime = Dune::Functions::makeAnalyticGridViewFunction( [](auto&& x) -> Dune::FieldMatrix { return { { 3. * x[0] * x[0] + 23. * x[1], 23. * x[0] }, { 6., 9000. }, { 18002. * x[0], 17. } }; }, gridView); const auto basis = makeBasis(gridView, power<3>(lagrange(), blockedInterleaved())); auto coefficients = std::vector< Dune::FieldVector >(); Dune::Functions::interpolate(basis, coefficients, f); auto f2 = Dune::Functions::makeDiscreteGlobalBasisFunction< Dune::FieldVector >(basis, coefficients); auto f2prime = derivative(f2); static_assert(Dune::Functions::Concept::isDifferentiableGridViewFunction< decltype(f2), Dune::FieldVector(Dune::FieldVector), std::decay_t>()); static_assert(Dune::Functions::Concept::isDifferentiableGridViewFunction< decltype(f2prime), Dune::FieldMatrix(Dune::FieldVector), std::decay_t>()); // `order` should be enough; `order+1` is more than enough. // The tolerance is ~100 times the error observed when writing this test. test.subTest(compare(f2prime, fprime, order+1, 1.7e-8)); } return test.exit(); } dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/discreteglobalbasisfunctiontest.cc000066400000000000000000000206361513634022200332420ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; using namespace Dune::Functions::Test; template double infinityDiff(const Dune::FieldVector& x, const Dune::FieldVector& y) { return (x-y).infinity_norm(); } double infinityDiff(const double& x, const double& y) { return std::fabs(x-y); } double infinityDiff(const bool& x, const bool& y) { return std::fabs(x-y); } template bool checkInterpolationConsistency(B&& basis, C&& x) { using Coeff = std::decay_t; using Range = R; bool passed = true; // generate a discrete function auto f = Dune::Functions::makeDiscreteGlobalBasisFunction(basis, x); // By wrapping into a lambda, we force interpolate to use the global evaluation of f auto fGlobal = [&](auto x){ return f(x); }; Dune::Hybrid::forEach(std::tie(f, fGlobal), [&](auto&& ff) { Coeff y; interpolate(basis, y, ff); for (typename std::decay_t::size_type i=0; i 1e-10) { std::cout << "Interpolation of DiscreteGlobalBasisFunction differs from original coefficient vector" << std::endl; passed = false; } } }); return passed; } int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); bool passed = true; // Generate grid for testing const int dim = 2; typedef YaspGrid GridType; FieldVector l(1); std::array elements = {{10, 10}}; GridType grid(l,elements); using namespace Functions::BasisFactory; const auto& gridView = grid.leafGridView(); // scalar Lagrange Basis { auto feBasis = makeBasis(gridView,lagrange<1>()); auto f = [](const auto& x){ return (x.two_norm()<0.5); }; std::vector x; interpolate(feBasis, x, f); using Range = bool; auto passedThisTest = checkInterpolationConsistency(feBasis, x); std::cout << "checkInterpolationConsistency for scalar Lagrange basis" << (passedThisTest? " " : " NOT ") << "successful." << std::endl; passed = passed and passedThisTest; } // power Lagrange basis { auto feBasis = makeBasis(gridView,power<2>(lagrange<2>(), blockedInterleaved())); using Range = FieldVector; // f(x,y) = (y,x) auto f = [](const auto& x){ return Range{ x[1], x[0] }; }; std::vector x; interpolate(feBasis, x, f); auto passedThisTest = checkInterpolationConsistency(feBasis, x); std::cout << "checkInterpolationConsistency for power Lagrange basis" << (passedThisTest? " " : " NOT ") << "successful." << std::endl; passed = passed and passedThisTest; } // Taylor-Hood basis { auto taylorHoodBasis = makeBasis( gridView, composite( power( lagrange<2>(), flatLexicographic()), lagrange<1>(), flatLexicographic() )); using namespace Dune::Indices; // check with velocity and pressure subspace basis { auto feBasis = Dune::Functions::subspaceBasis(taylorHoodBasis, _0); using Range = FieldVector; auto f = [](const auto& x){ return Range{ x[1], x[0] }; }; std::vector x; interpolate(feBasis, x, f); auto passedThisTest = checkInterpolationConsistency(feBasis, x); std::cout << "checkInterpolationConsistency for velocity part of Taylor-Hood basis" << (passedThisTest? " " : " NOT ") << "successful." << std::endl; passed = passed and passedThisTest; } { auto feBasis = Dune::Functions::subspaceBasis(taylorHoodBasis, _1); using Range = double; auto f = [](const auto& x){ return Range{ x[0] }; }; std::vector x; interpolate(feBasis, x, f); auto passedThisTest = checkInterpolationConsistency(feBasis, x); std::cout << "checkInterpolationConsistency for pressure part of Taylor-Hood basis" << (passedThisTest? " " : " NOT ") << "successful." << std::endl; passed = passed and passedThisTest; } } // Raviart-Thomas basis { auto feBasis = makeBasis(gridView, raviartThomas<0>()); // coefficients and range of the FE are different here! using Coeff = double; using Range = FieldVector; // f(x,y) = (y,x) auto f = [](const auto& x){ return Range{ x[1], x[0] }; }; std::vector x; interpolate(feBasis, x, f); auto passedThisTest = checkInterpolationConsistency(feBasis, x); std::cout << "checkInterpolationConsistency for Raviart-Thomas basis " << (passedThisTest? " " : " NOT ") << "successful." << std::endl; passed = passed and passedThisTest; } // Nédélec basis { auto feBasis = makeBasis(gridView, nedelec<1,1>()); // coefficients and range of the FE are different here! using Coeff = double; using Range = FieldVector; // f(x,y) = (y,x) auto f = [](const auto& x){ return Range{ x[1], x[0] }; }; std::vector x; interpolate(feBasis, x, f); auto passedThisTest = checkInterpolationConsistency(feBasis, x); std::cout << "checkInterpolationConsistency for Nédélec basis " << (passedThisTest? " " : " NOT ") << "successful." << std::endl; passed = passed and passedThisTest; } // complex nested Lagrange basis { auto feBasis = makeBasis(gridView, power<2>( composite( lagrange<2>(), power<2>( lagrange<1>(), flatLexicographic()), flatLexicographic()), flatLexicographic())); using Range = std::array>,2>; auto f = [](const auto& x) -> Range { return std::array{ Dune::makeTupleVector(x[0]+x[1], std::array{x[0]+x[1]+1.0, x[0]+x[1]+2.0}), Dune::makeTupleVector(x[0]+x[1]+3.0, std::array{x[0]+x[1]+4.0, x[0]+x[1]+5.0}), }; }; std::vector x; interpolate(feBasis, x, f); auto passedThisTest = checkInterpolationConsistency(feBasis, x); std::cout << "checkInterpolationConsistency for complex nested Lagrange basis" << (passedThisTest? " " : " NOT ") << "successful." << std::endl; passed = passed and passedThisTest; } // Sample the function f(x,y) = x on the grid vertices // If we use that as the coefficients of a finite element function, // we know its integral and can check whether quadrature returns // the correct result. Notice that resizing is done by the interpolate method. auto feBasis = makeBasis(gridView,lagrange<1>()); std::vector x; auto fAnalytic = [](const auto& x){ return x[0];}; interpolate(feBasis, x, fAnalytic); using Range = FieldVector; passed = passed and checkInterpolationConsistency(feBasis, x); // generate a discrete function to evaluate the integral auto f = Dune::Functions::makeDiscreteGlobalBasisFunction(feBasis, x); double exactIntegral = 0.5; std::cout << "Testing with raw DiscreteGlobalBasisFunction" << std::endl; passed = passed and Dune::Functions::Test::checkGridViewFunction(gridView, f, exactIntegral); if (passed) std::cout << "All tests passed" << std::endl; return passed ? 0: 1; } catch ( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/facenormalgridfunctiontest.cc000066400000000000000000000115541513634022200322110ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include template Dune::TestSuite checkFaceNormalGridFunction(const GridView& gridView, std::string name) { using Dune::Functions::Concept::isGridFunction; using F = Dune::Functions::FaceNormalGridFunction; using EntitySet = typename F::EntitySet; using Domain = typename EntitySet::GlobalCoordinate; using Range = Domain; auto testSuite = Dune::TestSuite(name); // Check is GridFunction concept is satisfied testSuite.check(isGridFunction()) << "FaceNormalGridFunction does not model GridFunction concept"; // Check if normals coincide with intersection normals // up to given tolerance at quadrature point. double TOL = 1e-14; std::size_t sampleQuadratureOrder = 10; constexpr int dimension = GridView::dimension; auto normals = Dune::Functions::FaceNormalGridFunction(gridView); auto localNormals = localFunction(normals); for(auto&& element : elements(gridView)) { localNormals.bind(element); for(const auto& intersection: intersections(gridView, element)) { const auto& quad = Dune::QuadratureRules::rule(intersection.type(), sampleQuadratureOrder); double mismatch = 0.0; for (const auto& quadPoint : quad) { auto normal = intersection.unitOuterNormal(quadPoint.position()); normal -= localNormals(intersection.geometryInInside().global(quadPoint.position())); mismatch = std::max(mismatch, normal.infinity_norm()); } testSuite.check(mismatch <= TOL) << "Normal mismatch of " << Dune::formatString("%12.5e", mismatch) << " for face " << intersection.indexInInside() << " of " << intersection.inside().type() << " with index " << gridView.indexSet().index(intersection.inside()); } } return testSuite; } int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); Dune::TestSuite testSuite; { using Grid = Dune::YaspGrid<2>; auto grid = Dune::StructuredGridFactory::createCubeGrid({{0,0}}, {{1,1}}, {{2,2}}); grid->globalRefine(2); testSuite.subTest(checkFaceNormalGridFunction(grid->leafGridView(), "YaspGrid<2>")); } { using Grid = Dune::YaspGrid<3>; auto grid = Dune::StructuredGridFactory::createCubeGrid({{0,0,0}}, {{1,1,1}}, {{2,2,2}}); grid->globalRefine(2); testSuite.subTest(checkFaceNormalGridFunction(grid->leafGridView(), "YaspGrid<3>")); } { using Grid = Dune::UGGrid<2>; auto grid = Dune::StructuredGridFactory::createSimplexGrid({{0,0}}, {{1,1}}, {{1,1}}); grid->globalRefine(2); testSuite.subTest(checkFaceNormalGridFunction(grid->leafGridView(), "UGGrid<2> (triangles)")); } { using Grid = Dune::UGGrid<2>; auto factory = Dune::GridFactory(); factory.insertVertex({0,0}); factory.insertVertex({0,1}); factory.insertVertex({1,0}); factory.insertVertex({2,2}); factory.insertElement(Dune::GeometryTypes::cube(2), {0,1,2,3}); auto grid = factory.createGrid(); grid->globalRefine(2); testSuite.subTest(checkFaceNormalGridFunction(grid->leafGridView(), "UGGrid<2> (nonaffine rectangles)")); } { using Grid = Dune::UGGrid<3>; auto grid = Dune::StructuredGridFactory::createSimplexGrid({{0,0,0}}, {{1,1,1}}, {{1,1,1}}); grid->globalRefine(2); testSuite.subTest(checkFaceNormalGridFunction(grid->leafGridView(), "UGGrid<2> (triangles)")); } { using Grid = Dune::UGGrid<3>; auto factory = Dune::GridFactory(); factory.insertVertex({0,0,0}); factory.insertVertex({1,0,0}); factory.insertVertex({0,1,0}); factory.insertVertex({1,1,0}); factory.insertVertex({0,0,1}); factory.insertVertex({1,0,1}); factory.insertVertex({0,1,1}); factory.insertVertex({2,2,2}); factory.insertElement(Dune::GeometryTypes::cube(3), {0,1,2,3,4,5,6,7}); auto grid = factory.createGrid(); grid->globalRefine(2); testSuite.subTest(checkFaceNormalGridFunction(grid->leafGridView(), "UGGrid<3> (nonaffine cubes)")); } return testSuite.exit(); } catch ( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/gridfunctiontest.cc000066400000000000000000000041501513634022200301530ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include //#include //#include #include #include #include #include #include #include using namespace Dune; using namespace Dune::Functions; int main (int argc, char* argv[]) try { Dune::MPIHelper::instance(argc, argv); // Generate grid for testing const int dim = 2; typedef YaspGrid GridType; FieldVector l(1); // FieldVector l = {{21.0, 4.0}}; std::array elements = {{10, 10}}; GridType grid(l,elements); // Test whether PQ1FunctionSpaceBasis.hh can be instantiated on the leaf view using GridView = GridType::LeafGridView; using EntitySet = GridViewEntitySet; [[maybe_unused]] auto entitySet = EntitySet(grid.leafGridView()); GridFunction), EntitySet> f; // Since we default-constructed the function it must be invalid. if (f) { std::cerr << "Default-constructed GridFunction object is not invalid!\n"; return 1; } // Assign a test function to the GridFunction object auto testFunction = [](FieldVector x) { return x[0]*x[1]; } ; f = Functions::makeGridViewFunction(testFunction, grid.leafGridView()); // Since we assigned something to f it must be valid now. if (!f) { std::cerr << "Nonempty GridFunction object is not valid!\n"; return 1; } return 0; } catch ( Dune::Exception &e ) { std::cerr << "Dune reported error: " << e << std::endl; return 1; } catch(...) { std::cerr << "Unknown exception thrown!" << std::endl; return 1; } dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/gridfunctiontest.hh000066400000000000000000000126361513634022200301750ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_FUNCTIONS_GRIDFUNCTIONS_TEST_GRIDFUNCTIONTEST_HH #define DUNE_FUNCTIONS_GRIDFUNCTIONS_TEST_GRIDFUNCTIONTEST_HH #include #include #include #include #include #include namespace Dune { namespace Functions { namespace Test { bool checkTrue(bool value, std::string error) { if (not(value)) std::cout << "TEST FAILURE:" << error << std::endl; return value; } template double integrateGridViewFunction(const GridView& gridView, const F& f, unsigned int quadOrder) { static const int dim = GridView::dimension; double integral = 0; auto fLocal = localFunction(f); // Loop over elements and integrate over the function for (const auto& e : elements(gridView)) { auto geometry = e.geometry(); fLocal.bind(e); if (not fLocal.bound()) DUNE_THROW(Exception, "Function is not bound after bind()"); // A quadrature rule const auto& quad = QuadratureRules::rule(e.type(), quadOrder); // Loop over all quadrature points for ( size_t pt=0; pt < quad.size(); pt++ ) { // Position of the current quadrature point in the reference element auto quadPos = quad[pt].position(); // The multiplicative factor in the integral transformation formula auto integrationElement = geometry.integrationElement(quadPos); integral += fLocal(quadPos) * quad[pt].weight() * integrationElement; } fLocal.unbind(); } return integral; } template bool checkGridViewFunction(const GridView& gridView, const F& f, double exactIntegral, unsigned int quadOrder=1) { bool passed = true; double integral; using Dune::Functions::Concept::isGridFunction; using EntitySet = typename F::EntitySet; using Domain = typename EntitySet::GlobalCoordinate; using Range = std::invoke_result_t; std::cout << "Checking raw function f on grid view" << std::endl; passed = checkTrue(isGridFunction(), "Function does not model GridFunction concept"); integral = integrateGridViewFunction(gridView, f, quadOrder); passed = checkTrue(std::abs(integral-exactIntegral) < 1e-10, "Integral is "+std::to_string(integral)+" but should be "+std::to_string(exactIntegral)); std::cout << "Checking GridFunction(f) on grid view" << std::endl; GridFunction f2 = f; passed = checkTrue(isGridFunction(), "Function does not model GridFunction concept"); integral = integrateGridViewFunction(gridView, f2, quadOrder); passed = checkTrue(std::abs(integral-exactIntegral) < 1e-10, "Integral is "+std::to_string(integral)+" but should be "+std::to_string(exactIntegral)); std::cout << "Checking GridViewFunction(f) on grid view" << std::endl; GridViewFunction f3 = f; passed = checkTrue(isGridFunction(), "Function does not model GridFunction concept"); integral = integrateGridViewFunction(gridView, f3, quadOrder); passed = checkTrue(std::abs(integral-exactIntegral) < 1e-10, "Integral is "+std::to_string(integral)+" but should be "+std::to_string(exactIntegral)); std::cout << "Checking makeGridFunction(f) on grid view" << std::endl; auto f4 = makeGridViewFunction(f, gridView); passed = checkTrue(isGridFunction(), "Function does not model GridFunction concept"); integral = integrateGridViewFunction(gridView, f4, quadOrder); passed = checkTrue(std::abs(integral-exactIntegral) < 1e-10, "Integral is "+std::to_string(integral)+" but should be "+std::to_string(exactIntegral)); std::cout << "Checking GridViewFunction(makeGridFunction(f)) on grid view" << std::endl; GridViewFunction f5 = f4; integral = integrateGridViewFunction(gridView, f5, quadOrder); passed = checkTrue(std::abs(integral-exactIntegral) < 1e-10, "Integral is "+std::to_string(integral)+" but should be "+std::to_string(exactIntegral)); std::cout << "Checking default constructed and assigned GridViewFunction on grid view" << std::endl; GridViewFunction f6; f6 = f5; integral = integrateGridViewFunction(gridView, f6, quadOrder); passed = checkTrue(std::abs(integral-exactIntegral) < 1e-10, "Integral is "+std::to_string(integral)+" but should be "+std::to_string(exactIntegral)); std::cout << "Checking reassigned GridViewFunction on grid view" << std::endl; f6 = f3; integral = integrateGridViewFunction(gridView, f6, quadOrder); passed = checkTrue(std::abs(integral-exactIntegral) < 1e-10, "Integral is "+std::to_string(integral)+" but should be "+std::to_string(exactIntegral)); return passed; } } // namespace Test } // namespace Functions } // namespace Dune #endif // DUNE_FUNCTIONS_GRIDFUNCTIONS_TEST_GRIDFUNCTIONTEST_HH dune-functions-2.11.0+dfsg/dune/functions/gridfunctions/test/localfunctioncopytest.cc000066400000000000000000000047731513634022200312260ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include // An initializer of MPI #include // We use exceptions #include // We use exceptions #include #include #include #include #include #include using namespace Dune; template auto checkCopyConstruction(F&& f, const GV& gv) { TestSuite suite; auto localF = localFunction(f); suite.check(not localF.bound(), "Check if LocalFunction was not marked bound too early."); for (const auto& element: elements(gv)) { // bind localF localF.bind(element); suite.check(localF.bound(), "Check if LocalFunction was bound."); auto localF_copy = localF; suite.check(localF_copy.bound(), "Check if copied LocalFunction was bound."); // Test evaluation using Domain = typename std::decay_t::Domain; auto xval = Domain(0); // evaluate the copied localFunction. This gave a segmentation fault earlier auto copy_eval = localF_copy(xval); auto eval = localF(xval); suite.check((copy_eval == eval), "Check if copied and original local function give same result"); } return suite; } int main(int argc, char** argv) { MPIHelper::instance(argc, argv); TestSuite suite; // Grid using GridType =Dune::YaspGrid<2>; auto grid = Dune::StructuredGridFactory ::createCubeGrid({{0,0}}, {{1,1}}, {{2,2}}); // Basis using Basis = Dune::Functions::LagrangeBasis; Basis basis(grid->leafGridView()); // some artificial coefficients using Vector = Dune::BlockVector; Vector x; x.resize(basis.dimension()); auto xbe = Dune::Functions::istlVectorBackend(x); x=1.0; // create a discrete global basis function auto f = Dune::Functions::makeDiscreteGlobalBasisFunction(basis, xbe); // test with DiscreteGlobalBasisFunction suite.subTest(checkCopyConstruction(f, grid->leafGridView())); return suite.exit(); } dune-functions-2.11.0+dfsg/dune/python/000077500000000000000000000000001513634022200177255ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/python/CMakeLists.txt000066400000000000000000000003461513634022200224700ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory(functions) add_subdirectory(test) dune-functions-2.11.0+dfsg/dune/python/functions/000077500000000000000000000000001513634022200217355ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/python/functions/CMakeLists.txt000066400000000000000000000006441513634022200245010ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later set(HEADERS discretefunction.hh foreachboundarydof.hh globalbasis.hh hierarchicvectorwrapper.hh interpolate.hh subspacebasis.hh tree.hh ) install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/python/functions) dune-functions-2.11.0+dfsg/dune/python/functions/discretefunction.hh000066400000000000000000000066041513634022200256340ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_PYTHON_FUNCTIONS_DISCRETEFUNCTION_HH #define DUNE_PYTHON_FUNCTIONS_DISCRETEFUNCTION_HH #include #include #include #include #include #include namespace Dune { namespace Python { // HierarchicPythonVector // ---------------------- template< class K > using HierarchicPythonVector = HierarchicVectorWrapper< PythonVector< K >, K >; namespace detail { // registerDiscreteFunctionConstructor // ----------------------------------- template< class Basis, class K, class Range, class... options > inline static void registerDiscreteFunctionConstructor ( pybind11::class_< Dune::Functions::DiscreteGlobalBasisFunction< Basis, HierarchicPythonVector< K >, Dune::Functions::HierarchicNodeToRangeMap, Range >, options... > &cls, PriorityTag< 1 > ) { using pybind11::operator""_a; typedef HierarchicPythonVector< K > Vector; typedef Dune::Functions::HierarchicNodeToRangeMap NodeToRangeMap; typedef Dune::Functions::DiscreteGlobalBasisFunction< Basis, Vector, NodeToRangeMap, Range > DiscreteFunction; cls.def( pybind11::init( [] ( const Basis &basis, pybind11::buffer dofVector ) { auto nodeToRangeMapPtr = std::make_shared(); std::shared_ptr basisPtr = Dune::wrap_or_move( basis ); auto vectorPtr = std::make_shared< const Vector >( dofVector ); return new DiscreteFunction( basisPtr, vectorPtr, nodeToRangeMapPtr ); } ), pybind11::keep_alive< 1, 2 >(), pybind11::keep_alive< 1, 3 >(), "basis"_a, "dofVector"_a ); } template< class DiscreteFunction, class... options > inline static void registerDiscreteFunctionConstructor ( pybind11::class_< DiscreteFunction, options... > &cls, PriorityTag< 0 > ) {} } // namespace detail // registerDiscreteFunction // ------------------------ template< class Basis, class Vector, class NodeToRangeMap, class Range, class... options > inline static void registerDiscreteFunction ( pybind11::module module, pybind11::class_< Dune::Functions::DiscreteGlobalBasisFunction< Basis, Vector, NodeToRangeMap, Range >, options... > &cls ) { typedef Dune::Functions::DiscreteGlobalBasisFunction< Basis, Vector, NodeToRangeMap, Range > DiscreteFunction; registerGridFunction( module, cls ); detail::registerDiscreteFunctionConstructor( cls, PriorityTag< 42 >() ); cls.def_property_readonly( "basis", [] ( const DiscreteFunction &self ) -> const Basis & { return self.basis(); }, pybind11::keep_alive< 0, 1 >() ); cls.def_property_readonly( "grid", [] ( const DiscreteFunction &self ) -> const typename Basis::GridView & { return self.basis().gridView(); }, pybind11::keep_alive< 0, 1 >() ); } } // namespace Python } // namespace Dune #endif // #ifndef DUNE_PYTHON_FUNCTIONS_DISCRETEFUNCTION_HH dune-functions-2.11.0+dfsg/dune/python/functions/globalbasis.hh000066400000000000000000000260561513634022200245510ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_PYTHON_FUNCTIONS_GLOBALBASIS_HH #define DUNE_PYTHON_FUNCTIONS_GLOBALBASIS_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace PYBIND11_NAMESPACE { namespace detail { /** \brief Implement type_caster for Dune::ReservedVector * * This uses the standard array_caster implementation for resizeable types. * * \warning array_caster is not documented officially. But as long as we have * a copy of pybind11 vendored in dune-common using it should be fine. */ template struct type_caster > : array_caster, T, true> {}; } } namespace Dune { namespace Python { namespace Impl { template static constexpr bool hasStaticDegree() { return requires() { T::degree(); }; } // Utility to compute an appropriate range dimension for grid function associated to a node template constexpr std::size_t nodeRangeDim() { using namespace Dune::TypeTree::Concept; if constexpr (LeafTreeNode) return Node::FiniteElement::Traits::LocalBasisType::Traits::dimRange; else if constexpr (StaticDegreeInnerTreeNode and (not UniformInnerTreeNode) ) return Dune::unpackIntegerSequence([&](auto... i) { return (nodeRangeDim>() + ...); }, std::make_index_sequence{}); else if constexpr (UniformInnerTreeNode and hasStaticDegree()) return Node::degree() * nodeRangeDim>(); else static_assert(Dune::AlwaysFalse::value, "Dynamic power bases are not supported through the python interface."); } } // end namespace Impl template struct LocalViewWrapper : public Basis::LocalView { typedef typename Basis::LocalView Base; typedef typename Base::Element EntityType; LocalViewWrapper(const Basis &b) : Base(b) {} LocalViewWrapper(const Base &lv) : Base(lv) {} Base& base() { return *this; } const Base& base() const { return *this; } std::vector index(int idx) const { // call index in the base class auto ind = base().index(idx); std::vector ret(ind.size()); for (std::size_t i=0;i(); base().bind(entity); } void unbind ( ) { base().unbind(); obj_.release(); } pybind11::object obj_; }; // The type of the value of a global finite element function at a particular point template struct RangeType { using type = Dune::FieldVector< K, n >; static void registerRange(pybind11::module scope) { registerFieldVector(scope); } }; template struct RangeType { using type = K; static void registerRange(pybind11::module scope) {} // nothing to register, as K is a basic type }; template< class GlobalBasis, class... options, class ConstructCall, bool hasUpdate> void registerBasisType ( pybind11::module module, pybind11::class_< GlobalBasis, options... > &cls, ConstructCall constructCall, std::bool_constant) { using pybind11::operator""_a; using GridView = typename GlobalBasis::GridView; using RawLocalView = typename GlobalBasis::LocalView; using RawTree = typename GlobalBasis::LocalView::Tree; using namespace Dune::TypeTree::Concept; const std::size_t dimRange = Impl::nodeRangeDim< RawTree >(); const std::size_t dimWorld = GridView::dimensionworld; cls.def( pybind11::init( constructCall ), pybind11::keep_alive< 1, 2 >() ); cls.def( "__len__", [](const GlobalBasis& self) { return self.dimension(); } ); cls.def("size", pybind11::overload_cast<>(&GlobalBasis::size, pybind11::const_), "Return number of possible values for next position in empty multi index" ); cls.def("size", pybind11::overload_cast(&GlobalBasis::size, pybind11::const_), "Return number of possible values for next position in multi index"); if constexpr (hasUpdate) { cls.def_property( "gridView", [](const GlobalBasis& basis) { return basis.gridView(); }, [](GlobalBasis& basis, const GridView& gridView) { basis.update(gridView); }); } else { cls.def_property_readonly( "gridView", [](const GlobalBasis& basis) { return basis.gridView(); }); } using LocalView = LocalViewWrapper< GlobalBasis >; using Tree = typename LocalView::Tree; auto includes = IncludeFiles{"dune/python/functions/globalbasis.hh"}; auto lv = insertClass< LocalView >( module, "LocalView", GenerateTypeName("Dune::Python::LocalViewWrapper", MetaType()), includes).first; lv.def( "bind", &LocalView::bind ); lv.def( "unbind", &LocalView::unbind ); lv.def( "element", &LocalView::element ); lv.def( "index", [] ( const LocalView &localView, int index ) { return localView.index( index ); }); lv.def( "__len__", [] ( LocalView &self ) -> int { return self.size(); } ); lv.def( "size", [] ( LocalView &self ) -> int { return self.size(); } ); lv.def( "maxSize", [] ( LocalView &self ) -> int { return self.maxSize(); } ); Functions::registerTree(lv); lv.def("tree", [](const LocalView& view) { return Dune::stackobject_to_shared_ptr(view.tree()); }); cls.def( "localView", [] ( const GlobalBasis &self ) -> LocalView { return self.localView(); }, pybind11::keep_alive< 0, 1 >() ); cls.def_property_readonly( "dimension", [] ( const GlobalBasis &self ) -> int { return self.dimension(); } ); // Register the 'interpolate' method // TODO: Currently we only register the method for cases where either scalars or FieldVector // are reasonable value types for functions to be interpolated. This excludes various not-very-exotic // composite bases. Support for them is planned for the future. if constexpr (dimRange==1) { cls.def( "interpolate", &Dune::Python::Functions::interpolate ); cls.def( "interpolate", &Dune::Python::Functions::interpolate ); cls.def( "interpolate", &Dune::Python::Functions::interpolate ); } else if constexpr (LeafTreeNode or UniformInnerTreeNode) { cls.def( "interpolate", &Dune::Python::Functions::interpolate > ); cls.def( "interpolate", &Dune::Python::Functions::interpolate > ); cls.def( "interpolate", &Dune::Python::Functions::interpolate > ); } // Register various grid function types // TODO: As we do not currently have support for nested range types, // we register grid function types only for 'simple' bases. // A more general implementation is planned for the future. if constexpr (LeafTreeNode or UniformInnerTreeNode) { using Range = typename RangeType< double, dimRange >::type; RangeType< double, dimRange >::registerRange(module); using Domain = Dune::FieldVector< double, dimWorld >; registerFieldVector(module); using DiscreteFunction = Dune::Functions::DiscreteGlobalBasisFunction< GlobalBasis, HierarchicPythonVector< double >, Dune::Functions::HierarchicNodeToRangeMap, Range >; // register the HierarchicPythonVector Dune::Python::addToTypeRegistry>( GenerateTypeName("Dune::Python::HierarchicPythonVector", MetaType()), {"dune/python/functions/discretefunction.hh"} ); // and add the DiscreteFunction to our module auto clsDiscreteFunction = insertClass< DiscreteFunction >( module, "DiscreteFunction", GenerateTypeName( "Dune::Functions::DiscreteGlobalBasisFunction", MetaType(), MetaType>(), "Dune::Functions::HierarchicNodeToRangeMap", MetaType() ), includes); // register the GridViewFunction and register the implicit conversion Dune::Python::addToTypeRegistry(GenerateTypeName(className())); using GridViewFunction = Dune::Functions::GridViewFunction; auto clsGridViewFunction = insertClass< GridViewFunction >( module, "GridViewFunction", GenerateTypeName( "Dune::Functions::GridViewFunction", MetaType(), MetaType() ), includes); clsGridViewFunction.first.def(pybind11::init()); pybind11::implicitly_convertible(); registerDiscreteFunction( module, clsDiscreteFunction.first ); cls.def("asFunction", [] ( GlobalBasis &self, pybind11::buffer dofVector ) { return new DiscreteFunction( self, HierarchicPythonVector(dofVector), Dune::Functions::HierarchicNodeToRangeMap()); }, pybind11::keep_alive< 0, 1 >(), pybind11::keep_alive< 0, 2 >(), "dofVector"_a ); } } template< class GlobalBasis, class... options > DUNE_EXPORT void registerGlobalBasis ( pybind11::module module, pybind11::class_< GlobalBasis, options... > &cls ) { using GridView = typename GlobalBasis::GridView; auto construct = [] ( const GridView &gridView ) { return new GlobalBasis( gridView ); }; registerBasisType ( module, cls, construct, std::true_type{} ); } } // namespace Python } // namespace Dune #endif // #ifndef DUNE_PYTHON_FUNCTIONS_GLOBALBASIS_HH dune-functions-2.11.0+dfsg/dune/python/functions/hierarchicvectorwrapper.hh000066400000000000000000000056041513634022200272100ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_PYTHON_FUNCTIONS_HIERARCHICVECTORWRAPPER_HH #define DUNE_PYTHON_FUNCTIONS_HIERARCHICVECTORWRAPPER_HH #include #include #include namespace Dune { namespace Python { // HierarchicalVectorWrapper // ------------------------- template< class V, class C, class Holder = V > class HierarchicVectorWrapper { typedef HierarchicVectorWrapper< V, C, Holder > This; public: typedef V Vector; typedef typename Vector::size_type size_type; template< class MultiIndex > using Entry = C; template< class... Args, std::enable_if_t< std::is_constructible_v< Holder, Args &&... >, int > = 0 > HierarchicVectorWrapper ( Args &&... args ) : holder_( std::forward< Args >( args )... ) {} template< class MultiIndex > const Entry< MultiIndex > &operator[] ( const MultiIndex &index ) const { return Dune::Functions::hybridMultiIndexAccess< const Entry< MultiIndex > & >( vector(), index ); } template< class MultiIndex > Entry< MultiIndex > &operator[] ( const MultiIndex &index ) { return Dune::Functions::hybridMultiIndexAccess< Entry< MultiIndex > & >( vector(), index ); } template< class MultiIndex > const Entry< MultiIndex > &operator() ( const MultiIndex &index ) const { return (*this)[ index ]; } template< class MultiIndex > Entry< MultiIndex > &operator() ( const MultiIndex &index ) { return (*this)[ index ]; } const Vector &vector () const { return get( holder_ ); } Vector &vector () { return get( holder_ ); } private: template< class H > static decltype( static_cast< const Vector & >( std::declval< const H & >() ) ) get ( const H &holder ) { return static_cast< const Vector & >( holder ); } template< class H > static decltype( static_cast< Vector & >( std::declval< H & >() ) ) get ( H &holder ) { return static_cast< Vector & >( holder ); } template< class H > static decltype( static_cast< const Vector & >( *std::declval< const H & >() ) ) get ( const H &holder ) { return static_cast< const Vector & >( *holder ); } template< class H > static decltype( static_cast< Vector & >( *std::declval< H & >() ) ) get ( H &holder ) { return static_cast< Vector & >( *holder ); } Holder holder_; }; } // namespace Python } // namespace Dune #endif // #ifndef DUNE_PYTHON_FUNCTIONS_HIERARCHICVECTORWRAPPER_HH dune-functions-2.11.0+dfsg/dune/python/functions/interpolate.hh000066400000000000000000000020461513634022200246060ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_PYTHON_FUNCTIONS_INTERPOLATE_HH #define DUNE_PYTHON_FUNCTIONS_INTERPOLATE_HH #include #include #include #include namespace Dune { namespace Python { namespace Functions { template void interpolate(const Basis& basis, pybind11::array_t x, const std::function::Geometry::GlobalCoordinate)>& f) { if (x.shape(0) != basis.size()) throw std::runtime_error("Coefficient vector has wrong size"); auto x1 = x.template mutable_unchecked<1>(); auto x2 = Dune::Functions::istlVectorBackend(x1); interpolate(basis, x2, f); } } /* namespace Functions */ } /* namespace Python */ } /* namespace Dune */ #endif dune-functions-2.11.0+dfsg/dune/python/functions/subspacebasis.hh000066400000000000000000000023121513634022200251030ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_PYTHON_FUNCTIONS_SUBSPACEBASIS_HH #define DUNE_PYTHON_FUNCTIONS_SUBSPACEBASIS_HH #include #include #include #include #include namespace Dune { namespace Python { template< class SubspaceBasis, class... options > DUNE_EXPORT void registerSubspaceBasis ( pybind11::module module, pybind11::class_< SubspaceBasis, options... > &cls ) { using RootBasis = typename SubspaceBasis::RootBasis; // Use default constructed TreePath. This requires, that // the PrefixPath template parameter of SubspaceBasis is // a fully static tree path. auto construct = [] ( const RootBasis &rootBasis ) { return new SubspaceBasis( rootBasis, {} ); }; registerBasisType ( module, cls, construct, std::false_type{} ); } } // namespace Python } // namespace Dune #endif // #ifndef DUNE_PYTHON_FUNCTIONS_SUBSPACEBASIS_HH dune-functions-2.11.0+dfsg/dune/python/functions/tree.hh000066400000000000000000000141411513634022200232160ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #ifndef DUNE_PYTHON_FUNCTIONS_TREE_HH #define DUNE_PYTHON_FUNCTIONS_TREE_HH #include #include #include #include #include // for void_t, in for C++17 #include #include #include #include #include #include #include namespace Dune { namespace Python { namespace Functions { namespace detail { template requires Dune::TypeTree::Concept::StaticDegreeInnerTreeNode and (not Dune::TypeTree::Concept::UniformInnerTreeNode) void registerTree_(pybind11::handle scope, const char* name = "Tree"); template requires Dune::TypeTree::Concept::LeafTreeNode void registerTree_(pybind11::handle scope, const char* name = "Tree"); template requires Dune::TypeTree::Concept::UniformInnerTreeNode void registerTree_(pybind11::handle scope, const char* name = "Tree"); template void registerTreeCommon(pybind11::class_>& cls) { using namespace Dune::TypeTree::Concept; /* dune-typetree properties */ cls.def_property_readonly_static("isComposite", [](pybind11::object) { return StaticDegreeInnerTreeNode and (not UniformInnerTreeNode); }); cls.def_property_readonly_static("isLeaf", [](pybind11::object) { return LeafTreeNode; }); cls.def_property_readonly_static("isPower", [](pybind11::object) { return UniformInnerTreeNode; }); cls.def("degree", [](pybind11::object) -> std::size_t { return Tree::degree(); }); cls.def( "__len__", [] (const Tree& self) -> int { return self.size(); } ); cls.def( "size", [] (const Tree& self) -> int { return self.size(); } ); cls.def("localIndex", [](const Tree& self, unsigned int index) { return self.localIndex(index); }); cls.def("treeIndex", [](const Tree& self, unsigned int index) { return self.treeIndex(); }); } template auto childAccessor() { return [](Tree& tree) { return pybind11::cast(tree.template child(index_constant{})); }; } template std::array< std::function, sizeof...(I) > childAccessors(std::index_sequence) { static_assert(sizeof...(I) == Tree::degree(),"size of index sequence does not match degree of tree"); return { (childAccessor())... }; } template void registerTreeChildAccessor(pybind11::class_>& cls) { const auto accessors = childAccessors(std::make_index_sequence{}); cls.def( "child", [accessors](Tree& tree, std::size_t i) { return accessors.at(i)(tree); }, pybind11::arg("i")); } template requires Dune::TypeTree::Concept::StaticDegreeInnerTreeNode and (not Dune::TypeTree::Concept::UniformInnerTreeNode) void registerTree_(pybind11::handle scope, const char* name) { if( !pybind11::already_registered< Tree >() ) { pybind11::class_> cls(scope, name); registerTreeCommon(cls); // static variable cls is captured automatically - explicit capture fails with clang: // fatal error: 'cls' cannot be captured because it does not have automatic storage duration Hybrid::forEach(std::make_index_sequence{}, [&cls](auto i) { using SubTree = Dune::TypeTree::Child; std::string subName = std::string("Tree") + std::to_string(i); if( !pybind11::already_registered< SubTree >() ) registerTree_(cls, subName.c_str()); }); registerTreeChildAccessor(cls); } } template> struct hasFiniteElement : std::false_type {}; template struct hasFiniteElement> : std::true_type {}; template< typename Tree, std::enable_if_t< !hasFiniteElement::value, int > = 0> void registerFiniteElementProperty(pybind11::class_< Tree, std::shared_ptr >&) { /* Nothing. */ } template< typename Tree, std::enable_if_t< hasFiniteElement::value, int > = 0> void registerFiniteElementProperty(pybind11::class_< Tree, std::shared_ptr >& cls) { // this should probably be fixed in dune-localfunctions if (!pybind11::already_registered< typename Tree::FiniteElement >()) registerLocalFiniteElement(cls); cls.def( "finiteElement", &Tree::finiteElement, pybind11::return_value_policy::reference_internal ); } // fatal error: template parameter redefines default argument (clang) // using static to avoid double registration doesn't work with clang // because statics remain local to module and are not made unique between modules // If the class is needed use the TypeRegistry to make this work smoothly template requires Dune::TypeTree::Concept::LeafTreeNode void registerTree_(pybind11::handle scope, const char* name) { if( !pybind11::already_registered< Tree >() ) { pybind11::class_< Tree, std::shared_ptr > cls(scope, name); registerTreeCommon(cls); registerFiniteElementProperty(cls); } } template requires Dune::TypeTree::Concept::UniformInnerTreeNode void registerTree_(pybind11::handle scope, const char* name) { if( !pybind11::already_registered< Tree >() ) { pybind11::class_< Tree, std::shared_ptr > cls(scope, name); registerTreeCommon(cls); registerTree_>(cls); registerTreeChildAccessor(cls); } } } /* namespace detail */ template void registerTree(pybind11::handle scope, const char* name = "Tree") { detail::registerTree_(scope, name); } } /* namespace Functions */ } /* namespace Python */ } /* namespace Dune */ #endif dune-functions-2.11.0+dfsg/dune/python/test/000077500000000000000000000000001513634022200207045ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/dune/python/test/CMakeLists.txt000066400000000000000000000030231513634022200234420ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later dune_python_add_test(NAME pyfunction SCRIPT function.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} LABELS python) dune_python_add_test(NAME pydiscretefunction SCRIPT discretefunction.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} LABELS python) dune_python_add_test(NAME pycompositebasistest SCRIPT compositebasistest.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} LABELS python) dune_python_add_test(NAME pylagrangebasistest SCRIPT lagrangebasistest.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} LABELS python) dune_python_add_test(NAME pynedelecbasistest SCRIPT nedelecbasistest.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} LABELS python) dune_python_add_test(NAME pyraviartthomasbasistest SCRIPT raviartthomasbasistest.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} LABELS python) dune_python_add_test(NAME pysubspacebasistest SCRIPT subspacebasistest.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} LABELS python) dune-functions-2.11.0+dfsg/dune/python/test/basistest.py000066400000000000000000000125061513634022200232630ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later from array import array # Check consistency of basis.size(prefix) def checkBasisSizeConsistency(basis, multiIndexSet): # Based on the index tree, build a map that contains all possible # prefixes and map each prefix to the size (of the subsequent digit). prefixSet = dict() for index in multiIndexSet: prefix = []; for digit in index: if not tuple(prefix) in prefixSet: prefixSet[tuple(prefix)] = 0 prefixSet[tuple(prefix)] = max(prefixSet[tuple(prefix)], digit+1); prefix.append(digit); prefixSet[tuple(prefix)] = 0; # Now check for all prefixes, if the size computed from the # index tree is consistent with basis.size(prefix). for prefix, size in prefixSet.items(): prefixSize = basis.size(prefix); if prefixSize != size: raise IndexError("basis.size(" + str(prefix) + ") = " + str(prefixSize) + ", but should be " + str(size)) # Check indices of basis: # - First store the whole index tree in a set # - Check if this corresponds to a consistent index tree # - Check if index tree is consistent with basis.size(prefix) and basis.dimension() def checkBasisIndices(basis): multiIndexSet = set() localView = basis.localView() gridView = basis.gridView for element in gridView.elements: localView.bind(element) for i in range(len(localView)): multiIndex = localView.index(i) for digit in multiIndex: if digit < 0: raise IndexError(str(element) + ": Global multi-index contains negative entry for shape function " + str(i)) multiIndexSet.add(tuple(multiIndex)) # TODO: Implement this! # checkBasisIndexTreeConsistency(multiIndexSet); checkBasisSizeConsistency(basis, multiIndexSet); if (basis.dimension != len(multiIndexSet)): raise ValueError("basis.dimension() does not equal the total number of basis functions.") # Check the methods of individual nodes of a function bases tree # This method calls itself recursively to traverse the entire tree. def checkTreeNode(node, localIndices): if node.isLeaf: if node.size() != node.finiteElement().size(): raise ValueError("Size of leaf node and finite element are different.") for i in range(node.size()): if node.localIndex(i) >= len(localIndices): raise ValueError("Local index exceeds localView size.") # Log that we have seen this local index localIndices[node.localIndex(i)] += 1 elif node.isPower or node.isComposite: # TODO: Test features of non-leaf nodes # Recursively test the children for i in range(node.degree()): checkTreeNode(node.child(i), localIndices) else: raise NotImplementedError("Found an unsupported node type") def checkLocalView(basis, localView, checkLocalIndexSetCompleteness): if (localView.size() > localView.maxSize()): raise ValueError("localView.size() is " + str(localView.size()) + " but localView.maxSize() is " + str(localView.maxSize())) tree1 = localView.tree() tree2 = localView.tree() if (tree1 != tree2): raise ValueError("Multiple calls to localView.tree() do not hand out the same tree.") if (tree1.isLeaf and tree1.isPower): raise ValueError("Tree claims to be both 'leaf' and 'power'.") if (tree1.isLeaf and tree1.isComposite): raise ValueError("Tree claims to be both 'leaf' and 'composite'.") # Recursively test all nodes in the tree localIndices = array('I', [0] * localView.size()) checkTreeNode(tree1, localIndices) # Check that each local index appeared exactly once. if checkLocalIndexSetCompleteness: for localIndex in localIndices: if localIndex < 1: raise ValueError("Local index " + str(localIndex) + " did not appear.") elif localIndex > 1: raise ValueError("Local index " + str(localIndex) + " appeared multiple times.") # Perform tests that don't modify the basis def checkConstBasis(basis, *, checkLocalIndexSetCompleteness): # Perform all local tests localView = basis.localView() gridView = basis.gridView for element in gridView.elements: localView.bind(element) # Check the 'element' method boundElement = localView.element() if boundElement != element: raise ValueError("LocalView object does not yield the element it is bound to.") checkLocalView(basis,localView, checkLocalIndexSetCompleteness) # Perform global index tests checkBasisIndices(basis) # Perform continuity check # TODO def checkBasis(basis, *, checkLocalIndexSetCompleteness=True): # Check the basis checkConstBasis(basis, checkLocalIndexSetCompleteness=checkLocalIndexSetCompleteness) # Check whether the basis can be copied copiedBasis = basis checkConstBasis(copiedBasis, checkLocalIndexSetCompleteness=checkLocalIndexSetCompleteness) # Can the 'update' method be called? gridView = basis.gridView # TODO: 'update' method not exposed by the interface yet! #basis.update(gridView) dune-functions-2.11.0+dfsg/dune/python/test/compositebasistest.py000066400000000000000000000026471513634022200252130ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later import dune.grid import dune.functions as functions import basistest # The general test suite for function space bases # Test all currently supported global bases in dimension `dimension`. # Only the size of the global bases are tested. def test(dimension): lowerLeft = [0] * dimension upperRight = [1] * dimension elements = [4] * dimension grid = dune.grid.structuredGrid(lowerLeft,upperRight,elements) # Test a basis consisting of three identical scalar-valued children basis = functions.defaultGlobalBasis(grid, functions.Composite(functions.Lagrange(order=1), functions.Lagrange(order=1), functions.Lagrange(order=1))) basistest.checkBasis(basis) # Test a composite of a power node and a Lagrange basis basis = functions.defaultGlobalBasis(grid, functions.Composite(functions.Power(functions.Lagrange(order=2),exponent=dimension), functions.Lagrange(order=1))) basistest.checkBasis(basis) # Run tests for grids of dimension 2 and 3 test(2) test(3) dune-functions-2.11.0+dfsg/dune/python/test/discretefunction.py000066400000000000000000000014431513634022200246300ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later from dune.grid import structuredGrid import dune.functions import numpy as np # create a simple grid grid = structuredGrid([0,0],[1,1],[10,10]) # simple P1 basis basis = dune.functions.defaultGlobalBasis(grid, dune.functions.Lagrange(order=1)) # boring coefficient vector coeff = np.zeros( len(basis) ) # discrete function over the grid f = basis.asFunction(coeff) # extract the original grid grid2 = f.grid # deduce type of discrete function DiscreteFunction = type(f) # check constructor of DiscreteFunction f2 = DiscreteFunction(basis, coeff) # check if there are still 100 squares assert( grid2.size(0) == 100 ) dune-functions-2.11.0+dfsg/dune/python/test/function.py000066400000000000000000000065301513634022200231070ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later import numpy as np import dune.grid from dune.grid import cartesianDomain from dune.grid import yaspGrid import dune.functions from dune.generator import algorithm import io # we try to pass different dune-functions functions from the python side to C++ code=""" #include #include #include #include #include #include #include #include #include static const int dim = 2; using Grid = Dune::YaspGrid>; using GV = typename Grid::LeafGridView; using Signature = double(Dune::FieldVector); using GridViewFunction = Dune::Functions::GridViewFunction; template std::string callScalar(const Dune::Functions::DiscreteGlobalBasisFunction & f) { return "DiscreteGlobalBasisFunction<...>"; } std::string callScalar(const GridViewFunction & f) { return "GridViewFunction"; } std::string callVector(const std::vector & f) { return "vector>"; } """ class CppTypeInfo: def __init__(self,name,includes): self.cppTypeName = name self.cppIncludes = includes scalarTypeName = "GridViewFunction" vectorTypeName = "std::vector" callScalar = algorithm.load('callScalar', io.StringIO(code), CppTypeInfo(scalarTypeName,[])); callVector = algorithm.load('callVector', io.StringIO(code), CppTypeInfo(vectorTypeName,[])); # number of grid elements (in one direction) gridSize = 4 # create a YaspGrid of the unit square domain = cartesianDomain([0,0],[1,1],[gridSize,gridSize]) grid = yaspGrid(domain, dimgrid=2) # create a nodal Lagrange FE basis of order 1 and order 2 basis1 = dune.functions.defaultGlobalBasis(grid, dune.functions.Lagrange(order=1)) basis2 = dune.functions.defaultGlobalBasis(grid, dune.functions.Lagrange(order=2)) # create a DOF vectors N1 = len(basis1) x1 = np.ndarray(N1) N2 = len(basis2) x2 = np.ndarray(N2) # interpolate data basis1.interpolate(x1, lambda x : np.linalg.norm(x-np.array([0.5,0.5]))-0.3) basis2.interpolate(x2, lambda x : np.linalg.norm(x-np.array([0.5,0.5]))-0.3) # create a grid function f1 = basis1.asFunction(x1) f2 = basis2.asFunction(x2) # calling the concrete interface requires the lagrange basis header incLagrange = "#include \n" callConcrete1 = algorithm.load('callScalar', io.StringIO(incLagrange + code), f1); callConcrete2 = algorithm.load('callScalar', io.StringIO(incLagrange + code), f2); print ("Calling into C++") print ("Called as " + callConcrete1(f1)) print ("Called as " + callConcrete2(f2)) # try to call as GridViewFunction print ("Called as " + callScalar(f1)) print ("Called as " + callScalar(f2)) print ("Called as " + callVector([f1,f1])) print ("Called as " + callVector([f1,f2])) print ("Called as " + callVector([f2,f2])) dune-functions-2.11.0+dfsg/dune/python/test/interpolatetest.py000066400000000000000000000043241513634022200245070ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later import numpy as np import dune.grid from dune.grid import cartesianDomain from dune.grid import yaspGrid import dune.functions from dune.generator import algorithm from io import StringIO # Compute a mask vector identifying only the DOFs # contained in the subspace. def subspaceMask(basis): mask = np.zeros(len(basis)) localView = basis.localView() for element in basis.gridView.elements: localView.bind(element) tree = localView.tree() treeSize = len(tree) treeOffset = tree.localIndex(0) for i in range(treeOffset, treeOffset + treeSize): mask[localView.index(i)[0]] = 1 return mask # Manually convert function to std::function def asStdFunction(f): code=""" #include #include #include template auto run(F f) { using Range = typename F::Range; using Domain = typename F::Domain; return std::function(std::move(f)); } """ return dune.generator.algorithm.run("run",StringIO(code), f) # Check if individual basis functions are interpolated correctly def checkBasisFunctionInterpolation(basis): mask = subspaceMask(basis) coeffTol = 1e-10; ei = np.zeros(len(basis)) y = np.zeros(len(basis)) for i in range(len(basis)): ei[i] = 1 ei *= mask f = asStdFunction(basis.asFunction(ei)) basis.interpolate(y,f) if (np.linalg.norm(y-ei) > coeffTol): raise ValueError("FE basis function is not interpolated correctly.") ei[i] = 0 # Test interpolation of constant function # Since we cannot deduce a range type automatically, # a prototype needs to be passed. def checkConstantInterpolation(basis, rangePrototype): mask = subspaceMask(basis) coeffTol = 1e-10; one = rangePrototype one *= 0 one += 1 f = lambda x : one x = np.zeros(len(basis)) basis.interpolate(x,f) if (np.linalg.norm(x-mask) > coeffTol): raise ValueError("Interpolation of constant function yields wrong result.") dune-functions-2.11.0+dfsg/dune/python/test/lagrangebasistest.py000066400000000000000000000033221513634022200247600ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later import numpy as np import dune.grid import dune.functions as functions import basistest # The general test suite for function space bases import interpolatetest # The general test suite for interpolation into bases # Test all currently supported global bases in dimension `dimension`. # Only the size of the global bases are tested. def test(dimension): lowerLeft = [-1] * dimension upperRight = [1] * dimension elements = [3] * dimension grid = dune.grid.structuredGrid(lowerLeft,upperRight,elements) # Test a first-order Lagrange basis basis1 = functions.defaultGlobalBasis(grid, functions.Lagrange(order=1)) basistest.checkBasis(basis1) # Test a second-order Lagrange basis basis2 = functions.defaultGlobalBasis(grid, functions.Lagrange(order=2)) basistest.checkBasis(basis2) # Test a vector-value Lagrange basis basisPower = functions.defaultGlobalBasis(grid, functions.Power(functions.Lagrange(order=1),exponent=dimension)) basistest.checkBasis(basisPower) # Test interpolation of constant functions interpolatetest.checkConstantInterpolation(basis1, 0) interpolatetest.checkConstantInterpolation(basis2, 0) interpolatetest.checkConstantInterpolation(basisPower, np.zeros(dimension)) # Test interpolation of all basis functions interpolatetest.checkBasisFunctionInterpolation(basis1) interpolatetest.checkBasisFunctionInterpolation(basis2) interpolatetest.checkBasisFunctionInterpolation(basisPower) # Run tests for grids of dimension 2 and 3 test(2) test(3) dune-functions-2.11.0+dfsg/dune/python/test/nedelecbasistest.py000066400000000000000000000007111513634022200245760ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later import dune.grid import dune.functions import basistest # The general test suite for function space bases grid = dune.grid.structuredGrid([0,0],[1,1],[5,5]) basis = dune.functions.defaultGlobalBasis(grid, dune.functions.Nedelec(kind=1,order=1)) basistest.checkBasis(basis) dune-functions-2.11.0+dfsg/dune/python/test/raviartthomasbasistest.py000066400000000000000000000007111513634022200260630ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later import dune.grid import dune.functions import basistest # The general test suite for function space bases grid = dune.grid.structuredGrid([0,0],[1,1],[5,5]) basis = dune.functions.defaultGlobalBasis(grid, dune.functions.Nedelec(kind=1,order=1)) basistest.checkBasis(basis) dune-functions-2.11.0+dfsg/dune/python/test/subspacebasistest.py000066400000000000000000000064161513634022200250140ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later import numpy as np import dune.grid import dune.functions as functions from dune.functions import defaultGlobalBasis, subspaceBasis, Lagrange, Power, RaviartThomas, Composite import basistest # The general test suite for function space bases import interpolatetest # The general test suite for interpolation into bases # Test all currently supported global bases in dimension `dimension`. # Only the size of the global bases are tested. def test(dimension): lowerLeft = [-1] * dimension upperRight = [1] * dimension elements = [1] * dimension grid = dune.grid.structuredGrid(lowerLeft,upperRight,elements) # Test with first-order Lagrange basis vector field basis1 = defaultGlobalBasis(grid, Power(Lagrange(order=1), dimension)) for i in range(dimension): basis1_i = subspaceBasis(basis1, i) basistest.checkBasis(basis1_i, checkLocalIndexSetCompleteness=False) interpolatetest.checkConstantInterpolation(basis1_i, 0) interpolatetest.checkBasisFunctionInterpolation(basis1_i) # Test with lowest order Taylor-Hood basis taylorHoodBasis = defaultGlobalBasis(grid, Composite(Power(Lagrange(order=2), dimension), Lagrange(order=1))) # Check velocity subspace velocityBasis = subspaceBasis(taylorHoodBasis, 0) basistest.checkBasis(velocityBasis, checkLocalIndexSetCompleteness=False) interpolatetest.checkConstantInterpolation(velocityBasis, np.zeros(dimension)) interpolatetest.checkBasisFunctionInterpolation(velocityBasis) # Check pressure subspace pressureBasis = subspaceBasis(taylorHoodBasis, 1) basistest.checkBasis(pressureBasis, checkLocalIndexSetCompleteness=False) interpolatetest.checkConstantInterpolation(pressureBasis, 0) interpolatetest.checkBasisFunctionInterpolation(pressureBasis) # Check velocity component subspaces obtained by passing multiple indices at once for i in range(dimension): velocityBasis_i = subspaceBasis(taylorHoodBasis, 0, i) basistest.checkBasis(velocityBasis_i, checkLocalIndexSetCompleteness=False) interpolatetest.checkConstantInterpolation(velocityBasis_i, 0) interpolatetest.checkBasisFunctionInterpolation(velocityBasis_i) # Check velocity component subspaces obtained via nested subspaceBasis call for i in range(dimension): velocityBasis_i = subspaceBasis(velocityBasis, i) basistest.checkBasis(velocityBasis_i, checkLocalIndexSetCompleteness=False) interpolatetest.checkConstantInterpolation(velocityBasis_i, 0) interpolatetest.checkBasisFunctionInterpolation(velocityBasis_i) # Test with the basis tree used for the mixed Poisson problem # (This involves a basis of vector-valued functions.) mixedPoissonBasis = defaultGlobalBasis(grid, Composite(RaviartThomas(order=0), Lagrange(order=0))) for i in (0,1): subBasis = functions.subspaceBasis(mixedPoissonBasis, i) basistest.checkBasis(subBasis, checkLocalIndexSetCompleteness=False) interpolatetest.checkBasisFunctionInterpolation(subBasis) # Run tests for grids of dimension 2 test(2) dune-functions-2.11.0+dfsg/examples/000077500000000000000000000000001513634022200172675ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/examples/CMakeLists.txt000066400000000000000000000026001513634022200220250ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_custom_target(build_examples) add_dependencies(build_tests build_examples) add_executable("advection-reaction-dg" advection-reaction-dg.cc) target_link_libraries(advection-reaction-dg PRIVATE Dune::Functions) set_property(TARGET advection-reaction-dg PROPERTY EXCLUDE_FROM_ALL 1) add_dependencies(build_examples advection-reaction-dg) add_executable("interpolation" interpolation.cc) target_link_libraries(interpolation PRIVATE Dune::Functions) set_property(TARGET interpolation PROPERTY EXCLUDE_FROM_ALL 1) add_dependencies(build_examples interpolation) add_executable("poisson-pq2" poisson-pq2.cc) target_link_libraries(poisson-pq2 PRIVATE Dune::Functions) set_property(TARGET poisson-pq2 PROPERTY EXCLUDE_FROM_ALL 1) add_dependencies(build_examples poisson-pq2) add_executable("stokes-taylorhood" stokes-taylorhood.cc) target_link_libraries(stokes-taylorhood PRIVATE Dune::Functions) set_property(TARGET stokes-taylorhood PROPERTY EXCLUDE_FROM_ALL 1) add_dependencies(build_examples stokes-taylorhood) add_executable("poisson-mfem" poisson-mfem.cc) target_link_libraries(poisson-mfem PRIVATE Dune::Functions) set_property(TARGET poisson-mfem PROPERTY EXCLUDE_FROM_ALL 1) add_dependencies(build_examples poisson-mfem) dune-functions-2.11.0+dfsg/examples/advection-reaction-dg.cc000066400000000000000000000442701513634022200237530ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** \file * \brief Example implementation of a DG-discretized advection-reaction equation * * Implemented using Alexandre Ern's lecture notes "Discontinuous Galerkin Methods", Chapter 3.3. */ using namespace Dune; // Compute the element stiffness matrix for an element coupling with itself template void getLocalMatrix(const LocalView& localView, MatrixType& elementMatrix, LocalVelocityField&& localVelocityField, LocalReactionCoefficient&& localReactionCoefficient) { // Get the grid element from the local FE basis view typedef typename LocalView::Element Element; const Element& element = localView.element(); const int dim = Element::dimension; auto geometry = element.geometry(); // Get set of shape functions for this element const auto& localFiniteElement = localView.tree().finiteElement(); // Set all matrix entries to zero elementMatrix.setSize(localFiniteElement.size(),localFiniteElement.size()); elementMatrix = 0; // fills the entire matrix with zeroes // Get a quadrature rule int order = 2*dim*localFiniteElement.localBasis().order(); const auto& quad = QuadratureRules::rule(element.type(), order); // Loop over all quadrature points for (const auto& quadPoint : quad) { // The inverse Jacobian of the map from the reference element to the element const auto& jacobianInverse = geometry.jacobianInverse(quadPoint.position()); // The multiplicative factor in the integral transformation formula const double integrationElement = geometry.integrationElement(quadPoint.position()); // The values of the shape functions at the quadrature point std::vector > values; localFiniteElement.localBasis().evaluateFunction(quadPoint.position(), values); // The gradients of the shape functions on the reference element std::vector > referenceJacobians; localFiniteElement.localBasis().evaluateJacobian(quadPoint.position(), referenceJacobians); // Compute the shape function gradients on the real element std::vector > jacobians(referenceJacobians.size()); for (size_t i=0; i::rule(intersection.type(), order); for (auto&& quadPoint : quad) { auto positionInElement = intersection.geometryInInside().global(quadPoint.position()); // The values of the shape functions at the quadrature point std::vector > values; localFiniteElement.localBasis().evaluateFunction(positionInElement, values); auto factor = localVelocityField(positionInElement)*intersection.integrationOuterNormal(quadPoint.position()); // Disable the term at outflow boundary faces if (!intersection.neighbor()) factor = std::min(factor, 0.0); for (size_t i=0; i void getOffDiagonalLocalMatrix(const Intersection& intersection, const LocalView& insideLocalView, const LocalView& outsideLocalView, MatrixType& elementMatrix, LocalVelocityField&& localVelocityField) { const int intersectionDim = Intersection::mydimension; // Get set of shape functions for the inside and the outside element const auto& insideLocalFiniteElement = insideLocalView.tree().finiteElement(); const auto& outsideLocalFiniteElement = outsideLocalView.tree().finiteElement(); // Set all matrix entries to zero elementMatrix.setSize(insideLocalFiniteElement.size(),outsideLocalFiniteElement.size()); elementMatrix = 0; // fills the entire matrix with zeroes // Get a quadrature rule on the element face int order = insideLocalFiniteElement.localBasis().order() * outsideLocalFiniteElement.localBasis().order(); const auto& quad = QuadratureRules::rule(intersection.type(), order); for (auto&& quadPoint : quad) { auto positionInInsideElement = intersection.geometryInInside().global(quadPoint.position()); auto positionInOutsideElement = intersection.geometryInOutside().global(quadPoint.position()); // The values of the shape functions at the quadrature point std::vector > insideValues, outsideValues; insideLocalFiniteElement. localBasis().evaluateFunction(positionInInsideElement, insideValues); outsideLocalFiniteElement.localBasis().evaluateFunction(positionInOutsideElement, outsideValues); // Velocity field times face normal auto factor = localVelocityField(positionInInsideElement)*intersection.integrationOuterNormal(quadPoint.position()); for (size_t i=0; i void getVolumeTerm( const LocalView& localView, BlockVector& localRhs, LocalSourceTerm&& localSourceTerm) { // Get the grid element from the local FE basis view typedef typename LocalView::Element Element; const Element& element = localView.element(); const int dim = Element::dimension; // Get set of shape functions for this element const auto& localFiniteElement = localView.tree().finiteElement(); // Set all entries to zero localRhs.resize(localFiniteElement.size()); localRhs = 0; // A quadrature rule int order = dim*localFiniteElement.localBasis().order(); const auto& quad = QuadratureRules::rule(element.type(), order); // Loop over all quadrature points for (const auto& quadPoint : quad) { // The multiplicative factor in the integral transformation formula const double integrationElement = element.geometry().integrationElement(quadPoint.position()); double functionValue = localSourceTerm(quadPoint.position()); // Evaluate all shape function values at this point std::vector > shapeFunctionValues; localFiniteElement.localBasis().evaluateFunction(quadPoint.position(), shapeFunctionValues); // Actually compute the vector entries for (size_t i=0; i void getOccupationPattern(const FEBasis& feBasis, MatrixIndexSet& nb) { // Total number of grid vertices auto n = feBasis.size(); nb.resize(n, n); // A view on the FE basis on a single element auto localView = feBasis.localView(); // Loop over all leaf elements for(const auto& e : elements(feBasis.gridView())) { // Bind the local FE basis view to the current element localView.bind(e); for (size_t i=0; i void assembleStiffnessMatrix(const FEBasis& feBasis, BCRSMatrix& matrix, BlockVector& rhs, VelocityField&& velocityField, ReactionCoefficient&& reactionCoefficient, SourceTerm&& sourceTerm) { // Get the grid view from the finite element basis typedef typename FEBasis::GridView GridView; GridView gridView = feBasis.gridView(); // Make 'local' versions of the coefficient functions // 'local' means that you can bind them to grid elements, and evaluate them // in local coordinates of that element. auto localVelocityField = localFunction(Functions::makeGridViewFunction(velocityField, gridView)); auto localReactionCoefficient = localFunction(Functions::makeGridViewFunction(reactionCoefficient, gridView)); auto localSourceTerm = localFunction(Functions::makeGridViewFunction(sourceTerm, gridView)); // MatrixIndexSets store the occupation pattern of a sparse matrix. // They are not particularly efficient, but simple to use. MatrixIndexSet occupationPattern; getOccupationPattern(feBasis, occupationPattern); // ... and give it the occupation pattern we want. occupationPattern.exportIdx(matrix); // set rhs to correct length -- the total number of basis vectors in the basis rhs.resize(feBasis.size()); // Set all entries to zero matrix = 0; rhs = 0; // A view on the FE basis on a single element auto localView = feBasis.localView(); // A loop over all elements of the grid for(const auto& element : elements(gridView)) { // Bind the local FE basis view to the current element localView.bind(element); localVelocityField.bind(element); localReactionCoefficient.bind(element); // Now let's get the element stiffness matrix // A dense matrix is used for the element stiffness matrix Matrix > elementMatrix; getLocalMatrix(localView, elementMatrix, localVelocityField, localReactionCoefficient); // Add element stiffness matrix onto the global stiffness matrix for (size_t i=0; i localRhs; localSourceTerm.bind(element); getVolumeTerm(localView, localRhs, localSourceTerm); for (size_t i=0; i GridType; FieldVector l = {1.0, 1.0}; std::array elements = {{10, 10}}; GridType grid(l,elements); typedef GridType::LeafGridView GridView; GridView gridView = grid.leafGridView(); ///////////////////////////////////////////////////////// // Choose a finite element space ///////////////////////////////////////////////////////// // Second-order Lagrange DG space typedef Functions::LagrangeDGBasis FEBasis; FEBasis feBasis(gridView); ///////////////////////////////////////////////////////// // Stiffness matrix and right hand side vector ///////////////////////////////////////////////////////// typedef BlockVector VectorType; typedef BCRSMatrix MatrixType; VectorType rhs; MatrixType stiffnessMatrix; ///////////////////////////////////////////////////////// // Assemble the system ///////////////////////////////////////////////////////// using Domain = GridType::Codim<0>::Geometry::GlobalCoordinate; // FieldVector auto sourceTerm = [] (const Domain& x) { return 10;}; auto velocityField = [] (const Domain& x) -> Domain { return {1,1};}; auto reactionCoefficient = [] (const Domain& x) { return 10;}; assembleStiffnessMatrix(feBasis, stiffnessMatrix, rhs, velocityField, reactionCoefficient, sourceTerm); ///////////////////////////////////////////////// // Choose an initial iterate ///////////////////////////////////////////////// VectorType x(feBasis.size()); x = 0; //////////////////////////// // Compute solution //////////////////////////// // Technicality: turn the matrix into a linear operator MatrixAdapter op(stiffnessMatrix); // Sequential incomplete LU decomposition as the preconditioner SeqILU preconditioner(stiffnessMatrix,1.0); // Richardson preconditioner(1.0); // Preconditioned conjugate-gradient solver RestartedGMResSolver solver(op, preconditioner, 1e-10, // desired residual reduction factor 500, // number of iterations between restarts 500, // maximum number of iterations 2); // verbosity of the solver // Object storing some statistics about the solving process InverseOperatorResult statistics; // Solve! solver.apply(x, rhs, statistics); //////////////////////////////////////////////////////////////////////////// // Make a discrete function from the FE basis and the coefficient vector //////////////////////////////////////////////////////////////////////////// auto xFunction = Functions::makeDiscreteGlobalBasisFunction(feBasis, x); ////////////////////////////////////////////////////////////////////////////////////////////// // Write result to VTK file // We need to subsample, because VTK cannot natively display real second-order functions ////////////////////////////////////////////////////////////////////////////////////////////// SubsamplingVTKWriter vtkWriter(gridView, Dune::refinementLevels(2)); vtkWriter.addVertexData(xFunction, VTK::FieldInfo("x", VTK::FieldInfo::Type::scalar, 1)); vtkWriter.write("advection-reaction-dg"); } // Error handling catch (Exception& e) { std::cout << e.what() << std::endl; } dune-functions-2.11.0+dfsg/examples/interpolation.cc000066400000000000000000000127611513634022200224740ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; int main (int argc, char *argv[]) { //Maybe initialize Mpi MPIHelper::instance(argc, argv); // make grid const int dim = 2; FieldVector L(1.0); std::array N = {{1,1}}; YaspGrid<2> grid(L,N); using GridView = YaspGrid<2>::LeafGridView; GridView gridView = grid.leafGridView(); // Create closed-form function to interpolate // { definition_f1_begin } auto f1 = [](const FieldVector& x) { return exp(-1.0*x.two_norm2()); }; // { definition_f1_end } // { definition_p2basis_begin } Functions::LagrangeBasis p2basis(gridView); // { definition_p2basis_end } // { definition_x1_begin } std::vector x1; // { definition_x1_end } // { interpolation1_begin } interpolate(p2basis, x1, f1); // { interpolation1_end } // Interpolate a vector-valued function using a vector-valued basis // { taylorhood_basis_begin } using namespace Functions::BasisFactory; auto taylorHoodBasis = makeBasis( gridView, composite( power( lagrange<2>(), flatLexicographic()), lagrange<1>(), flatLexicographic() )); // { taylorhood_basis_end } // { taylorhood_vector_begin } BlockVector x2; // { taylorhood_vector_end } // { taylorhood_pressure_begin } using namespace Indices; interpolate(subspaceBasis(taylorHoodBasis, _1), x2, f1); // { taylorhood_pressure_end } // { taylorhood_velocity_begin } auto f2 = [](const FieldVector& x) { return x; }; interpolate(subspaceBasis(taylorHoodBasis, _0), x2, f2); // { taylorhood_velocity_end } // { setup_mask_begin } BlockVector isBoundary; auto isBoundaryBackend = Functions::istlVectorBackend(isBoundary); isBoundaryBackend.resize(taylorHoodBasis); isBoundary = false; forEachBoundaryDOF(subspaceBasis(taylorHoodBasis, _0), [&] (auto&& index) { isBoundaryBackend[index] = true; }); // { setup_mask_end } // { masked_interpolation_begin } interpolate(subspaceBasis(taylorHoodBasis, _0), x2, f2, isBoundary); // { masked_interpolation_end } // Test whether I can use std::vector as an argument to 'interpolate' { std::vector isBoundary; auto isBoundaryBackend = Functions::istlVectorBackend(isBoundary); isBoundaryBackend.resize(taylorHoodBasis); std::fill(isBoundary.begin(), isBoundary.end(), false); forEachBoundaryDOF(subspaceBasis(taylorHoodBasis, _0), [&] (auto&& index) { isBoundaryBackend[index] = true; }); interpolate(subspaceBasis(taylorHoodBasis, _0), x2, f2, isBoundary); } // Test with a TupleVector { auto taylorHoodBasis = makeBasis( gridView, composite( power( lagrange<2>(), blockedInterleaved()), lagrange<1>(), blockedLexicographic() )); // Note: In the following code we cannot use // // using VelocityBitVector = std::vectordim> >; // using PressureBitVector = std::vector; // using BitVectorType = TupleVector; // // Reason: istlVectorBackend requires all terminal entries to have the same type. // However, accessing entries of std::bitset and std::vector by operator[] // does *not* return the same type. Instead, it returns two different proxy types. using VelocityBitVector = std::vector >; using PressureBitVector = std::vector; using BitVectorType = TupleVector; BitVectorType isBoundary; auto isBoundaryBackend = Functions::istlVectorBackend(isBoundary); isBoundaryBackend.resize(taylorHoodBasis); for (auto&& b0i : isBoundary[_0]) for (std::size_t j=0; j>; using PressureVector = BlockVector; using VectorType = MultiTypeBlockVector; VectorType x; interpolate(subspaceBasis(taylorHoodBasis, _0), x, f2, isBoundary); } return 0; } dune-functions-2.11.0+dfsg/examples/poisson-mfem.cc000066400000000000000000000435331513634022200222220ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DIM2 // Use a two-dimensional test, otherwise three-dimensional using namespace Dune; // Compute the stiffness matrix for a single element template void getLocalMatrix(const LocalView& localView, MatrixType& elementMatrix) { // Get the grid element from the local FE basis view typedef typename LocalView::Element Element; const Element& element = localView.element(); const int dim = Element::dimension; auto geometry = element.geometry(); // Set all matrix entries to zero elementMatrix.setSize(localView.size(), localView.size()); elementMatrix = 0; // fills the entire matrix with zeroes // Get set of shape functions for this element using namespace Dune::Indices; const auto& fluxLocalFiniteElement = localView.tree().child(_0).finiteElement(); const auto& pressureLocalFiniteElement = localView.tree().child(_1).finiteElement(); // Get a quadrature rule int fluxOrder = dim*fluxLocalFiniteElement.localBasis().order(); int pressureOrder = dim*pressureLocalFiniteElement.localBasis().order(); int order = std::max(2*fluxOrder, (fluxOrder-1)+pressureOrder); const auto& quad = QuadratureRules::rule(element.type(), order); // Loop over all quadrature points for (const auto& quadPoint : quad) { // Position of the current quadrature point in the reference element const auto quadPos = quadPoint.position(); // The inverse Jacobian of the map from the reference element to the element const auto jacobianInverse = geometry.jacobianInverse(quadPos); // The multiplicative factor in the integral transformation formula const auto integrationElement = geometry.integrationElement(quadPos); /////////////////////////////////////////////////////////////////////////// // Shape functions - flux /////////////////////////////////////////////////////////////////////////// // Values of the flux shape functions on the current element std::vector > fluxValues(fluxLocalFiniteElement.size()); fluxLocalFiniteElement.localBasis().evaluateFunction(quadPos, fluxValues); // Gradients of the flux shape function gradients on the reference element std::vector > fluxReferenceJacobians(fluxLocalFiniteElement.size()); fluxLocalFiniteElement.localBasis().evaluateJacobian(quadPos, fluxReferenceJacobians); // Helper function to compute the trace of a matrix auto trace = [](const auto& matrix) { double r=0; for (size_t j=0; j fluxDivergence(fluxValues.size(), 0.0); for (size_t i=0; i > pressureValues(pressureLocalFiniteElement.size()); pressureLocalFiniteElement.localBasis().evaluateFunction(quadPos, pressureValues); ///////////////////////////// // Flux--flux coupling ///////////////////////////// for (size_t i=0; i void getVolumeTerm( const LocalView& localView, BlockVector& localRhs, LocalVolumeTerm&& localVolumeTerm) { // Get the grid element from the local FE basis view typedef typename LocalView::Element Element; const Element& element = localView.element(); const int dim = Element::dimension; // Set all entries to zero localRhs.resize(localView.size()); localRhs = 0; // Get set of shape functions for this element using namespace Dune::Indices; const auto& fluxLocalFiniteElement = localView.tree().child(_0).finiteElement(); const auto& pressureLocalFiniteElement = localView.tree().child(_1).finiteElement(); // A quadrature rule int fluxOrder = dim*fluxLocalFiniteElement.localBasis().order(); int pressureOrder = dim*pressureLocalFiniteElement.localBasis().order(); int order = std::max(2*fluxOrder, 2*pressureOrder); const auto& quad = QuadratureRules::rule(element.type(), order); // Loop over all quadrature points for (const auto& quadPoint : quad) { // Position of the current quadrature point in the reference element const auto& quadPos = quadPoint.position(); // The multiplicative factor in the integral transformation formula const double integrationElement = element.geometry().integrationElement(quadPos); double functionValue = localVolumeTerm(quadPos); // Evaluate all shape function values at this point std::vector > pressureValues; pressureLocalFiniteElement.localBasis().evaluateFunction(quadPos, pressureValues); // Actually compute the vector entries for (size_t j=0; j void getOccupationPattern(const Basis& basis, std::array,2>& nb) { // Set sizes of the 2x2 submatrices for (size_t i=0; i<2; ++i) for (size_t j=0; j<2; j++) nb[i][j].resize(basis.size({i}), basis.size({j})); // A view on the FE basis on a single element auto localView = basis.localView(); // Loop over all leaf elements for(const auto& element : elements(basis.gridView())) { // Bind the local FE basis view to the current element localView.bind(element); // Add element stiffness matrix onto global stiffness matrix for (size_t i=0; i void assembleMixedPoissonMatrix(const Basis& basis, MatrixType& matrix) { // Get the grid view from the finite element basis (use of test space arbitrary) auto gridView = basis.gridView(); // MatrixIndexSets store the occupation pattern of a sparse matrix. // They are not particularly efficient, but simple to use. std::array, 2> occupationPattern; getOccupationPattern(basis, occupationPattern); // ... and give it the occupation pattern we want. matrix.setSize(2,2); for (size_t i=0; i<2; i++) for (size_t j=0; j<2; j++) occupationPattern[i][j].exportIdx(matrix[i][j]); // Set all entries to zero matrix = 0; // A view on the FE basis on a single element auto localView = basis.localView(); // A loop over all elements of the grid for(const auto& element : elements(gridView)) { // Bind the local FE basis view to the current element localView.bind(element); // Now let's get the element stiffness matrix // A dense matrix is used for the element stiffness matrix Matrix elementMatrix; getLocalMatrix(localView, elementMatrix); // Add element stiffness matrix onto the global stiffness matrix for (size_t i=0; i void assembleMixedPoissonRhs(const Basis& basis, VectorType& rhs, VolumeTerm&& volumeTerm) { // Get the grid view from the finite element basis (use of test space arbitrary) auto gridView = basis.gridView(); auto localVolumeTerm = localFunction(Functions::makeGridViewFunction(volumeTerm, gridView)); // set rhs to correct length -- the total number of basis vectors in the basis using Functions::istlVectorBackend; istlVectorBackend(rhs).resize(basis); // Set all entries to zero rhs = 0; // A view on the FE basis on a single element auto localView = basis.localView(); // A loop over all elements of the grid for(const auto& element : elements(gridView)) { // Bind the local FE basis view to the current element localView.bind(element); // Now get the local contribution to the right-hand side vector BlockVector localRhs; localVolumeTerm.bind(element); getVolumeTerm(localView, localRhs, localVolumeTerm); for (size_t i=0; i void markBoundaryDOFsByIndicator(const Basis& basis, Vector& vector, const Indicator& indicator) { auto vectorBackend = Dune::Functions::istlVectorBackend(vector); Dune::Functions::forEachBoundaryDOF(basis, [&] (auto&& localIndex, const auto& localView, const auto& intersection) { if (indicator(intersection.geometry().center())>1e-8) vectorBackend[localView.index(localIndex)] = true; }); } int main (int argc, char *argv[]) { // Set up MPI, if available MPIHelper::instance(argc, argv); /////////////////////////////////// // Generate the grid /////////////////////////////////// #ifdef DIM2 const int dim = 2; std::array elements = {{50, 50}}; #else const int dim = 3; std::array elements = {{10, 10, 10}}; #endif typedef YaspGrid GridType; FieldVector l(1); GridType grid(l,elements); auto gridView = grid.leafGridView(); ///////////////////////////////////////////////////////// // Choose a finite element space ///////////////////////////////////////////////////////// using namespace Functions::BasisFactory; using namespace Indices; const int k = 0; auto basis = makeBasis( gridView, composite( raviartThomas(), lagrange(), blockedLexicographic() )); auto fluxBasis = Functions::subspaceBasis(basis, _0); auto pressureBasis = Functions::subspaceBasis(basis, _1); ///////////////////////////////////////////////////////// // Stiffness matrix and right hand side vector ///////////////////////////////////////////////////////// using MatrixType = Matrix >; using VectorType = BlockVector >; using BitVectorType = BlockVector >; using Functions::istlVectorBackend; MatrixType stiffnessMatrix; VectorType rhs; ///////////////////////////////////////////////////////// // Assemble the system ///////////////////////////////////////////////////////// using Domain = GridType::template Codim<0>::Geometry::GlobalCoordinate; assembleMixedPoissonMatrix(basis, stiffnessMatrix); auto rightHandSide = [] (const Domain& x) { return 2; }; assembleMixedPoissonRhs(basis, rhs, rightHandSide); // This marks the top and bottom boundary of the domain auto fluxDirichletIndicator = [&l] (const auto& x) { return ((x[dim-1] > l[dim-1] - 1e-8) or (x[dim-1] < 1e-8)); }; auto coordinate = Dune::Functions::makeAnalyticGridViewFunction([](const auto& x) { return x; }, gridView); auto normal = Dune::Functions::FaceNormalGridFunction(gridView); auto fluxDirichletValues = Dune::Functions::makeComposedGridFunction( [pi = std::acos(-1.0)](const auto& x, const auto& normal) { return std::sin(2.*pi*x[0]) * normal; }, coordinate, normal); // Mark all DOFs located in a boundary intersection marked // by the fluxDirichletIndicator function. If the flux // ansatz space also contains tangential components, this // approach will fail, because those are also marked. // For Raviart-Thomas this does not happen. auto isDirichlet = BitVectorType(); istlVectorBackend(isDirichlet).resize(basis); isDirichlet = false; markBoundaryDOFsByIndicator(fluxBasis, isDirichlet, fluxDirichletIndicator); // Interpolate flux Dirichlet values interpolate(fluxBasis, rhs, fluxDirichletValues, isDirichlet); //////////////////////////////////////////// // Modify Dirichlet rows //////////////////////////////////////////// // loop over the matrix rows for (size_t i=0; i op(stiffnessMatrix); // Fancy (but only) way to not have a preconditioner at all Richardson preconditioner(1.0); // Preconditioned GMRES / BiCGSTAB solver //RestartedGMResSolver solver (op, preconditioner, 1e-6, 1000, 10000, 2); BiCGSTABSolver solver(op, preconditioner, 1e-6, 4000, 2); // Object storing some statistics about the solving process InverseOperatorResult statistics; // Solve! solver.apply(x, rhs, statistics); //////////////////////////////////////////////////////////////////////////// // Make a discrete function from the FE basis and the coefficient vector //////////////////////////////////////////////////////////////////////////// using FluxRange = FieldVector; using PressureRange = double; auto fluxFunction = Functions::makeDiscreteGlobalBasisFunction(fluxBasis, x); auto pressureFunction = Functions::makeDiscreteGlobalBasisFunction(pressureBasis, x); ////////////////////////////////////////////////////////////////////////////////////////////// // Write result to VTK file ////////////////////////////////////////////////////////////////////////////////////////////// auto vtkWriter = SubsamplingVTKWriter(gridView, Dune::refinementLevels(0)); vtkWriter.addVertexData(fluxFunction, VTK::FieldInfo("flux", VTK::FieldInfo::Type::vector, dim)); vtkWriter.addVertexData(pressureFunction, VTK::FieldInfo("pressure", VTK::FieldInfo::Type::scalar, 1)); vtkWriter.write("poisson-mfem-result"); } dune-functions-2.11.0+dfsg/examples/poisson-mfem.py000066400000000000000000000335001513634022200222560ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #! [imports] import numpy import scipy.sparse.linalg from scipy.sparse import lil_matrix import dune.geometry import dune.grid as grid import dune.functions as functions #! [imports] # Compute the stiffness matrix for a single element #! [getLocalMatrix] def getLocalMatrix(localView): # Get the grid element from the local FE basis view element = localView.element() # Make dense element stiffness matrix n = len(localView) # Number of local degrees of freedom (gradient + value) elementMatrix = numpy.zeros((n,n)) # Get set of shape functions for this element fluxLocalFiniteElement = localView.tree().child(0).finiteElement() pressureLocalFiniteElement = localView.tree().child(1).finiteElement() # The actual shape functions on the reference element localFluxBasis = fluxLocalFiniteElement.localBasis localPressureBasis = pressureLocalFiniteElement.localBasis nFlux = len(fluxLocalFiniteElement) nPressure = len(pressureLocalFiniteElement) # Get a quadrature rule fluxOrder = element.mydimension * fluxLocalFiniteElement.localBasis.order pressureOrder = element.mydimension * pressureLocalFiniteElement.localBasis.order quadOrder = numpy.max((2*fluxOrder, (fluxOrder-1)+pressureOrder)) quadRule = dune.geometry.quadratureRule(element.type, quadOrder) # Loop over all quadrature points for quadPoint in quadRule: # Position of the current quadrature point in the reference element quadPos = quadPoint.position # The inverse Jacobian of the map from the reference element to the element geometryJacobianInverse = element.geometry.jacobianInverse(quadPos) # The multiplicative factor in the integral transformation formula integrationElement = element.geometry.integrationElement(quadPos) # -------------------------------------------------------------- # Shape functions - gradient variable # -------------------------------------------------------------- # Values of the gradient variable shape functions on the current element fluxValues = localFluxBasis.evaluateFunction(quadPos) # Gradients of the gradient variable shape functions on the reference element fluxReferenceJacobians = localFluxBasis.evaluateJacobian(quadPos) fluxDivergence = numpy.zeros(nFlux) # Domain transformation of Jacobians and computation of div = trace(Jacobian) # TODO: Extend the Dune Python interface to allow to do this without casting to numpy.array for i in range(nFlux): fluxDivergence[i] = numpy.trace(numpy.array(fluxReferenceJacobians[i]) * geometryJacobianInverse) # -------------------------------------------------------------- # Shape functions - value variable # -------------------------------------------------------------- # Values of the pressure shape functions pressureValues = localPressureBasis.evaluateFunction(quadPos) # -------------------------------------------------------------- # Gradient-variable--gradient-variable coupling # -------------------------------------------------------------- for i in range(nFlux): row = localView.tree().child(0).localIndex(i) for j in range(nFlux): col = localView.tree().child(0).localIndex(j) # Compute the actual matrix entry contribution elementMatrix[row,col] += numpy.dot(fluxValues[i], fluxValues[j]) * quadPoint.weight * integrationElement # -------------------------------------------------------------- # Flux--pressure coupling # -------------------------------------------------------------- for i in range(nFlux): fluxIndex = localView.tree().child(0).localIndex(i) for j in range(nPressure): pressureIndex = localView.tree().child(1).localIndex(j) # Matrix entry contribution tmp = (fluxDivergence[i] * pressureValues[j][0]) * quadPoint.weight * integrationElement # Add to both off-diagonal block matrices elementMatrix[fluxIndex, pressureIndex] += tmp elementMatrix[pressureIndex, fluxIndex] += tmp return elementMatrix #! [getLocalMatrix] # Compute the right-hand side for a single element #! [getLocalVolumeTerm] def getVolumeTerm(localView, localVolumeTerm): # Get the grid element from the local FE basis view element = localView.element() # Construct an empty vector of the correct size localRhs = numpy.zeros(len(localView)) # Get set of shape functions for this element # Only the pressure part has a non-zero right-hand side pressureLocalFiniteElement = localView.tree().child(1).finiteElement() # A quadrature rule quadOrder = 2*element.mydimension*pressureLocalFiniteElement.localBasis.order quadRule = dune.geometry.quadratureRule(element.type, quadOrder) nPressure = len(pressureLocalFiniteElement) # Loop over all quadrature points for quadPoint in quadRule: # Position of the current quadrature point in the reference element quadPos = quadPoint.position # The multiplicative factor in the integral transformation formula integrationElement = element.geometry.integrationElement(quadPos) # Evaluate the load density at the quadrature point functionValue = localVolumeTerm(quadPos) # Evaluate all shape function values at this point pressureValues = pressureLocalFiniteElement.localBasis.evaluateFunction(quadPos) # Actually compute the vector entries for j in range(nPressure): pressureIndex = localView.tree().child(1).localIndex(j) localRhs[pressureIndex] += - pressureValues[j][0] * functionValue * quadPoint.weight * integrationElement return localRhs #! [getLocalVolumeTerm] # Assemble the stiffness matrix for the mixed Poisson problem with the given basis #! [assembleMixedPoissonMatrix] def assembleMixedPoissonMatrix(basis): # Total number of DOFs (both FE spaces together) n = len(basis) # Make an empty stiffness matrix stiffnessMatrix = lil_matrix( (n,n) ) # A view on the composite FE basis on a single element localView = basis.localView() # A loop over all elements of the grid for element in basis.gridView.elements: # Bind the local basis view to the current element localView.bind(element) # Assemble the element stiffness matrix elementMatrix = getLocalMatrix(localView) # Add element stiffness matrix onto the global stiffness matrix for i in range(len(localView)): # The global index of the i-th local degree of freedom of the element row = localView.index(i)[0] # The index is an array of length 1 for j in range(len(localView)): # The global index of the j-th local degree of freedom of the element col = localView.index(j)[0] stiffnessMatrix[row,col] += elementMatrix[i, j]; # Transform the stiffness matrix to CSR format, and return it return stiffnessMatrix.tocsr() #! [assembleMixedPoissonMatrix] # Assemble the right-hand side vector of the mixed Poisson problem with the given basis #! [assembleMixedPoissonRhs] def assembleMixedPoissonRhs(basis, volumeTerm): # Get the basis for the pressure variable pressureBasis = functions.subspaceBasis(basis, 1) # Represent the volume term function as a FE function in the pressure space volumeTermCoeff = numpy.zeros(len(basis)) pressureBasis.interpolate(volumeTermCoeff, volumeTerm) volumeTermGF = pressureBasis.asFunction(volumeTermCoeff) # A view on a single element localVolumeTerm = volumeTermGF.localFunction() # Create empty vector of correct size b = numpy.zeros(len(basis)) # A view on the FE basis on a single element localView = basis.localView() # A loop over all elements of the grid for element in basis.gridView.elements: # Bind the local FE basis view to the current element localView.bind(element) # Get the local contribution to the right-hand side vector localVolumeTerm.bind(element) localRhs = getVolumeTerm(localView, localVolumeTerm) # Add the local vector to the global one for i in range(len(localRhs)): # The global index of the i-th degree of freedom of the element row = localView.index(i)[0] # The index is an array of length 1. b[row] += localRhs[i] return b #! [assembleMixedPoissonRhs] # Mark all DOFs associated to entities for which # the boundary intersections center # is marked by the given indicator functions. # # This method simply calls the corresponding C++ code. A more Pythonic solution # is planned to appear eventually... #! [markBoundaryDOFsByIndicator] def markBoundaryDOFsByIndicator(basis, vector, indicator): from io import StringIO code=""" #include #include #include #include template void run(const Basis& basis, Vector& vector, const Indicator& indicator) { auto vectorBackend = vector.mutable_unchecked(); Dune::Functions::forEachBoundaryDOF(basis, [&] (auto&& localIndex, const auto& localView, const auto& intersection) { if (indicator(intersection.geometry().center()).template cast()) vectorBackend[localView.index(localIndex)] = true; }); } """ dune.generator.algorithm.run("run",StringIO(code), basis, vector, indicator) #! [markBoundaryDOFsByIndicator] # This incorporates essential constraints into matrix # and rhs of a linear system. # The mask vector isConstrained # indicates which DOFs should be constrained, # x contains the desired values of these DOFs. Other entries of x # are ignored. # Note that this implements the symmetrized approach to modify the matrix. #! [incorporateEssentialConstraints] def incorporateEssentialConstraints(A, b, isConstrained, x): b -= A*(x*isConstrained) rows, cols = A.nonzero() for i,j in zip(rows, cols): if isConstrained[i] or isConstrained[j]: A[i,j] = 0 for i in range(len(b)): if isConstrained[i]: A[i,i] = 1 b[i] = x[i] #! [incorporateEssentialConstraints] ############################ main program ################################### #! [createGrid] upperRight = [1, 1] # Upper right corner of the domain elements = [50, 50] # Number of grid elements per direction # Create a grid of the unit square gridView = grid.structuredGrid([0,0],upperRight,elements) #! [createGrid] # Construct a pair of finite element space bases # Note: In contrast to the corresponding C++ example we are using a single matrix with scalar entries, # and plain numbers to index it (no multi-digit multi-indices). #! [createBasis] basis = functions.defaultGlobalBasis(gridView, functions.Composite(functions.RaviartThomas(order=0), functions.Lagrange(order=0))) fluxBasis = functions.subspaceBasis(basis, 0); pressureBasis = functions.subspaceBasis(basis, 1); #! [createBasis] #! [assembly] # Compute the stiffness matrix stiffnessMatrix = assembleMixedPoissonMatrix(basis) # Compute the load vector volumeTerm = lambda x : 2 b = assembleMixedPoissonRhs(basis, volumeTerm) #! [assembly] #! [dirichletDOFs] # This marks the top and bottom boundary of the domain fluxDirichletIndicator = lambda x : 1.*((x[1] > upperRight[1] - 1e-8) or (x[1] < 1e-8)) isDirichlet = numpy.zeros(len(basis)) # Mark all DOFs located in a boundary intersection marked # by the fluxDirichletIndicator function. markBoundaryDOFsByIndicator(fluxBasis, isDirichlet, fluxDirichletIndicator); #! [dirichletDOFs] ############################################################ # ToDo: We should provide binding for FaceNormalGridFunction # and support for grid functions to the bindings of interpolate(). # This would allow to avoid having to define the normal field # manually. ############################################################ #! [dirichletValues] normal = lambda x : numpy.array([0.,1.]) if numpy.abs(x[1]-1) < 1e-8 else numpy.array([0., -1.]) fluxDirichletValues = lambda x : numpy.sin(2.*numpy.pi*x[0]) * normal(x) # TODO: This should be constrained to boundary DOFs fluxDirichletCoefficients = numpy.zeros(len(basis)) fluxBasis.interpolate(fluxDirichletCoefficients, fluxDirichletValues); #! [dirichletValues] # ////////////////////////////////////////// # Modify Dirichlet rows # ////////////////////////////////////////// #! [dirichletIntegration] incorporateEssentialConstraints(stiffnessMatrix, b, isDirichlet, fluxDirichletCoefficients) #! [dirichletIntegration] # ////////////////////////// # Compute solution # ////////////////////////// #! [solving] x = scipy.sparse.linalg.spsolve(stiffnessMatrix, b) #! [solving] # //////////////////////////////////////////////////////////////////////////////////////////// # Write result to VTK file # //////////////////////////////////////////////////////////////////////////////////////////// #! [vtkWriting] # TODO: Improve file writing. Currently this simply projects everything # onto a first-order Lagrange space vtkWriter = gridView.vtkWriter(subsampling=2) fluxFunction = fluxBasis.asFunction(x) fluxFunction.addToVTKWriter("flux", vtkWriter, grid.DataType.PointVector) pressureFunction = pressureBasis.asFunction(x) pressureFunction.addToVTKWriter("pressure", vtkWriter, grid.DataType.PointData) vtkWriter.write("poisson-mfem-result") #! [vtkWriting] dune-functions-2.11.0+dfsg/examples/poisson-pq2.cc000066400000000000000000000344131513634022200217750ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Dune; //! [getLocalMatrix] // Compute the stiffness matrix for a single element template void getLocalMatrix(const LocalView& localView, MatrixType& elementMatrix) { // Get the grid element from the local FE basis view typedef typename LocalView::Element Element; const Element& element = localView.element(); const int dim = Element::dimension; auto geometry = element.geometry(); // Get set of shape functions for this element const auto& localFiniteElement = localView.tree().finiteElement(); // Set all matrix entries to zero elementMatrix.setSize(localFiniteElement.localBasis().size(),localFiniteElement.localBasis().size()); elementMatrix = 0; // fills the entire matrix with zeroes // Get a quadrature rule int order = 2*(dim*localFiniteElement.localBasis().order()-1); const QuadratureRule& quad = QuadratureRules::rule(element.type(), order); // Loop over all quadrature points for (size_t pt=0; pt < quad.size(); pt++) { // Position of the current quadrature point in the reference element const FieldVector& quadPos = quad[pt].position(); // The inverse Jacobian of the map from the reference element to the element const auto& jacobianInverse = geometry.jacobianInverse(quadPos); // The multiplicative factor in the integral transformation formula const double integrationElement = geometry.integrationElement(quadPos); // The gradients of the shape functions on the reference element std::vector > referenceJacobians; localFiniteElement.localBasis().evaluateJacobian(quadPos, referenceJacobians); // Compute the shape function gradients on the real element std::vector > jacobians(referenceJacobians.size()); for (size_t i=0; i void getVolumeTerm( const LocalView& localView, BlockVector& localRhs, LocalVolumeTerm&& localVolumeTerm) { // Get the grid element from the local FE basis view typedef typename LocalView::Element Element; const Element& element = localView.element(); const int dim = Element::dimension; // Get set of shape functions for this element const auto& localFiniteElement = localView.tree().finiteElement(); // Set all entries to zero localRhs.resize(localFiniteElement.localBasis().size()); localRhs = 0; // A quadrature rule int order = dim*localFiniteElement.localBasis().order(); const QuadratureRule& quad = QuadratureRules::rule(element.type(), order); // Loop over all quadrature points for ( size_t pt=0; pt < quad.size(); pt++ ) { // Position of the current quadrature point in the reference element const FieldVector& quadPos = quad[pt].position(); // The multiplicative factor in the integral transformation formula const double integrationElement = element.geometry().integrationElement(quadPos); double functionValue = localVolumeTerm(quadPos); // Evaluate all shape function values at this point std::vector > shapeFunctionValues; localFiniteElement.localBasis().evaluateFunction(quadPos, shapeFunctionValues); // Actually compute the vector entries for (size_t i=0; i void getOccupationPattern(const FEBasis& feBasis, MatrixIndexSet& nb) { // Total number of grid vertices auto n = feBasis.size(); nb.resize(n, n); // A view on the FE basis on a single element auto localView = feBasis.localView(); // Loop over all leaf elements for(const auto& e : elements(feBasis.gridView())) { // Bind the local FE basis view to the current element localView.bind(e); // There is a matrix entry a_ij if the i-th and j-th vertex are connected in the grid for (size_t i=0; i void assembleLaplaceMatrix(const FEBasis& feBasis, BCRSMatrix& matrix, BlockVector& rhs, VolumeTerm&& volumeTerm) { // Get the grid view from the finite element basis typedef typename FEBasis::GridView GridView; GridView gridView = feBasis.gridView(); auto localVolumeTerm = localFunction(Functions::makeGridViewFunction(volumeTerm, gridView)); // MatrixIndexSets store the occupation pattern of a sparse matrix. // They are not particularly efficient, but simple to use. MatrixIndexSet occupationPattern; getOccupationPattern(feBasis, occupationPattern); // ... and give it the occupation pattern we want. occupationPattern.exportIdx(matrix); // set rhs to correct length -- the total number of basis vectors in the basis rhs.resize(feBasis.size()); // Set all entries to zero matrix = 0; rhs = 0; // A view on the FE basis on a single element auto localView = feBasis.localView(); // A loop over all elements of the grid for(const auto& e : elements(gridView)) { // Bind the local FE basis view to the current element localView.bind(e); // Now let's get the element stiffness matrix // A dense matrix is used for the element stiffness matrix Matrix elementMatrix; getLocalMatrix(localView, elementMatrix); // Add element stiffness matrix onto the global stiffness matrix for (size_t i=0; i localRhs; localVolumeTerm.bind(e); getVolumeTerm(localView, localRhs, localVolumeTerm); for (size_t i=0; i is currently not supported, // we use a vector which, in contrast to vector // is a real container. //! [boundaryTreatment] template void boundaryTreatment (const FEBasis& feBasis, std::vector& dirichletNodes ) { dirichletNodes.clear(); dirichletNodes.resize(feBasis.size(), false); Functions::forEachBoundaryDOF(feBasis, [&] (auto&& index) { dirichletNodes[index] = true; }); } //! [boundaryTreatment] //! [createMixedGrid] auto createMixedGrid() { using Grid = Dune::UGGrid<2>; Dune::GridFactory factory; for(unsigned int k : Dune::range(9)) factory.insertVertex({0.5*(k%3), 0.5*(k/3)}); factory.insertElement(Dune::GeometryTypes::cube(2), {0, 1, 3, 4}); factory.insertElement(Dune::GeometryTypes::cube(2), {1, 2, 4, 5}); factory.insertElement(Dune::GeometryTypes::simplex(2), {3, 4, 6}); factory.insertElement(Dune::GeometryTypes::simplex(2), {4, 7, 6}); factory.insertElement(Dune::GeometryTypes::simplex(2), {4, 5, 7}); factory.insertElement(Dune::GeometryTypes::simplex(2), {5, 8, 7}); return factory.createGrid(); } //! [createMixedGrid] //! [startMainAndMPI] int main (int argc, char *argv[]) try { // Set up MPI, if available MPIHelper::instance(argc, argv); //! [startMainAndMPI] /////////////////////////////////// // Generate the grid /////////////////////////////////// //! [createGridCall] auto grid = createMixedGrid(); grid->globalRefine(4); auto gridView = grid->leafGridView(); using GridView = decltype(gridView); //! [createGridCall] ///////////////////////////////////////////////////////// // Choose a finite element space ///////////////////////////////////////////////////////// //! [makeBasis] typedef Functions::LagrangeBasis FEBasis; FEBasis feBasis(gridView); //! [makeBasis] ///////////////////////////////////////////////////////// // Stiffness matrix and right hand side vector ///////////////////////////////////////////////////////// //! [theRest] using VectorType = BlockVector; using MatrixType = BCRSMatrix; VectorType rhs; MatrixType stiffnessMatrix; ///////////////////////////////////////////////////////// // Assemble the system ///////////////////////////////////////////////////////// std::cout << "Number of DOFs is " << feBasis.dimension() << std::endl; auto rightHandSide = [] (const auto& x) { return 10;}; Dune::Timer timer; assembleLaplaceMatrix(feBasis, stiffnessMatrix, rhs, rightHandSide); std::cout << "Assembling the problem took " << timer.elapsed() << "s" << std::endl; ///////////////////////////////////////////////// // Choose an initial iterate ///////////////////////////////////////////////// VectorType x(feBasis.size()); x = 0; // Determine Dirichlet dofs std::vector dirichletNodes; boundaryTreatment(feBasis, dirichletNodes); // Don't trust on non-standard M_PI. auto pi = std::acos(-1.0); auto dirichletValueFunction = [pi](const auto& x){ return std::sin(2*pi*x[0]); }; // Interpolate dirichlet values at the boundary nodes interpolate(feBasis, x, dirichletValueFunction, dirichletNodes); ////////////////////////////////////////////////////// // Incorporate Dirichlet values in a symmetric way ////////////////////////////////////////////////////// // Compute residual for non-homogeneous Dirichlet values // stored in x. Since x is zero for non-Dirichlet DOFs, // we can simply multiply by the matrix. stiffnessMatrix.mmv(x, rhs); // Change Dirichlet matrix rows and columns to the identity. for (size_t i=0; i op(stiffnessMatrix); // Sequential incomplete LU decomposition as the preconditioner SeqILDL ildl(stiffnessMatrix,1.0); // Preconditioned conjugate-gradient solver CGSolver cg(op, ildl, // preconditioner 1e-4, // desired residual reduction factor 100, // maximum number of iterations 2); // verbosity of the solver // Object storing some statistics about the solving process InverseOperatorResult statistics; // Solve! cg.apply(x, rhs, statistics); //////////////////////////////////////////////////////////////////////////// // Make a discrete function from the FE basis and the coefficient vector //////////////////////////////////////////////////////////////////////////// auto xFunction = Functions::makeDiscreteGlobalBasisFunction(feBasis, x); ////////////////////////////////////////////////////////////////////////////////////////////// // Write result to VTK file // We need to subsample, because VTK cannot natively display real second-order functions ////////////////////////////////////////////////////////////////////////////////////////////// SubsamplingVTKWriter vtkWriter(gridView, Dune::refinementLevels(2)); vtkWriter.addVertexData(xFunction, VTK::FieldInfo("x", VTK::FieldInfo::Type::scalar, 1)); vtkWriter.write("poisson-pq2"); } // Error handling catch (Exception& e) { std::cout << e.what() << std::endl; } //! [theRest] dune-functions-2.11.0+dfsg/examples/poisson-pq2.py000066400000000000000000000121301513634022200220300ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #! [imports] import numpy as np import scipy.sparse.linalg from scipy.sparse import lil_matrix import dune.geometry import dune.grid as grid import dune.functions as functions #! [imports] # Compute element stiffness matrix and element load vector # # TODO: This assembler loop is very inefficient in terms of run time and should be improved using Python vectorization. # See discussion at https://gitlab.dune-project.org/staging/dune-functions/-/merge_requests/295 for hints and code pointers. #! [localAssembler] def localAssembler(localView, volumeTerm): # Number of degrees of freedom on this element n = len(localView) # The grid element element = localView.element() # The set of shape functions localBasis = localView.tree().finiteElement().localBasis # Initialize element matrix and load vector localA = np.zeros((n,n)) localB = np.zeros(n) # Create a quadrature rule and integrate quadRule = dune.geometry.quadratureRule(element.type, order=4) for pt in quadRule: # The determinant that appears in the integral transformation formula integrationElement = element.geometry.integrationElement(pt.position) # Evaluate all shape functions (The return value is an array!) values = localBasis.evaluateFunction(pt.position) # Evaluate the shape function Jacobians on the reference element (array of arrays) referenceJacobians = localBasis.evaluateJacobian(pt.position) # Transform the reference Jacobians to the actual element geometryJacobianInverse = element.geometry.jacobianInverse(pt.position) jacobians = [ np.dot(np.array(g)[0], geometryJacobianInverse) for g in referenceJacobians ] quadPosGlobal = element.geometry.toGlobal(pt.position) for i in range( n ): for j in range( n ): localA[i,j] += pt.weight * integrationElement * np.dot(jacobians[i], jacobians[j]) localB[i] += pt.weight * integrationElement * values[i] * volumeTerm(quadPosGlobal) return localA, localB #! [localAssembler] # The assembler for the global stiffness matrix #! [assembleLaplaceMatrix] def assembleLaplaceMatrix(basis, volumeTerm): # Total number of degrees of freedom n = len(basis) # Make empty sparse matrix A = lil_matrix((n,n)) # Make empty vector b = np.zeros(n) # View on the finite element basis on a single element localView = basis.localView() # Loop over all grid elements grid = basis.gridView for element in grid.elements: # Bind the localView to the current element localView.bind(element) # Number of degrees of freedom on the current element localN = len(localView) # Assemble the local stiffness matrix and load vector localA, localb = localAssembler(localView, volumeTerm) # Copy the local entries into the global matrix and vector for i in range(localN): gi = localView.index(i)[0] b[gi] += localb[i] for j in range(localN): gj = localView.index(j)[0] A[gi, gj] += localA[i, j] # Convert matrix to CSR format return A.tocsr(), b #! [assembleLaplaceMatrix] ############################ main program ################################### #! [createGrid] # Number of grid elements in one direction gridSize = 32 # Create a grid of the unit square grid = grid.structuredGrid([0,0],[1,1],[gridSize,gridSize]) #! [createGrid] #! [createBasis] # Create a second-order Lagrange FE basis basis = functions.defaultGlobalBasis(grid, functions.Lagrange(order=2)) #! [createBasis] #! [assembly] # Source term f = lambda x : 10 # Compute stiffness matrix and load vector A,b = assembleLaplaceMatrix(basis, f) #! [assembly] #! [dirichletDOFs] # Determine all coefficients that are on the boundary isDirichlet = np.zeros(len(basis)) def markDOF(boundaryDOFNumber): isDirichlet[boundaryDOFNumber] = True functions.boundarydofs.forEachBoundaryDOF(basis,markDOF) #! [dirichletDOFs] #! [dirichletValues] # The function that implements the Dirichlet values dirichletValueFunction = lambda x : np.sin(2*np.pi*x[0]) # Get coefficients of a Lagrange-FE approximation of the Dirichlet values dirichletValues = np.zeros(len(basis)) basis.interpolate(dirichletValues, dirichletValueFunction) #! [dirichletValues] #! [dirichletIntegration] # Integrate Dirichlet conditions into the matrix and load vector rows, cols = A.nonzero() for i,j in zip(rows, cols): if isDirichlet[i]: if i==j: A[i,j] = 1.0 else: A[i,j] = 0 b[i] = dirichletValues[i] #! [dirichletIntegration] #! [solving] # Solve linear system! x = scipy.sparse.linalg.spsolve(A, b) #! [solving] #! [vtkWriting] # Write result as vtu file uh = basis.asFunction(x) vtkWriter = grid.vtkWriter(subsampling=2) uh.addToVTKWriter("u", vtkWriter, dune.grid.DataType.PointData) vtkWriter.write("poisson-pq2-result") #! [vtkWriting] dune-functions-2.11.0+dfsg/examples/stokes-taylorhood.cc000066400000000000000000000454701513634022200233020ustar00rootroot00000000000000// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: // SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md // SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BLOCKEDBASIS 1 // { using_namespace_dune_begin } using namespace Dune; // { using_namespace_dune_end } // Compute the stiffness matrix for a single element // { local_assembler_signature_begin } template void getLocalMatrix( const LocalView& localView, Matrix& elementMatrix) // { local_assembler_signature_end } { // Get the grid element from the local FE basis view // { local_assembler_get_element_information_begin } using Element = typename LocalView::Element; const Element element = localView.element(); const int dim = Element::dimension; auto geometry = element.geometry(); // { local_assembler_get_element_information_end } // Set all matrix entries to zero // { initialize_element_matrix_begin } elementMatrix.setSize(localView.size(), localView.size()); elementMatrix = 0; // fills the entire matrix with zeros // { initialize_element_matrix_end } // Get set of shape functions for this element // { get_local_fe_begin } using namespace Indices; const auto& velocityLocalFiniteElement /*@\label{li:stokes_taylorhood_get_velocity_lfe}@*/ = localView.tree().child(_0,0).finiteElement(); const auto& pressureLocalFiniteElement = localView.tree().child(_1).finiteElement(); /*@\label{li:stokes_taylorhood_get_pressure_lfe}@*/ // { get_local_fe_end } // Get a quadrature rule // { begin_quad_loop_begin } int order = 2*(dim*velocityLocalFiniteElement.localBasis().order()-1); const auto& quad = QuadratureRules::rule(element.type(), order); // Loop over all quadrature points for (const auto& quadPoint : quad) { // { begin_quad_loop_end } // { quad_loop_preamble_begin } // The inverse Jacobian of the map from the // reference element to the element const auto jacobianInverse = geometry.jacobianInverse(quadPoint.position()); // The multiplicative factor in the integral transformation formula const auto integrationElement = geometry.integrationElement(quadPoint.position()); // { quad_loop_preamble_end } /////////////////////////////////////////////////////////////////////// // Velocity--velocity coupling /////////////////////////////////////////////////////////////////////// // The gradients of the shape functions on the reference element // { velocity_gradients_begin } std::vector > referenceJacobians; velocityLocalFiniteElement.localBasis().evaluateJacobian( quadPoint.position(), referenceJacobians); // Compute the shape function gradients on the grid element std::vector > jacobians(referenceJacobians.size()); for (size_t i=0; i > pressureValues; pressureLocalFiniteElement.localBasis().evaluateFunction( quadPoint.position(), pressureValues); // { pressure_values_end } // Compute the actual matrix entries // { velocity_pressure_coupling_begin } for (size_t i=0; i void setOccupationPattern(const Basis& basis, MatrixType& matrix) { enum {dim = Basis::GridView::dimension}; // MatrixIndexSets store the occupation pattern of a sparse matrix. // They are not particularly efficient, but simple to use. std::array, 2> nb; // Set sizes of the 2x2 submatrices for (size_t i=0; i<2; i++) for (size_t j=0; j<2; j++) nb[i][j].resize(basis.size({i}), basis.size({j})); // A view on the FE basis on a single element auto localView = basis.localView(); // Loop over all leaf elements for(const auto& element : elements(basis.gridView())) { // Bind the local view to the current element localView.bind(element); // Add element stiffness matrix onto the global stiffness matrix for (size_t i=0; i decltype(auto) matrixEntry( Matrix& matrix, const MultiIndex& row, const MultiIndex& col) { using namespace Indices; if ((row[0]==0) and (col[0]==0)) return matrix[_0][_0][row[1]][col[1]][row[2]][col[2]]; if ((row[0]==0) and (col[0]==1)) return matrix[_0][_1][row[1]][col[1]][row[2]][0]; if ((row[0]==1) and (col[0]==0)) return matrix[_1][_0][row[1]][col[1]][0][col[2]]; return matrix[_1][_1][row[1]][col[1]]; /*@\label{li:matrixentry_pressure_pressure}@*/ } // { matrixentry_end } #else template decltype(auto) matrixEntry(Matrix& matrix, const MultiIndex& row, const MultiIndex& col) { return matrix[row[0]][col[0]][row[1]][col[1]]; } #endif /** \brief Assemble the Laplace stiffness matrix on the given grid view */ // { global_assembler_signature_begin } template void assembleStokesMatrix(const Basis& basis, MatrixType& matrix) // { global_assembler_signature_end } { // { setup_matrix_pattern_begin } // Set matrix size and occupation pattern setOccupationPattern(basis, matrix); // Set all entries to zero matrix = 0; // { setup_matrix_pattern_end } // A view on the FE basis on a single element // { get_localview_begin } auto localView = basis.localView(); // { get_localview_end } // A loop over all elements of the grid // { element_loop_and_bind_begin } for (const auto& element : elements(basis.gridView())) { // Bind the local FE basis view to the current element localView.bind(element); // { element_loop_and_bind_end } // Now let's get the element stiffness matrix // A dense matrix is used for the element stiffness matrix // { setup_element_stiffness_begin } Matrix elementMatrix; getLocalMatrix(localView, elementMatrix); // { setup_element_stiffness_end } // Add element stiffness matrix onto the global stiffness matrix // { accumulate_global_matrix_begin } for (size_t i=0; i; FieldVector upperRight = {1, 1}; std::array elements = {{4, 4}}; GridType grid(upperRight,elements); using GridView = typename GridType::LeafGridView; GridView gridView = grid.leafGridView(); // { grid_setup_end } ///////////////////////////////////////////////////////// // Choose a finite element space ///////////////////////////////////////////////////////// #if BLOCKEDBASIS // { function_space_basis_begin } using namespace Functions::BasisFactory; constexpr std::size_t p = 1; // pressure order for Taylor-Hood auto taylorHoodBasis = makeBasis( gridView, composite( power( lagrange(), blockedInterleaved()), lagrange

(), blockedLexicographic() )); // { function_space_basis_end } #else using namespace Functions::BasisFactory; static const std::size_t p = 1; // pressure order for Taylor-Hood auto taylorHoodBasis = makeBasis( gridView, composite( power( lagrange(), flatInterleaved()), lagrange

(), blockedLexicographic() )); #endif ///////////////////////////////////////////////////////// // Stiffness matrix and right hand side vector ///////////////////////////////////////////////////////// #if BLOCKEDBASIS // { linear_algebra_setup_begin } using VelocityVector = BlockVector>; using PressureVector = BlockVector; using VectorType = MultiTypeBlockVector; using VelocityBitVector = BlockVector>; using PressureBitVector = BlockVector; using BitVectorType = MultiTypeBlockVector; using Matrix00 = BCRSMatrix>; using Matrix01 = BCRSMatrix>; using Matrix10 = BCRSMatrix>; using Matrix11 = BCRSMatrix; /*@\label{li:matrix_type_pressure_pressure}@*/ using MatrixRow0 = MultiTypeBlockVector; using MatrixRow1 = MultiTypeBlockVector; using MatrixType = MultiTypeBlockMatrix; // { linear_algebra_setup_end } #else using VectorType = BlockVector > >; using BitVectorType = BlockVector > >; using MatrixType = Matrix > >; #endif ///////////////////////////////////////////////////////// // Assemble the system ///////////////////////////////////////////////////////// // { rhs_assembly_begin } VectorType rhs; auto rhsBackend = Dune::Functions::istlVectorBackend(rhs); rhsBackend.resize(taylorHoodBasis); rhs = 0; /*@\label{li:stokes_taylorhood_set_rhs_to_zero}@*/ // { rhs_assembly_end } // { matrix_assembly_begin } MatrixType stiffnessMatrix; assembleStokesMatrix(taylorHoodBasis, stiffnessMatrix); /*@\label{li:stokes_taylorhood_call_to_assemblestokesmatrix}@*/ // { matrix_assembly_end } ///////////////////////////////////////////////////////// // Set Dirichlet values. // Only velocity components have Dirichlet boundary values ///////////////////////////////////////////////////////// // { initialize_boundary_dofs_vector_begin } BitVectorType isBoundary; auto isBoundaryBackend = Dune::Functions::istlVectorBackend(isBoundary); isBoundaryBackend.resize(taylorHoodBasis); isBoundary = false; // { initialize_boundary_dofs_vector_end } // { determine_boundary_dofs_begin } using namespace Indices; Functions::forEachBoundaryDOF( Functions::subspaceBasis(taylorHoodBasis, _0), [&] (auto&& index) { isBoundaryBackend[index] = true; }); // { determine_boundary_dofs_end } // { interpolate_dirichlet_values_begin } using Coordinate = GridView::Codim<0> ::Geometry::GlobalCoordinate; using VelocityRange = FieldVector; auto&& velocityDirichletData = [](Coordinate x) { return VelocityRange{0.0, double(x[0] < 1e-8)}; }; Functions::interpolate( Functions::subspaceBasis(taylorHoodBasis, _0), rhs, velocityDirichletData, isBoundary); // { interpolate_dirichlet_values_end } //////////////////////////////////////////// // Modify Dirichlet rows //////////////////////////////////////////// // loop over the matrix rows // { set_dirichlet_matrix_begin } auto localView = taylorHoodBasis.localView(); for(const auto& element : Dune::elements(taylorHoodBasis.gridView())) { localView.bind(element); for (size_t i=0; i stiffnessOperator(stiffnessMatrix); // Fancy (but only) way to not have a preconditioner at all Richardson preconditioner(1.0); // Construct the actual iterative solver RestartedGMResSolver solver( stiffnessOperator, // operator to invert preconditioner, // preconditioner for iteration 1e-10, // desired residual reduction factor 500, // number of iterations between restarts 500, // maximum number of iterations 2); // verbosity of the solver // Object storing some statistics about the solving process InverseOperatorResult statistics; // Solve! solver.apply(x, rhs, statistics); // { stokes_solve_end } //////////////////////////////////////////////////////////////////////////// // Make a discrete function from the FE basis and the coefficient vector //////////////////////////////////////////////////////////////////////////// // { make_result_functions_begin } using VelocityRange = FieldVector; using PressureRange = double; auto velocityFunction = Functions::makeDiscreteGlobalBasisFunction( Functions::subspaceBasis(taylorHoodBasis, _0), x); auto pressureFunction = Functions::makeDiscreteGlobalBasisFunction( Functions::subspaceBasis(taylorHoodBasis, _1), x); // { make_result_functions_end } ////////////////////////////////////////////////////////////////////////////////////////////// // Write result to VTK file // We need to subsample, because VTK cannot natively display real second-order functions ////////////////////////////////////////////////////////////////////////////////////////////// // { vtk_output_begin } SubsamplingVTKWriter vtkWriter( gridView, refinementLevels(2)); vtkWriter.addVertexData( velocityFunction, VTK::FieldInfo("velocity", VTK::FieldInfo::Type::vector, dim)); vtkWriter.addVertexData( pressureFunction, VTK::FieldInfo("pressure", VTK::FieldInfo::Type::scalar, 1)); vtkWriter.write("stokes-taylorhood-result"); // { vtk_output_end } } // Error handling catch (Exception& e) { std::cout << e.what() << std::endl; } dune-functions-2.11.0+dfsg/python/000077500000000000000000000000001513634022200167725ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/python/CMakeLists.txt000066400000000000000000000004361513634022200215350ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory(dune) dune_python_configure_bindings( PATH "." CMAKE_METADATA_FLAGS DUNE_OPTS_FILE ) dune-functions-2.11.0+dfsg/python/dune/000077500000000000000000000000001513634022200177255ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/python/dune/CMakeLists.txt000066400000000000000000000003171513634022200224660ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_subdirectory(functions) dune-functions-2.11.0+dfsg/python/dune/functions/000077500000000000000000000000001513634022200217355ustar00rootroot00000000000000dune-functions-2.11.0+dfsg/python/dune/functions/CMakeLists.txt000066400000000000000000000004211513634022200244720ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later add_python_targets(functions __init__ boundarydofs globalbasis subspacebasis tree ) dune-functions-2.11.0+dfsg/python/dune/functions/__init__.py000066400000000000000000000015471513634022200240550ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later from dune.generator.generator import SimpleGenerator from dune.common.hashit import hashIt from dune.common.checkconfiguration import assertHave, ConfigurationError from .boundarydofs import * from .globalbasis import defaultGlobalBasis from .subspacebasis import subspaceBasis from .tree import * registry = dict() registry["globalBasis"] = { "default" : defaultGlobalBasis } generator = SimpleGenerator("GlobalBasis", "Dune::Python") def load(includes, typeName, *args): includes = includes + ["dune/python/functions/globalbasis.hh"] moduleName = "globalbasis_" + hashIt(typeName) module = generator.load(includes, typeName, moduleName, *args) return module dune-functions-2.11.0+dfsg/python/dune/functions/boundarydofs.py000066400000000000000000000024101513634022200250030ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later import dune.common # Mark all degrees of freedom on the grid boundary def forEachBoundaryDOF(basis, callBack): """Loop over all degrees of freedom (DOFs) associated to the boundary This loops over all DOFs of a basis associated to sub-entities on the boundary. This overload will pass a single argument to the given loop callback: The global (multi-)index of the boundary DOF. Notice that the same DOF may be visited multiple times. Parameters ---------- arg0: A function space basis that defines a set of degrees of freedom arg1: A callback that will be called with the global index of the visited boundary DOF """ from io import StringIO from dune import generator code=""" #include template void run(const Basis& basis, CallBack& callBack) { Dune::Functions::forEachBoundaryDOF >(basis, callBack); } """ dune.generator.algorithm.run("run",StringIO(code), basis, callBack) dune-functions-2.11.0+dfsg/python/dune/functions/globalbasis.py000066400000000000000000000044421513634022200245750ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later from .tree import Composite, DG, Lagrange, Nedelec, Power, RaviartThomas, Tree duneFunctionsLayouts = {"lexicographic": "Lexicographic", "interleaved": "Interleaved"} def indexMergingStrategy(blocked, layout): return "Dune::Functions::BasisFactory::" + ("Blocked" if blocked else "Flat") + duneFunctionsLayouts[layout] def preBasisTypeName(tree, gridViewTypeName): assert isinstance(tree, Tree) if isinstance(tree, Lagrange): return "Dune::Functions::LagrangePreBasis< " + gridViewTypeName + " , " + str(tree.order) + " >" elif isinstance(tree, DG): raise Exception(repr(tree) + " not supported by dune-functions.") elif isinstance(tree, Nedelec): return "Dune::Functions::NedelecPreBasis< " + gridViewTypeName + ", double, " + str(tree.kind) + ", " + str(tree.order) + " >" elif isinstance(tree, RaviartThomas): return "Dune::Functions::RaviartThomasPreBasis< " + gridViewTypeName + ", " + str(tree.order) + " >" elif isinstance(tree, Composite): IMS = indexMergingStrategy(tree.blocked, tree.layout) ChildPreBases = " , ".join(preBasisTypeName(c, gridViewTypeName) for c in tree.children) return "Dune::Functions::CompositePreBasis< " + IMS + " , " + ChildPreBases + " >" elif isinstance(tree, Power): IMS = indexMergingStrategy(tree.blocked, tree.layout) ChildPreBasis = preBasisTypeName(tree.children[0], gridViewTypeName) return "Dune::Functions::PowerPreBasis< " + IMS + " , " + ChildPreBasis + " , " + str(tree.exponent) + " >" else: raise Exception("Unknown type of tree: " + repr(tree)) def defaultGlobalBasis(gridView, tree): from dune.functions import load headers = ["powerbasis", "compositebasis", "lagrangebasis", "nedelecbasis", "raviartthomasbasis", "subspacebasis", "defaultglobalbasis"] includes = [] includes += list(gridView.cppIncludes) includes += ["dune/functions/functionspacebases/" + h + ".hh" for h in headers] typeName = "Dune::Functions::DefaultGlobalBasis< " + preBasisTypeName(tree, gridView.cppTypeName) + " >" return load(includes, typeName).GlobalBasis(gridView) dune-functions-2.11.0+dfsg/python/dune/functions/subspacebasis.py000066400000000000000000000032031513634022200251340ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later from dune.generator.generator import SimpleGenerator from dune.common.hashit import hashIt # A subspace basis for some descendent node, can be obtained # by passing the indices of the corresponding tree path # as individual arguments. Passing a tree path at once is # currently not supported. # # Netsed calls to subspaceBasis(subspaceBasis(basis, i), j) # will create a nested subspace basis. This works fine, but # is slightly inconsistent with C++ where this is automatically # resolved to subspaceBasis(basis, i, j). def subspaceBasis(basis, *args): includes = [] includes += list(basis.cppIncludes) includes += ["dune/functions/functionspacebases/subspacebasis.hh"] includes += ["dune/python/functions/subspacebasis.hh"] # In contrast to C++, the generated wrapper of SubspaceBasis # is only constructed from the root basis and no tree path # is passed. This is possible, because we only use fully # static tree path types that can be default constructed. prefixPathTypeName = "Dune::TypeTree::TreePath<" prefixPathTypeName += ",".join("Dune::index_constant<"+str(i)+">" for i in args) prefixPathTypeName += ">" typeName = "Dune::Functions::SubspaceBasis< " + basis.cppTypeName+ "," + prefixPathTypeName + " >" generator = SimpleGenerator("SubspaceBasis", "Dune::Python") moduleName = "subspaceBasis_" + hashIt(typeName) module = generator.load(includes, typeName, moduleName) return module.SubspaceBasis(basis) dune-functions-2.11.0+dfsg/python/dune/functions/tree.py000066400000000000000000000055411513634022200232530ustar00rootroot00000000000000# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later import warnings class Tree(object): def __init__(self, name, children=None): self.name = name self.children = [] if children is not None: assert(all(isinstance(c, Tree) for c in children)) self.children = list(children) def __mul__(self, other): return Composite(self, other) def __pow__(self, p): return Power(self, p) class Lagrange(Tree): def __init__(self, order): Tree.__init__(self, "Lagrange") self.order = order def __repr__(self): return "Lagrange<" + str(self.order) + ">" class DG(Tree): def __init__(self, order, dimRange=1): Tree.__init__(self, "DG") self.order = order self.dimRange = dimRange def __repr__(self): if self.dimRange == 1: return "DG<" + str(self.order) + ">" else: return "DG<" + str(self.order) + ">^" + str(self.dimRange) class Nedelec(Tree): def __init__(self, kind, order): Tree.__init__(self, "Nedelec") self.kind = kind self.order = order def __repr__(self): return "Nedelec<" + str(self.kind) + "," + str(self.order) + ">" class RaviartThomas(Tree): def __init__(self, order): Tree.__init__(self, "RaviartThomas") self.order = order def __repr__(self): return "RaviartThomas<" + str(self.order) + ">" class Composite(Tree): def __init__(self, *args, **kwargs): assert len(args) > 0 Tree.__init__(self, "Composite", args) # Please do not change the defaults here without taking the C++ interface into account self.blocked = kwargs.get("blocked", False) self.layout = kwargs.get("layout", "lexicographic") def __repr__(self): return "(" + " * ".join(repr(c) for c in self.children) + ")" class Power(Tree): def __init__(self, children, exponent, **kwargs): assert children is not None Tree.__init__(self, "Power", [children]) assert len(self.children) == 1 self.exponent = exponent # Please do not change the defaults here without taking the C++ interface into account self.blocked = kwargs.get("blocked", False) if (not "layout" in kwargs): warnings.warn('''The default layout of Power nodes will change from 'lexicographic' to 'interleaved' in release 2.11. Please add the argument 'layout="lexicographic"' explicitly to retain the old behavior!''') self.layout = kwargs.get("layout", "lexicographic") def __repr__(self): if self.exponent == 1: return repr(self.children[0]) else: return "[" + repr(self.children[0]) + "]^" + str(self.exponent)