Commit 46684b80 authored by Praetorius, Simon's avatar Praetorius, Simon
Browse files

Merge branch 'feature/data-transfer-cleanup' into 'master'

Make DataTransfer a customization point

Closes #4 and #15

See merge request amdis/amdis!52
parents ca5c3312 4f5731a9
......@@ -17,6 +17,9 @@
set of entities the basis can be bound to. This set is typically related to the
linear-algebra backend traits' `PartitionSet`.
- Make the function `ProblemInstat::oldSolutionVector()` also available with mutable access.
- A data transfer strategy based on a simple caching and interpolation is added, called
`SimpleDataTransfer`. It can be selected with the datatranser tag `tag::simple_datatransfer`.
Note, this datatransfer is restricted to grids with a single GeometryType.
### Fixed
......@@ -39,6 +42,9 @@
- Make `valueOf` a friend function of `DOFVector`.
- Traversal in interpolators and assemblers is replaced by the new introduced
`entitySet()` function.
- Make the `DataTransfer` a customization point by providing a class `DataTransferFactory`
that can be specialized with a `<tag>` and must implement a `create(basis,coefficients)`
static function.
### Removed
......
......@@ -32,7 +32,6 @@ install(FILES
CreatorInterface.hpp
CreatorMap.hpp
DataTransfer.hpp
DataTransfer.inc.hpp
DirichletBC.hpp
DirichletBC.inc.hpp
DOFVector.hpp
......@@ -74,6 +73,7 @@ install(FILES
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amdis)
add_subdirectory("common")
add_subdirectory("datatransfers")
add_subdirectory("functions")
add_subdirectory("gridfunctions")
add_subdirectory("interpolators")
......
......@@ -13,6 +13,7 @@
#include <amdis/common/Concepts.hpp>
#include <amdis/common/DerivativeTraits.hpp>
#include <amdis/common/TypeTraits.hpp>
#include <amdis/datatransfers/InterpolationDataTransfer.hpp>
#include <amdis/functions/GlobalBasis.hpp>
#include <amdis/typetree/TreePath.hpp>
......@@ -58,40 +59,45 @@ namespace AMDiS
/// The type of the elements of the DOFVector
using value_type = T;
/// Wrapper for the implementation of the transfer of data during grid adaption
using DataTransfer = DataTransferWrapper<Self>;
/// Collection of parameter for the linear-algebra backend
using Traits = TraitsType;
template <class Self>
static auto& coefficients(Self& self)
{
if constexpr(std::is_const_v<Self>)
return static_cast<Coefficients const&>(self);
else
return static_cast<Coefficients&>(self);
}
public:
/// (1) Constructor. Stores the shared_ptr of the basis and creates a new
/// DataTransfer.
DOFVector(std::shared_ptr<GB> const& basis,
DataTransferOperation op = DataTransferOperation::INTERPOLATE)
template <class DataTransferTag = tag::interpolation_datatransfer>
DOFVector(std::shared_ptr<GB> const& basis, DataTransferTag = {})
: Coefficients(*basis)
, Observer<event::preAdapt>(basis->gridView().grid())
, Observer<event::adapt>(*basis)
, Observer<event::postAdapt>(basis->gridView().grid())
, dataTransfer_(op, basis)
, dataTransfer_(DataTransferFactory<DataTransferTag>::create(*basis, coefficients(*this)))
, basis_(basis)
{}
/// (2) Constructor. Forwards to (1) by wrapping reference into non-destroying
/// shared_ptr, see \ref Dune::wrap_or_move.
template <class GB_,
template <class GB_, class DataTransferTag = tag::interpolation_datatransfer,
REQUIRES(Concepts::Similar<GB_,GB>)>
DOFVector(GB_&& basis, DataTransferOperation op = DataTransferOperation::INTERPOLATE)
: DOFVector(Dune::wrap_or_move(FWD(basis)), op)
DOFVector(GB_&& basis, DataTransferTag tag = {})
: DOFVector(Dune::wrap_or_move(FWD(basis)), tag)
{}
/// (3) Constructor. Forwards to (1) by creating a new basis from a dune-functions
/// pre-basis factory.
template <class GV, class PBF,
template <class GV, class PBF, class DataTransferTag = tag::interpolation_datatransfer,
REQUIRES(Concepts::PreBasisFactory<PBF, GV>)>
DOFVector(GV const& gridView, PBF const& preBasisFactory,
DataTransferOperation op = DataTransferOperation::INTERPOLATE)
: DOFVector(std::make_shared<GB>(gridView, preBasisFactory), op)
DOFVector(GV const& gridView, PBF const& preBasisFactory, DataTransferTag tag = {})
: DOFVector(std::make_shared<GB>(gridView, preBasisFactory), tag)
{}
/// Return the global basis
......@@ -102,12 +108,12 @@ namespace AMDiS
Coefficients const& coefficients() const
{
return static_cast<Coefficients const&>(*this);
return coefficients(*this);
}
Coefficients& coefficients()
{
return static_cast<Coefficients&>(*this);
return coefficients(*this);
}
/// Write DOFVector to file
......@@ -131,32 +137,28 @@ namespace AMDiS
using Coefficients::resizeZero;
/// Return the associated DataTransfer object
DataTransfer const& dataTransfer() const
auto const& dataTransfer() const
{
return dataTransfer_;
}
/// Return the associated DataTransfer object
DataTransfer& dataTransfer()
auto& dataTransfer()
{
return dataTransfer_;
}
/// Assign the DataTransfer object
void setDataTransfer(DataTransfer const& dataTransfer)
{
dataTransfer_ = dataTransfer;
}
void setDataTransfer(DataTransfer&& dataTransfer)
void setDataTransfer(DataTransfer<GB,Coefficients> dataTransfer)
{
dataTransfer_ = std::move(dataTransfer);
}
/// Create a new DataTransfer object based on the operation type
void setDataTransfer(DataTransferOperation op)
template <class DataTransferTag>
void setDataTransfer(DataTransferTag tag)
{
setDataTransfer(DataTransfer(op, basis_));
setDataTransfer(DataTransferFactory<DataTransferTag>::create(basis(), coefficients()));
}
......@@ -221,7 +223,7 @@ namespace AMDiS
/// method of the \ref DataTransfer object.
void updateImpl(event::preAdapt e) override
{
dataTransfer_.preAdapt(*this, e.value);
dataTransfer_.preAdapt(*basis_, coefficients(), e.value);
}
/// Override of Observer update(event::adapt) method. Redirects to adapt method
......@@ -230,20 +232,20 @@ namespace AMDiS
{
assert(e.value);
resize();
dataTransfer_.adapt(*this);
dataTransfer_.adapt(*basis_, coefficients());
}
/// Override of Observer update(event::postAdapt) method. Redirects to postAdapt
/// method of the \ref DataTransfer object.
void updateImpl(event::postAdapt) override
{
dataTransfer_.postAdapt(*this);
dataTransfer_.postAdapt(coefficients());
}
private:
/// Data interpolation when the grid changes, set by default
/// to \ref DataTransferOperation::INTERPOLATE.
DataTransfer dataTransfer_;
DataTransfer<GB,Coefficients> dataTransfer_;
std::shared_ptr<GlobalBasis const> basis_;
};
......@@ -251,12 +253,23 @@ namespace AMDiS
// deduction guides
template <class GB>
DOFVector(GB&& basis, DataTransferOperation op = DataTransferOperation::INTERPOLATE)
DOFVector(GB&& basis)
-> DOFVector<Underlying_t<GB>>;
template <class GB, class DataTransferTag,
REQUIRES(std::is_base_of_v<tag::datatransfer, DataTransferTag>)>
DOFVector(GB&& basis, DataTransferTag)
-> DOFVector<Underlying_t<GB>>;
template <class GV, class PBF>
DOFVector(GV const& gridView, PBF const& pbf,
DataTransferOperation op = DataTransferOperation::INTERPOLATE)
template <class GV, class PBF,
REQUIRES(Concepts::PreBasisFactory<PBF, GV>)>
DOFVector(GV const& gridView, PBF const& pbf)
-> DOFVector<decltype(GlobalBasis{gridView,pbf})>;
template <class GV, class PBF, class DataTransferTag,
REQUIRES(Concepts::PreBasisFactory<PBF, GV>),
REQUIRES(std::is_base_of_v<tag::datatransfer, DataTransferTag>)>
DOFVector(GV const& gridView, PBF const& pbf, DataTransferTag)
-> DOFVector<decltype(GlobalBasis{gridView,pbf})>;
......@@ -270,11 +283,12 @@ namespace AMDiS
* DOFVector. The default is interpolation of the data to the new grid. See
* \ref DataTransferOperation for more options.
**/
template <class ValueType = double, class GB>
template <class ValueType = double, class GB,
class DataTransferTag = tag::interpolation_datatransfer>
DOFVector<Underlying_t<GB>, ValueType>
makeDOFVector(GB&& basis, DataTransferOperation op = DataTransferOperation::INTERPOLATE)
makeDOFVector(GB&& basis, DataTransferTag tag = {})
{
return {FWD(basis), op};
return {FWD(basis), tag};
}
} // end namespace AMDiS
......
......@@ -3,215 +3,142 @@
#include <map>
#include <memory>
#include <dune/grid/common/mcmgmapper.hh>
#include <dune/common/concept.hh>
#include <dune/functions/common/typeerasure.hh>
#include <amdis/Output.hpp>
#include <amdis/typetree/TreeContainer.hpp>
namespace AMDiS
{
/**
* \addtogroup Adaption
* @{
**/
namespace AMDiS {
namespace Impl {
enum class DataTransferOperation {
NO_OPERATION = 0,
INTERPOLATE = 1
template <class B, class C>
struct DataTransferDefinition
{
// Definition of the interface a DataTransfer class must fulfill
struct Interface
{
virtual ~Interface() = default;
virtual void preAdapt(B const& basis, C const& container, bool mightCoarsen) = 0;
virtual void adapt(B const& basis, C& container) = 0;
virtual void postAdapt(C& container) = 0;
};
/// \brief Interface for Containers allowing data transfer between grid changes.
template <class Container>
class DataTransferInterface
// Templatized implementation of the interface
template <class Impl>
struct Model : public Impl
{
public:
/// Virtual destructor
virtual ~DataTransferInterface() = default;
/// Clone method
virtual std::unique_ptr<DataTransferInterface> clone() const = 0;
/// Collect data that is needed before grid adaption
virtual void preAdapt(Container const& container, bool mightCoarsen) = 0;
/// Interpolate data to new grid after grid adaption
virtual void adapt(Container& container) = 0;
/// Perform cleanup after grid adaption
virtual void postAdapt(Container& container) = 0;
using Impl::Impl;
void preAdapt(B const& b, C const& c, bool mc) final { this->get().preAdapt(b,c,mc); }
void adapt(B const& b, C& c) final { this->get().adapt(b,c); }
void postAdapt(C& c) final { this->get().postAdapt(c); }
};
/**
* \brief Implementation of \ref DataTransferInterface that does not interpolate, but
* just resizes the containers to the dimension of the basis
**/
template <class Container>
class NoDataTransfer
: public DataTransferInterface<Container>
// The Concept definition of a DataTransfer
struct Concept
{
using Interface = DataTransferInterface<Container>;
public:
std::unique_ptr<Interface> clone() const override
{
return std::make_unique<NoDataTransfer>();
}
void preAdapt(Container const&, bool) override {}
void adapt(Container& container) override
{
container.resize();
}
void postAdapt(Container&) override {}
template <class DT>
auto require(DT&& dt) -> decltype
(
dt.preAdapt(std::declval<B const&>(), std::declval<C const&>(), true),
dt.adapt(std::declval<B const&>(), std::declval<C&>()),
dt.postAdapt(std::declval<C&>())
);
};
using Base = Dune::Functions::TypeErasureBase<Interface,Model>;
};
template <class Node, class Container, class Basis>
class NodeDataTransfer;
} // end namespace Impl
/** Data Transfer implementation for a single grid using interpolation
* Handles computations related to the geometric information of the grid and passes that to the
* underlying NodeDataTransfer classes
*/
template <class Container, class Basis>
class DataTransfer
: public DataTransferInterface<Container>
{
using LocalView = typename Basis::LocalView;
using Tree = typename LocalView::Tree;
using GridView = typename Basis::GridView;
using Grid = typename GridView::Grid;
using Element = typename GridView::template Codim<0>::Entity;
using Geometry = typename Element::Geometry;
using LocalCoordinate = typename Geometry::LocalCoordinate;
using IdType = typename Grid::LocalIdSet::IdType;
template <class Node>
using NodeElementData = typename NodeDataTransfer<Node, Container, Basis>::NodeElementData;
using ElementData = TypeTree::TreeContainer<NodeElementData,Tree,true>;
using Interface = DataTransferInterface<Container>;
public:
DataTransfer(std::shared_ptr<Basis const> basis);
std::unique_ptr<Interface> clone() const override
{
return std::make_unique<DataTransfer<Container, Basis>>(basis_);
}
/** Saves data contained in coeff in the PersistentContainer
* To be called after grid.preAdapt() and before grid.adapt()
*/
void preAdapt(Container const& coeff, bool mightCoarsen) override;
/** Unpacks data from the PersistentContainer
* To be called after grid.adapt() and before grid.postAdapt()
*/
// [[expects: basis is updated in gridView]]
// [[expects: comm is updated in basis]]
void adapt(Container& coeff) override;
/** Performs cleanup
* To be called after grid.postAdapt()
*/
void postAdapt(Container& coeff) override;
private:
/// The global basis
std::shared_ptr<Basis const> basis_;
/// Container with data that persists during grid adaptation
using PersistentContainer = std::map<IdType, ElementData>;
PersistentContainer persistentContainer_;
/// Map leaf entities to unique index
using Mapper = Dune::MultipleCodimMultipleGeomTypeMapper<GridView>;
Mapper mapper_;
/// Data transfer on a single basis node
template <class Node>
using NDT = NodeDataTransfer<Node, Container, Basis>;
using NodeDataTransferContainer = TypeTree::TreeContainer<NDT,Tree,true>;
NodeDataTransferContainer nodeDataTransfer_;
};
/**
* \addtogroup Adaption
* @{
**/
/// Factory to create DataTransfer objects based on the \ref DataTransferOperation
template <class Container>
class DataTransferFactory
/// \brief The base class for data transfer classes.
/**
* Type-erasure base class for data-transfer.
*
* \tparam Basis The global basis, the data to interpolate is defined on.
* \tparam Container A vector type for storign the data to interpolate.
**/
template <class Basis, class Container>
class DataTransfer
: public Impl::DataTransferDefinition<Basis,Container>::Base
{
using Definition = Impl::DataTransferDefinition<Basis,Container>;
using Super = typename Definition::Base;
public:
/// \brief Constructor from a type supporting the DataTransferInterface.
template <class Impl, Dune::disableCopyMove<DataTransfer,Impl> = 0>
DataTransfer(Impl&& impl)
: Super{FWD(impl)}
{
using Interface = DataTransferInterface<Container>;
public:
template <class Basis>
static std::unique_ptr<Interface> create(DataTransferOperation op, std::shared_ptr<Basis> basis)
{
switch (op)
{
case DataTransferOperation::NO_OPERATION:
return std::make_unique<NoDataTransfer<Container>>();
case DataTransferOperation::INTERPOLATE:
return std::make_unique<DataTransfer<Container, Basis>>(std::move(basis));
default:
error_exit("Invalid data transfer\n");
return nullptr; // avoid warnings
}
}
};
static_assert(Concepts::models<typename Definition::Concept(Impl)>,
"Implementation does not model the DataTransfer concept.");
}
/// \brief Default Constructor.
DataTransfer() = default;
/** Wrapper class for (No)DataTransfer objects.
* This wrapper class may be used instead of a pointer type to DataTransferInterface. It wraps
* and redirects to the implementation, while providing deep copy and move functionality.
* A virtual clone() method in the interface and implementation classes is required to perform
* the deep copy of the implementation.
*/
template <class Container>
class DataTransferWrapper
/// \brief Collect data that is needed before grid adaption.
/**
* \param basis The global basis before the grid is updated.
* \param container The original data before grid adaption, can be used to create a
* persistent storage.
* \param mightCoarsen Flag to indicate whether there are elements marked for coarsening.
**/
void preAdapt(Basis const& basis, Container const& container, bool mightCoarsen)
{
using Interface = DataTransferInterface<Container>;
using Factory = DataTransferFactory<Container>;
using Self = DataTransferWrapper<Container>;
this->asInterface().preAdapt(basis, container, mightCoarsen);
}
public:
template <class Basis>
DataTransferWrapper(DataTransferOperation op, std::shared_ptr<Basis> basis)
: impl_(std::move(Factory::create(op, basis)))
{}
DataTransferWrapper(Self const& that)
: impl_(std::move(that.impl_->clone()))
{}
/// \brief Interpolate data to new grid after grid adaption.
/**
* \param basis The global basis after the grid is updated
* \param container The original data vector not yet updated. Should be adapted in this method.
**/
void adapt(Basis const& basis, Container& container)
{
this->asInterface().adapt(basis, container);
}
DataTransferWrapper(Self&& that) = default;
/// \brief Perform cleanup after grid adaption
/**
* \param container The data vector after any adaption and data transfer
**/
void postAdapt(Container& container)
{
this->asInterface().postAdapt(container);
}
};
Self& operator=(Self const& that)
{
impl_ = std::move(that.impl_->clone());
return *this;
}
Self& operator=(Self&& that) = default;
namespace tag {
void preAdapt(Container const& c, bool b) { impl_->preAdapt(c, b); }
/// \brief Base tag type for all data transfer tags.
struct datatransfer {};
void adapt(Container& c) { impl_->adapt(c); }
} // end namespace tag
void postAdapt(Container& c) { impl_->postAdapt(c); }
private:
std::unique_ptr<Interface> impl_;
};
//// \brief Factory class to create DataTransfer based on a tag.
/**
* Specializations of this factory can be used to generically construct a DataTransfer
* based on a given <tag>. The <tag> should be derived from `tag::datatransfer`.
**/
template <class Tag>
struct DataTransferFactory
{
template <class Basis, class Container>
static DataTransfer<Basis,Container> create(Basis const&, Container const&)
{
error_exit("Unknown <tag> passed to the DataTransferFactory.");
return DataTransfer<Basis,Container>{};
}
};
/// @}
/// @}
} // end namespace AMDiS
#include "DataTransfer.inc.hpp"
install(FILES
InterpolationDataTransfer.hpp
InterpolationDataTransfer.inc.hpp
NoDataTransfer.hpp
SimpleDataTransfer.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amdis/datatransfers)
#pragma once
#include <map>
#include <memory>
#include <amdis/DataTransfer.hpp>
#include <amdis/typetree/TreeContainer.hpp>
namespace AMDiS {
/**
* \addtogroup Adaption
* @{
**/
namespace tag {
struct interpolation_datatransfer
: datatransfer
{};
} // end namespace tag
// forward declaration
template <class Node, class Container, class Basis>
class NodeDataTransfer;
/** Data Transfer implementation for a single grid using interpolation
* Handles computations related to the geometric information of the grid and passes that to the
* underlying NodeDataTransfer classes
*/
template <class Basis, class Container>
class InterpolationDataTransfer
{
using LocalView = typename Basis::LocalView;
using Tree = typename LocalView::Tree;
using GridView = typename Basis::GridView;
using Grid = typename GridView::Grid;
using Element = typename GridView::template Codim<0>::Entity;
using Geometry = typename Element::Geometry;
using LocalCoordinate = typename Geometry::LocalCoordinate;
using IdType = typename Grid::LocalIdSet::IdType;
template <class Node>
using NodeElementData = typename NodeDataTransfer<Node, Container, Basis>::NodeElementData;
using ElementData = TypeTree::TreeContainer<Node