Hi everyone,
JLBorges, you definitely helped me a lot, and I'm very thankful. Indeed, till know the code does not seem to have lot of use. So, let me try to explain how I get to pursue this approach:
The "simplest" situation I can think about is solving a 2D problem of a free fluid flow in one scale. In this situation, we have 3 equations: the mass balance equation, and 2 momentum equations one for each axis X and Y. A good practice would be to refer to each equation by an index (or a tag) throughout all the code. Otherwise, if we refer to it by an index here, a name their and a tag in another place God knows where it is, maintainability turns to be nightmare, and further development, eventually trying to add another equation, will be if not "impossible", too expensive. This is one of the big challenges that many mature software, commercial and academic, are facing.
Now, lets suppose we have the following indices and model:
1 2 3 4 5 6 7 8 9 10 11 12
|
namespace One_Scale_Model {
template < size_t offset > struct Indices // Note: grid dimension is a compile-time parameter
{
static const int GridDimension = 2;
static const int NumberOfEquations = offset + 1 + GridDimension;
static const int MassBalanceEqIdx = offset;
static const int MomentumEqFirstIdx = MassBalanceEqIdx;
static const int MomentumEqLastIdx = MassBalanceEqIdx + GridDimension;
};
} // namespace One_Scale_Model
struct Model { using Indices = One_Scale_Model::Indices<0>; };
|
now, some finite volume methods use different finite volume geometries for different equations. For example, the
momentum Finite Volume Geometry class may be completely different from that of a the
mass balance when using a staggered grid. So, one can write a class for the momentum FVG and specialize it for the mass balance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
template < typename T, std::size_t N > struct FVG
{
void update() { std::cout << "FVG<T," << N << ">::update() " ; }
void reset( int v ) { std::cout << "FVG<T," << N << ">::reset(" << v << ") " ; }
// momentum dependent members...
void A() { std::cout << "momentum fvg's member A()\n"; }
};
template < typename T > struct FVG< T, T::Indices::MassBalanceEqIdx >
{
void update() { std::cout << "FVG<T," << N << ">::update() " ; }
void reset( int v ) { std::cout << "FVG<T," << N << ">::reset(" << v << ") " ; }
// mass balance dependent members...
void B() { std::cout << "mass balance fvg's member()\n"; }
};
|
Note that
partial specialization might be needed for multi scale models (like weather modelling cited by
TheIdeasMan, or other multi-scale approaches...), and this can be done by adding an additional index as argument for the FVG template class:
|
template < typename T, std::size_t EqIdx, std::size_t ScaleIdx > struct FVG;
|
In the present case 2D problem - 1 scale model, for each grid cell, one need to instantiate the FVG 3 times, each instance has a different types. Thus, one may think about putting them (the 3 instances) together in a container.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
template < template< typename, std::size_t > class G, typename T, std::size_t N > struct FVG_C
{
typename make_sequence< G, T, N >::type tup ;
template < typename Seq, typename F>
static void for_each( Seq &seq, const F &f ) { boost::fusion::for_each(seq,f); }
void update() { for_each( tup, [] ( auto& x ) { x.update() ; } ) ; }
void reset( int v ) { for_each( tup, [v] ( auto& x ) { x.reset(v) ; } ) ; }
template < int idx >
const G<T,idx> &fvg() const
{ return std::get<idx>(tup); }
};
|
and the tuple is defined by,
1 2 3 4 5 6 7 8 9
|
template < template< typename, std::size_t > class G, typename T, std::size_t N >
struct make_sequence
{
using type = typename std::remove_reference<
decltype( std::tuple_cat( std::declval< typename make_sequence<G,T,N-1>::type >(),
std::declval< std::tuple< G<T,N> > >() ) ) >::type ;
};
template < template< typename, std::size_t > class G, typename T >
struct make_sequence<G,T,0> { using type = std::tuple< G<T,0> > ; };
|
doing so, we make a slim interface to FVC instances and types where ever in the code just using the equation's indices.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
int main()
{
using Indices = typename Model::Indices;
// run-time variables
int GridCellsNumber = 4,
markedCellIdx = 2;
// I have NumberOfEquations (i.e. 3) FVC'instances for each grid cell
std::vector< FVG_C< FVG, Model, Indices::NumberOfEquations > > fvg_vec(GridCellsNumber) ;
// access (read-only) the mass balance FVC instance of the grid cell index i.
fvg_vec[ markedCellIdx ].fvg< Indices::MassBalanceEqIdx >();
// access (read-only) the momentum FVC instance of the grid cell index i along the axis X.
fvg_vec[ markedCellIdx ].fvg< Indices::MomentumEqFirstIdx >();
// update (read/write) FVC instances for all grid cells if the grid has been changed.
if( /*grid_changed() == true*/ true) { for( auto& fvg : fvg_vec ) { fvg.update() ; std::cout << "\n"; } }
}
|
here is the full code:
http://coliru.stacked-crooked.com/a/0126a95ebb22f173
I hope you find this useful for your aplications.
Thank you everyone for helping me out. See you!
Adim