Commit fcf5e668 authored by Santiago Ospina De Los Ríos's avatar Santiago Ospina De Los Ríos
Browse files

Merge branch 'feature/avoid-ordering-transformation-for-non-root-gfs' into feature/copy-fsds

parents 2d2d0dff 96ccd121
Pipeline #38786 failed with stage
in 8 minutes and 41 seconds
......@@ -32,6 +32,78 @@
@ingroup PDELab
@addtogroup GridFunctionSpace
@{
@brief Representation of an function space in a grid
## Overview
A Grid Function Space (often `GFS`) is a representation of an function space
in a grid. There are several attributes that a GFS has and one has to be
aware when using them:
## Tree Structure
A GFS is a tree of nodes that describe different type of space products.
A leaf node represents a space spanned by local finite elements. This is
acheived with the help of an EntitySet and FiniteElementMap wich gives the
node a local finite element for each entity.
## Ordering
The root node of the tree contains an @ref Ordering and an entity set used
for assembly. For a given GFS tree, only one ordering is allowed to be
active.
## Construction
Grid Function Spaces are constructed bottom-up. That is, we first declare
the leaf nodes and then compose them into product spaces:
@code{.cpp}
// define some finite element map
using FEM = ...;
auto fem = std::make_shared<FEM>(...);
// create entity set
using EntitySet = Dune::PDELab::AllEntitySet<GV>;
EntitySet es{grid.leafGridView()};
// make leaf grid function space (bottom)
using LeafGFS = Dune::PDELab::UnorderedGridFunctionSpace<EntitySet, FEM, CON, VBE>;
LeafGFS gfs_1{es, fem};
LeafGFS gfs_2{es, fem};
// make a product space them with a power GFS (up)
using PowerGFS = Dune::PDELab::UnorderedPowerGridFunctionSpace<LeafGFS, 2>;
PowerGFS pgfs{gfs_1, gfs_2};
// enable ordering on root node
using GFS = Dune::PDELab::OrderedGridFunctionSpace<PowerGFS, EntitySet>;
GFS gfs{pgfs, es};
@endcode
## Eager and Lazy construction
The example above shows an example of eager construction of GFS. That is,
by the time the `gfs` is constructed, the ordering is eagerly
initializated and ready for usage. However, a lazy initialization is also
possible and is indeed the traditional way of using PDELab:
@code{.cpp}
// make leaf grid function space (bottom)
using LeafGFS = Dune::PDELab::GridFunctionSpace<EntitySet, FEM, CON, VBE>;
// this leaf node contains an ordering, but since it may become part of
// another bigger tree, it is not yet initializated.
LeafGFS gfs_1{es, fem};
LeafGFS gfs_2{es, fem};
// make a product space them with a power GFS (up)
using PowerGFS = Dune::PDELab::PowerGridFunctionSpace<LeafGFS, 2>;
// this node also contains an ordering which is not yet initializated.
PowerGFS gfs{gfs_1, gfs_2};
// when we use the ordering, it gets initializated internally and is ready to use.
std::cout << "DOF count: " << gfs.ordering().size() << std::endl;
@endcode
@}
@addtogroup Ordering
@{
@brief Map between DOFIndex and ContainerIndex
......
......@@ -52,7 +52,7 @@ namespace Dune {
#endif // DOXYGEN
namespace Impl {
//! internal utility to fetch the first leaf node on a type-tree
template<class Node0, class... Others>
decltype(auto) first_leaf(Node0&& node0, Others&&...) {
using TypeTree::child;
......@@ -62,6 +62,7 @@ namespace Dune {
return first_leaf(child(std::forward<Node0>(node0),Indices::_0));
}
//! internal utility to fetch the first leaf node type on a type-tree
template<class... Nodes>
using FirstLeaf = std::decay_t<decltype(first_leaf(std::declval<Nodes>()...))>;
}
......
......@@ -47,6 +47,7 @@ namespace Dune {
};
//! \}
} // namespace PDELab
} // namespace Dune
......
......@@ -116,6 +116,27 @@ namespace Dune {
using Base = OrderedGridFunctionSpace<UnorderedCompositeGridFunctionSpace<Backend,OrderingTag,Children...>, typename Impl::FirstLeaf<Children...>::Traits::EntitySet>;
public:
struct Traits : public Base::Traits {
enum {
//! \brief True if this grid function space is composed of others.
isComposite
[[deprecated("This enum will be removed after PDELab 2.9.")]] = 1,
//! \brief number of child spaces
noChilds
[[deprecated("This enum will be removed after PDELab 2.9.")]] =
sizeof...(Children)
};
[[deprecated(
"This enum will be removed after PDELab 2.9. Use degree() from the "
"TypeTree base class")]] const static std::size_t CHILDREN =
sizeof...(Children);
//! \brief mapper
using MapperType [[deprecated("This enum will be removed after PDELab "
"2.9. Use OrderingTag instead.")]] =
typename Base::Traits::OrderingTag;
};
template<class... Args>
CompositeGridFunctionSpace(Args&&... args)
......
......@@ -25,7 +25,7 @@ namespace Dune {
//! \addtogroup GridFunctionSpace grid function space
//! \ingroup PDELab
//! \{
//! \}
namespace Experimental {
......@@ -619,6 +619,8 @@ namespace Dune {
} // namespace Experimental
//! \}
} // namespace PDELab
} // namespace Dune
......
......@@ -373,6 +373,8 @@ namespace Dune {
{}
};
//! \}
} // namespace PDELab
} // namespace Dune
......
......@@ -106,8 +106,8 @@ namespace Dune {
namespace impl {
// helper class with minimal dependencies. Orderings keep a pointer to this structure and populate it
// during their update procedure.
//! Helper class with minimal dependencies.
// Orderings keep a pointer to this structure and populate it during their update procedure.
template<typename size_type>
class GridFunctionSpaceOrderingData
......@@ -171,10 +171,14 @@ namespace Dune {
#endif // DOXYGEN
//! Checks whether an object has function called ordering.
// Used to distinguish betwen unordered and ordered grid function spaces
template<class GFS>
constexpr bool HasOrdering = Std::is_detected<impl::HasOrderingConcept,GFS>{};
namespace impl {
//! Helper function to extract ordering data from a grid function space
// In particular, if the space is not ordered, it creates new ordering data.
template<class GFS>
auto gfs_data(GFS& gfs) {
using SizeType = typename GFS::Traits::SizeType;
......@@ -190,22 +194,24 @@ namespace Dune {
struct GridFunctionSpaceNodeTraits
{
//! vector backend
[[deprecated]] typedef B BackendType;
[[deprecated("Use Backend instead. This alias will be removed after "
"PDELab 2.9.")]] typedef B BackendType;
typedef B Backend;
//! vector backend
using Backend = B;
//! short cut for size type exported by Backend
typedef typename B::size_type SizeType;
using SizeType = typename B::size_type;
//! tag describing the ordering.
/**
* The tag type may contain additional constants and typedefs to
* control the behavior of the created ordering.
*/
typedef O OrderingTag;
using OrderingTag = O;
};
//! Base class for every grid function space node
template<typename GFSTraits>
class GridFunctionSpaceNode
{
......@@ -261,7 +267,9 @@ namespace Dune {
template<typename GFS, typename GFSTraits>
class [[deprecated]] GridFunctionSpaceBase
class [[deprecated("Use GridFunctionSpaceNode instead."
"This class will be removed after PDELab 2.9.")]]
GridFunctionSpaceBase
: public GridFunctionSpaceNode<GFSTraits>
, public impl::GridFunctionSpaceOrderingData<typename GFSTraits::SizeType>
{
......@@ -279,6 +287,8 @@ namespace Dune {
using Base::Base;
};
//! \}
} // namespace PDELab
} // namespace Dune
......
......@@ -307,16 +307,16 @@ namespace Dune {
template<typename GFS, typename DOFIndex>
struct GridViewLocalFunctionSpaceBaseTraits : public LocalFunctionSpaceBaseTraits<GFS,DOFIndex>
{
// //! \brief Type of the grid view that the underlying grid function space is defined on.
// using GridView = typename GFS::Traits::GridView;
//! \brief Type of the grid view that the underlying grid function space is defined on.
using GridView [[deprecated("This alias will be removed after PDELab 2.9.")]] = typename Impl::FirstLeaf<GFS>::Traits::GridView;
// //! \brief Type of the grid view that the underlying grid function space is defined on.
// using GridViewType /*[[deprecated]]*/ = GridView;
//! \brief Type of the grid view that the underlying grid function space is defined on.
using GridViewType [[deprecated("This alias will be removed after PDELab 2.9.")]] = GridView;
// using EntitySet = typename GFS::Traits::EntitySet;
using EntitySet [[deprecated("This alias will be removed after PDELab 2.9.")]] = typename Impl::FirstLeaf<GFS>::Traits::EntitySet;
// //! \brief Type of codim 0 entity in the grid
// using Element = typename EntitySet::Element;
//! \brief Type of codim 0 entity in the grid
using Element [[deprecated("This alias will be removed after PDELab 2.9. Use a template argument instead.")]] = typename EntitySet::Element;
};
template <typename GFS, typename DOFIndex>
......@@ -426,8 +426,8 @@ namespace Dune {
{}
//! \brief bind local function space to entity
template<bool fast = false>
void bind (const typename Traits::Element& e, std::integral_constant<bool,fast> fast_ = std::integral_constant<bool,fast>{})
template<typename Element, bool fast = false>
void bind (const Element& e, std::integral_constant<bool,fast> fast_ = std::integral_constant<bool,fast>{})
{
// call method on base class, this avoid the barton neckman trick
BaseT::bind(*this,e,fast_);
......@@ -590,8 +590,8 @@ namespace Dune {
{}
//! \brief bind local function space to entity
template<bool fast = false>
void bind (const typename Traits::Element& e, std::integral_constant<bool,fast> fast_ = std::integral_constant<bool,fast>{})
template<typename Element, bool fast = false>
void bind (const Element& e, std::integral_constant<bool,fast> fast_ = std::integral_constant<bool,fast>{})
{
// call method on base class, this avoid the barton neckman trick
BaseT::bind(*this,e,fast_);
......
......@@ -4,10 +4,8 @@
#define DUNE_PDELAB_GRIDFUNCTIONSPACE_ORDEREDGRIDFUNCTIONSPACE_HH
#include <dune/pdelab/gridfunctionspace/datahandleprovider.hh>
#include <dune/pdelab/ordering/transformations.hh>
#include <dune/pdelab/ordering/lexicographicordering.hh>
#include <dune/pdelab/ordering/decorator.hh>
#include <dune/pdelab/ordering/entityblockedlocalordering.hh>
#include <dune/pdelab/constraints/common/constraintstransformation.hh>
#include <dune/pdelab/constraints/noconstraints.hh>
namespace Dune {
namespace PDELab {
......@@ -15,6 +13,10 @@ namespace PDELab {
template <class GFS, class Tag> class LocalFunctionSpace;
template<class LFS, class C, bool fast> class LFSIndexCache;
//! \addtogroup GridFunctionSpace grid function space
//! \ingroup PDELab
//! \{
namespace Impl {
/**
* @brief Constraint type accumulator
......@@ -72,28 +74,26 @@ public:
} // namespace Impl
/*
The OrderedGridFunctionSpace intends to demangle the strong relationship that
pdelab currently has between trees of grid function spaces and its ordering.
The problem is that there is not a clear separation of concerns and is very
difficult to reason about one without the other. Moreover, the construction at
compile time and initialization at run time follow different order. This make
the two of them very difficult to debug. In particular, the
OrderedGridFunctionSpace intends to perform all the interactions between the grid
function space and the ordering.
The plan for the future is that the user uses UnorderedXXXGridFunctionSpace
to build a tree of spaces and when this is finished, it pipes the tree to the
OrderedGridFunctionSpace. In other words, we want to deprecate XXXGridFunctionSpace.
*/
/**
* @brief Add ordering and data handler to an unordered grid function space
* @details This class extends the grid function space interface to have
* an ordering and a data handler for parallel communication. In particular,
* this is intened to only be used on the root node.
* an ordering and a data handler for parallel communication.
*
* The OrderedGridFunctionSpace intends to demangle the strong relationship that
* pdelab previously has between trees of grid function spaces and its ordering.
* The problem is that there was not a clear separation of concerns and is very
* difficult to reason about one without the other. Moreover, the construction
* at compile time and initialization at run time follow different order
* (lazy initialization). This makes the two of them very difficult to debug.
* The OrderedGridFunctionSpace intends to perform all the interactions between
* the grid function space and the ordering.
*
* This class, in particular, separates the initialization of the ordering in
* eager and lazy initialization. If you want to understand how grid function
* spaces interact with orderings, try to do so with the eager initialization
* where _every_ node un the UnorderedGFS type has no ordering.
*
* @tparam UnorderedGFS A grid function space that has no ordering
* @tparam UnorderedGFS A grid function space that may not have an ordering
*
* \see Ordering
*/
......@@ -102,14 +102,16 @@ class OrderedGridFunctionSpace
: public UnorderedGFS
, public DataHandleProvider<OrderedGridFunctionSpace<UnorderedGFS, AssemblyEntitySet>>
{
static_assert(isEntitySet<AssemblyEntitySet>{});
static_assert(
isEntitySet<AssemblyEntitySet>{},
"OrderedGridFunctionSpace does not accept grid views, use entity sets!");
using ordering_transformation =
TypeTree::TransformTree<UnorderedGFS, gfs_to_ordering<UnorderedGFS>>;
using GFSData = impl::GridFunctionSpaceOrderingData<typename UnorderedGFS::Traits::SizeType>;
// the backwards compatible case requires us to access other gfs data to check initialization
// ensure that we can access other ordered nodes data (lazy ordering only).
template<class,class>
friend class ::Dune::PDELab::OrderedGridFunctionSpace;
......@@ -125,9 +127,20 @@ public:
using Ordering = typename ordering_transformation::Type;
};
using OrderingTag [[deprecated]] = typename Traits::OrderingTag;
using OrderingTag [[deprecated("This alias will be removed after "
"PDELab 2.9. Use Traits::OrderingTag.")]] =
typename Traits::OrderingTag;
// in this case othe order is created and updated at construction time
/**
* @brief Construct a new Eager Ordered Grid Function Space object
* @details This constructor initializes an ordering object eagerly.
* Notice that when this constructor is used, this node **cannot** be used to
* construct bigger grid function space trees. This is because it is only
* allowed to have one ordering in the whole tree.
*
* @param ugfs Unordered grid function space
* @param entity_set Entity set used for assembly algorithms
*/
OrderedGridFunctionSpace(const UnorderedGFS& ugfs, AssemblyEntitySet entity_set)
: UnorderedGFS{ugfs}
, _entity_set{entity_set}
......@@ -136,21 +149,34 @@ public:
if constexpr (UnorderedGFS::isLeaf)
static_assert(std::is_same<typename UnorderedGFS::Traits::EntitySet, AssemblyEntitySet>{});
// new behavior: we are the only node in the tree that is ordered. But
// because we allow the whole tree to be ordered in order to be backwards
// compatible, we need to check that when using this constructor, the whole tree is unordered
// Eager behavior: we need to check that no other node is initialized.
TypeTree::forEachNode(static_cast<UnorderedGFS&>(*this),[&](auto& gfs_node, auto& path){
if constexpr (HasOrdering<decltype(gfs_node)>)
if (gfs_node.data()->_initialized)
DUNE_THROW(GridFunctionSpaceHierarchyError,"initialized space cannot become part of larger GridFunctionSpace tree");
DUNE_THROW(GridFunctionSpaceHierarchyError,
"Initialized space cannot become part of larger GridFunctionSpace tree");
});
reset_root_flag();
// set up ordering eagerly
create_ordering();
update_ordering();
}
// backwards compatible constructor
// in this case, the ordering is delayed to be constructed and updated until the ordering is called or explicitely constructed
/**
* @brief Construct a new Lazy Ordered Grid Function Space object
* @details This constructor **does not** initialize an ordering object,
* instead, this process is delayed until the ordering is required.
* Notice that when this constructor is used, this node **can** be used to
* construct bigger grid function space trees. However, only the root node
* will be allowed to hold an ordering.
*
* @tparam Args Argument types of the UnorderedGFS constructor
* @param args Argument types of the UnorderedGFS constructor
*/
template<class... Args>
OrderedGridFunctionSpace(std::in_place_t, Args&&... args)
: UnorderedGFS(std::forward<Args>(args)...)
......@@ -164,7 +190,7 @@ public:
}
//! Ordering tree type TODO deprecate in the future
//! Ordering tree type
using Ordering = typename Traits::Ordering;
//! extract type for storing constraints
......@@ -244,10 +270,10 @@ public:
Ordering &ordering() { return *orderingStorage(); }
//! Direct access to the storage of the DOF ordering.
std::shared_ptr<const Ordering> orderingStorage() const { create_ordering_in_place(); return _ordering; }
std::shared_ptr<const Ordering> orderingStorage() const { lazy_create_ordering(); return _ordering; }
//! Direct access to the storage of the DOF ordering.
std::shared_ptr<Ordering> orderingStorage() { create_ordering_in_place(); return _ordering; }
std::shared_ptr<Ordering> orderingStorage() { lazy_create_ordering(); return _ordering; }
//! Update the indexing information of the GridFunctionSpace.
/**
......@@ -275,8 +301,8 @@ public:
private:
void reset_root_flag() {
// the backwards compatible behavior: every node of this tree is also
// ordered, thus, we need to make sure that no two of them are initialized at the same time
// lazy behavior: every node of this tree may also be ordered, thus, we need
// to make sure that no two of them are initialized at the same time.
TypeTree::forEachNode(*this,[&](auto& gfs_node, auto& path){
if constexpr (HasOrdering<decltype(gfs_node)>) {
if (gfs_node.data()->_initialized and gfs_node.data()->_is_root_space and not gfs_node.isLeaf)
......@@ -288,7 +314,7 @@ private:
void update_ordering() {
check_root_space();
create_ordering_in_place();
lazy_create_ordering();
_ordering->update();
TypeTree::forEachNode(*_ordering, [&](auto& ordering_node, auto& path){
// bool is_root = (path.size() == 0);
......@@ -313,8 +339,10 @@ private:
_ordering->_gfs_data = _data;
}
void create_ordering_in_place() const {
// we do this only to be backwards compatible
void lazy_create_ordering() const {
// Since the usage may come from a const object, we need to const_cast to
// mutate ourselves. Yes, this is as bad as it sounds but we need some
// backwards compatibility.
if (not _ordering) {
const_cast<OrderedGridFunctionSpace*>(this)->create_ordering();
const_cast<OrderedGridFunctionSpace*>(this)->update_ordering();
......@@ -334,6 +362,8 @@ private:
std::shared_ptr<GFSData> _data;
};
// \}
} // namespace PDELab
} // namespace Dune
......
......@@ -19,6 +19,8 @@
#include <dune/pdelab/ordering/lexicographicordering.hh>
#include <dune/pdelab/ordering/entityblockedlocalordering.hh>
#warning "This header will be deprecated after 2.9"
namespace Dune {
namespace PDELab {
......@@ -120,8 +122,7 @@ namespace Dune {
};
//! \}
}
}
//! \}
#endif // DUNE_PDELAB_GRIDFUNCTIONSPACE_POWERCOMPOSITEGRIDFUNCTIONSPACEBASE_HH
......@@ -52,11 +52,13 @@ namespace Dune {
typedef GridFunctionSpaceNode<GridFunctionSpaceNodeTraits<Backend,OrderingTag>> ImplementationBase;
//! helper function to create storage at initialization
static std::array<std::shared_ptr<T>,k> make_storage(std::initializer_list<std::reference_wrapper<T>> list) {
std::array<std::shared_ptr<T>,k> storage;
assert(list.size() == k or list.size() == 1);
assert(list.size() == k or list.size() == 1 && "Wrong number of arguments, use k or 1 arguments");
auto it = begin(list);
for (std::size_t i = 0; i < k; ++i) {
// notice that we copy content because gfs have value semantics
storage[i] = std::make_shared<T>(it->get());
if (list.size() == k) ++it;
}
......@@ -64,12 +66,13 @@ namespace Dune {
return storage;
}
//! helper function to create storage at initialization
static std::array<std::shared_ptr<T>,k> make_storage(std::initializer_list<std::shared_ptr<T>> list) {
std::array<std::shared_ptr<T>,k> storage;
if(list.size() == 1)
if (list.size() == 1)
std::fill(storage.begin(), storage.end(), list.front());
else {
assert(list.size() == k);
assert(list.size() == k && "Wrong number of arguments, use k or 1 arguments");
std::move(begin(list), end(list), begin(storage));
}
return storage;
......@@ -213,6 +216,25 @@ namespace Dune {
using Base = OrderedGridFunctionSpace<UnorderedPowerGridFunctionSpace<T,k,Backend,OrderingTag>, typename Impl::FirstLeaf<T>::Traits::EntitySet>;
public:
struct Traits : public Base::Traits {
enum {
//! \brief True if this grid function space is composed of others.
isComposite
[[deprecated("This enum will be removed after PDELab 2.9.")]] = 1,
//! \brief number of child spaces
noChilds
[[deprecated("This enum will be removed after PDELab 2.9.")]] = k
};
[[deprecated(
"This enum will be removed after PDELab 2.9. Use degree() from the "
"TypeTree base class")]] const static std::size_t CHILDREN = k;
//! \brief mapper
using MapperType [[deprecated("This enum will be removed after PDELab "
"2.9. Use OrderingTag instead.")]] =
typename Base::Traits::OrderingTag;
};
template<class... Args>
PowerGridFunctionSpace(Args&&... args)
......
......@@ -125,7 +125,7 @@ namespace Dune {
}
};
//! \copydoc UnorderedVectorGridFunctionSpace
template<typename ES,
typename FEM,
std::size_t k,
......@@ -137,32 +137,52 @@ namespace Dune {
class VectorGridFunctionSpace
: public OrderedGridFunctionSpace<UnorderedVectorGridFunctionSpace<impl::EntitySet<ES>,FEM,k,Backend,LeafBackend,Constraints,OrderingTag,LeafOrderingTag>,impl::EntitySet<ES>>
{
using OrderedGFS = OrderedGridFunctionSpace<UnorderedVectorGridFunctionSpace<impl::EntitySet<ES>,FEM,k,Backend,LeafBackend,Constraints,OrderingTag,LeafOrderingTag>,impl::EntitySet<ES>>;
using LeafGFS = typename OrderedGFS::ChildType;
using Base = OrderedGridFunctionSpace<UnorderedVectorGridFunctionSpace<impl::EntitySet<ES>,FEM,k,Backend,LeafBackend,Constraints,OrderingTag,LeafOrderingTag>,impl::EntitySet<ES>>;
using LeafGFS = typename Base::ChildType;
public:
struct Traits : public Base::Traits {
enum {
//! \brief True if this grid function space is composed of others.
isComposite
[[deprecated("This enum will be removed after PDELab 2.9.")]] = 1,
//! \brief number of child spaces
noChilds
[[deprecated("This enum will be removed after PDELab 2.9.")]] = k
};
[[deprecated(
"This enum will be removed after PDELab 2.9. Use degree() from the "
"TypeTree base class")]] const static std::size_t CHILDREN = k;
//! \brief mapper
using MapperType [[deprecated("This enum will be removed after PDELab "
"2.9. Use OrderingTag instead.")]] =
typename Base::Traits::OrderingTag;