Function copy mechanism
Problem
Especially in solvers (but probably also elsewhere) it is necessary to create copies of existing functions.
Consider for example the temporary variables in solvers. Those should have the same properties as the function that represents the solution variable (e.g. boundary conditions).
Somehow such properties need to be copied/duplicated - but the function type might not be known when the copy is requested.
A simple copy-constructor is probably not what we want either, since the duplicate may have another name, different allocated min and max-level etc. So certain properties should not be copied.
There are two approaches: ctor or a method that offers a duplicate instance.
An orthogonal issue is the following: consider current solver implementation:
// pseudo solver
SomeSolver(
const std::shared_ptr< PrimitiveStorage >& storage,
uint_t minLevel,
uint_t maxLevel,
uint_t maxIter = std::numeric_limits< uint_t >::max(),
real_t tolerance = 1e-16,
)
// When the temporary functions are constructed, there is no access yet to an actual function.
// So the properties have to be copied later on - OR we make member functions pointers that are initialized in a second step.
: tmp_( "tmp", storage, minLevel, maxLevel )
// ...
void solve( const OperatorType& A, const FunctionType& x, const FunctionType& b, const uint_t level ) override
{
// Only at this point we even _know_ the required properties of the tmp variable.
tmp_.copyBoundaryConditionFromFunction( x );
// ...
Proposal
a) ctor - cannot be virtual so we need to force implementation in derived functions via virtual function anyway(?)
// Function
Function( const FunctionType & other, std::string name, uint_t minLevel, uint_t maxLevel )
{
ctor_impl( other, name, minLevel, maxLevel );
}
virtual void Function::ctor_impl( ... )
{
WALBERLA_ABORT( "Not implemented." )
}
// Derived
void ctor_impl( ... )
{
// here we go
}
// usage
// pseudo solver
SomeSolver(
const std::shared_ptr< PrimitiveStorage >& storage,
uint_t minLevel,
uint_t maxLevel,
uint_t maxIter = std::numeric_limits< uint_t >::max(),
real_t tolerance = 1e-16,
)
// ...
std::shared_ptr< FunctionType > tmp_;
void solve( const OperatorType& A, const FunctionType& x, const FunctionType& b, const uint_t level ) override
{
// not even sure if this works
tmp_ = std::make_shared< Function >( x, "tmp", 2, 2 );
// ...
b) virtual copy/duplicate methods
// Function
virtual std::shared_ptr< FunctionType > duplicate( std::string name, uint_t minLevel, uint_t maxLevel, bool copyValues )
{
WALBERLA_ABORT( "Not implemented." )
}
// Derived
virtual std::shared_ptr< FunctionType > duplicate( std::string name, uint_t minLevel, uint_t maxLevel, bool copyValues )
{
// copy stuff
}
// usage
// pseudo solver
SomeSolver(
const std::shared_ptr< PrimitiveStorage >& storage,
uint_t minLevel,
uint_t maxLevel,
uint_t maxIter = std::numeric_limits< uint_t >::max(),
real_t tolerance = 1e-16,
)
// ...
std::shared_ptr< FunctionType > tmp_;
void solve( const OperatorType& A, const FunctionType& x, const FunctionType& b, const uint_t level ) override
{
// not even sure if this works
tmp_ = x.duplicate( "tmp", 2, 2, false );
// ...
Writing this I feel that the duplicate method approach is simpler and straightforward.
Input appreciated!