dgmasspass.hh 18.92 KiB
#ifndef DUNE_FEM_DG_MASS_INVERSE_PASS_HH
#define DUNE_FEM_DG_MASS_INVERSE_PASS_HH
#include <cassert>
#include <iosfwd>
#include <sstream>
#include <utility>
#include <dune/common/typetraits.hh>
#include <dune/fem/storage/singletonlist.hh>
#include <dune/fem/common/tupleutility.hh>
#include <dune/fem/pass/common/local.hh>
#include <dune/fem/quadrature/cachingquadrature.hh>
#include <dune/fem/pass/dginversemass.hh>
#include <dune/fem-dg/misc/crs.hh>
namespace Dune
{
namespace Fem
{
// DGMassInverseMassPass
// ---------------------
/** \brief Pass applying the local inverse mass matrix on each element
*
* \ingroup Pass
*
* \tparam functionalId pass id of functional to convert
* \tparam PreviousPass type of previous pass
* \tparam id pass id
*/
template< class ScalarDiscreteFunctionSpace, bool inverse >
class DGMassInverseMassImplementation
{
public:
static_assert((ScalarDiscreteFunctionSpace::dimRange == 1), "dimRange of ScalarSpace > 1");
//! \brief discrete function space type
typedef ScalarDiscreteFunctionSpace ScalarDiscreteFunctionSpaceType ;
typedef typename ScalarDiscreteFunctionSpaceType::RangeType ScalarRangeType;
typedef typename ScalarDiscreteFunctionSpaceType::GridPartType GridPartType;
static const int numScalarBasis = ScalarDiscreteFunctionSpaceType :: localBlockSize ;
class Key
{
protected:
const GridPartType & gridPart_;
const int numScalarBasis_;
const bool inverse_ ;
public:
//! constructor taking space
Key(const GridPartType& gridPart)
: gridPart_(gridPart),
numScalarBasis_( numScalarBasis ),
inverse_( inverse ) {}
//! copy constructor
//! returns true if indexSet pointer and numDofs are equal
bool operator == (const Key& otherKey) const
{
// mapper of space is singleton
return (&(gridPart_.indexSet()) == & (otherKey.gridPart_.indexSet()) )
&& ( numScalarBasis_ == otherKey.numScalarBasis_ ) && ( inverse_ == otherKey.inverse_ );
}
//! return reference to grid part for construction
const GridPartType& gridPart() const { return gridPart_; }
};
typedef Key KeyType;
private:
typedef typename ScalarDiscreteFunctionSpaceType :: RangeFieldType ctype;
typedef Dune::FieldMatrix< ctype, numScalarBasis, numScalarBasis > LocalMatrixType ;
typedef Dune::Fem::BlockCRSMatrix< ctype, numScalarBasis, numScalarBasis > SparseLocalMatrixType ;
typedef Fem::CachingQuadrature< GridPartType, 0 > VolumeQuadratureType;
typedef typename GridPartType::template Codim< 0 >::EntityType EntityType;
typedef typename ScalarDiscreteFunctionSpaceType :: BasisFunctionSetType BasisFunctionSetType ;
typedef typename EntityType :: Geometry Geometry ;
typedef typename Geometry :: GlobalCoordinate GlobalCoordinate ;
typedef LocalMassMatrix< ScalarDiscreteFunctionSpaceType, VolumeQuadratureType > LocalMassMatrixType ;
typedef LocalMassMatrixImplementationDgOrthoNormal<
ScalarDiscreteFunctionSpaceType, VolumeQuadratureType > DgOrthoNormalMassMatrixType ;
static const bool isOrthoNormal = false ; //Conversion< LocalMassMatrixType, DgOrthoNormalMassMatrixType > :: exists ;
struct VectorEntry
{
SparseLocalMatrixType matrix_ ;
GlobalCoordinate center_ ;
VectorEntry () : matrix_(), center_( std::numeric_limits< double > :: max() )
{}
};
typedef std::vector< VectorEntry* > MatrixStorageType;
public:
template <class Key>
explicit DGMassInverseMassImplementation ( const Key& key )
: scalarSpace_( const_cast< GridPartType& > (key.gridPart()) ),
volumeQuadratureOrder_( 2 * scalarSpace_.order() ),
matrices_( Fem :: ThreadManager :: maxThreads() ),
localMassMatrix_( scalarSpace_ ),
sequence_( -1 )
{
assert( Fem::ThreadManager::singleThreadMode() );
setup();
}
~DGMassInverseMassImplementation ()
{
const int maxThreads = Fem :: ThreadManager :: maxThreads() ;
for( int thread = 0; thread < maxThreads ; ++ thread )
{
for( size_t i=0; i<matrices_[ thread ].size(); ++i )
delete matrices_[ thread ][ i ];
}
}
//! interface method
template < class DestinationType >
void prepare( const DestinationType& argument, DestinationType &destination, const bool doSetup ) const
{
if( doSetup )
setup();
}
//! interface method
template < class DestinationType >
void finalize( const DestinationType &argument, DestinationType &destination ) const
{
}
//! apply inverse mass matrix locally
template < class DestinationType >
void applyLocal( const EntityType& entity,
const DestinationType& argument,
DestinationType& destination ) const
{
typedef typename DestinationType :: LocalFunctionType LocalFunctionType;
const Geometry& geometry = entity.geometry();
LocalFunctionType localDest = destination.localFunction( entity );
const LocalFunctionType localArg = argument.localFunction( entity );
if( isOrthoNormal && geometry.affine() )
{
// get inverse mass factor
const double massFactorInv = localMassMatrix_.getAffineMassFactor( geometry );
const double factor = ( ! inverse ) ? massFactorInv : 1.0/massFactorInv ;
// apply factor * localArg and store in dest
localDest.axpy( factor, localArg );
}
else
{
const int idx = scalarSpace_.gridPart().indexSet().index( entity );
const int thread = setup( idx, entity, geometry );
enum { dimRange = DestinationType :: DiscreteFunctionSpaceType :: dimRange };
assert( matrices_[ thread ][ idx ] );
multiplyBlock( dimRange, matrices_[ thread ][ idx ]->matrix_, localArg, localDest );
}
}
//! apply inverse mass matrix to local function
template< class Caller, class LocalFunction >
void applyInverse( Caller& caller, const EntityType& entity, LocalFunction& localDest ) const
{
// this pass only can handle no mass
assert( ! caller.hasMass() );
applyInverse( entity, localDest );
}
//! apply inverse mass matrix to local function
template< class LocalFunction >
void applyInverse( LocalFunction& localDest ) const
{
applyInverse( localDest.entity(), localDest );
}
//! apply inverse mass matrix to local function
template< class LocalFunction >
void applyInverse( const EntityType& entity, LocalFunction& localDest ) const
{
enum { dimRange = LocalFunction :: dimRange };
const int idx = scalarSpace_.gridPart().indexSet().index( entity );
const Geometry& geometry = entity.geometry();
const int thread = setup( idx, entity, geometry );
static const int numBasis = dimRange * numScalarBasis ;
assert( localDest.numDofs() == numBasis );
// vector holding basis function evaluation
std::array< ctype, numBasis > lfX;
for( int i=0; i<numBasis; ++i )
lfX[ i ] = localDest[ i ];
assert( matrices_[ thread ][ idx ] );
multiplyBlock( dimRange, matrices_[ thread ][ idx ]->matrix_, lfX, localDest );
}
template < class DestinationType >
void compute ( const DestinationType& argument, DestinationType &destination ) const
{
// make sure this method is not called in multi thread mode
assert( Fem :: ThreadManager :: singleThreadMode() );
// set pointer
prepare( argument, destination, true );
for( const auto& en : elements( scalarSpace_.gridPart(), Dune::Partitions::all ) )
{
applyLocal( en, argument, destination );
}
// remove pointer
finalize( argument, destination );
}
protected:
template < class ConstLocalFunction, class LocalFunctionType >
void multiplyBlock( const int dimRange,
const SparseLocalMatrixType& matrix,
const ConstLocalFunction& arg,
LocalFunctionType& dest ) const
{
// clear destination
// dest.clear();
matrix.mvb( dimRange, arg, dest );
/*
static const int rows = LocalMatrixType::rows;
static const int cols = LocalMatrixType::cols;
assert( int(rows * dimRange) == dest.numDofs() );
// apply matrix to arg and store in dest
for(int i=0; i<rows; ++i )
{
for(int j=0; j<cols; ++j )
{
for( int r=0, ir = i * dimRange, jr = j * dimRange ;
r<dimRange; ++ r, ++ ir, ++ jr )
{
dest[ ir ] += matrix[ i ][ j ] * arg[ jr ] ;
}
}
}
*/
}
void setup() const
{
assert( Fem :: ThreadManager :: singleThreadMode() );
const int gpSequence = scalarSpace_.gridPart().sequence();
if( sequence_ != gpSequence )
{
const GridPartType &gridPart = scalarSpace_.gridPart();
const int gpSize = gridPart.indexSet().size( 0 ) ;
const int maxThreads = Fem :: ThreadManager :: maxThreads() ;
for( int thread = 0; thread < maxThreads ; ++ thread )
{
matrices_[ thread ].resize( gpSize, (VectorEntry *) 0 );
}
sequence_ = gpSequence ;
}
}
int setup( const int idx,
const EntityType& entity,
const Geometry& geometry ) const
{
const int thread = Fem :: ThreadManager :: thread();
bool computeMatrix = false ;
if( matrices_[ thread ][ idx ] == 0 )
{
VectorEntry* entry = new VectorEntry();
matrices_[ thread ][ idx ] = entry;
entry->center_ = geometry.center();
computeMatrix = true ;
}
else
{
VectorEntry* entry = matrices_[ thread ][ idx ] ;
const GlobalCoordinate center = geometry.center();
GlobalCoordinate diff ( center );
diff -= entry->center_;
// for cells on the boundary we have to compute this all the time
computeMatrix = diff.infinity_norm() > 1e-12 ;
if( computeMatrix ) entry->center_ = center ;
}
if( computeMatrix )
{
VectorEntry* entry = matrices_[ thread ][ idx ];
setup( idx, entity,
geometry,
scalarSpace_.basisFunctionSet( entity ),
entry->matrix_ );
}
return thread ;
}
void setup( const int idx,
const EntityType& entity,
const Geometry& geo,
const BasisFunctionSetType& set,
SparseLocalMatrixType& storedMatrix ) const
{
// clear matrix
LocalMatrixType matrix( 0 );
// vector holding basis function evaluation
std::array< ScalarRangeType, numScalarBasis > phi ;
// make sure that the number of basis functions is correct
assert( numScalarBasis == int(set.size()) );
// create appropriate quadrature
VolumeQuadratureType volQuad( entity, volumeQuadratureOrder_ );
const int volNop = volQuad.nop();
for(int qp=0; qp<volNop; ++qp)
{
// calculate integration weight
const double intel = ( volQuad.weight(qp) * geo.integrationElement(volQuad.point(qp)) );
// eval basis functions
set.evaluateAll(volQuad[ qp ], phi);
for(int m=0; m<numScalarBasis; ++m)
{
const ScalarRangeType& phi_m = phi[ m ];
const ctype val = intel * (phi_m * phi_m);
matrix[ m ][ m ] += val;
for(int k=m+1; k<numScalarBasis; ++k)
{
const ctype val = intel * (phi_m * phi[ k ]);
matrix[ m ][ k ] += val;
matrix[ k ][ m ] += val;
}
}
}
for(int m=0; m<numScalarBasis; ++m)
{
for(int k=0; k<numScalarBasis; ++k)
{
if( std::abs( matrix[ m ][ k ] ) < 1e-14 )
matrix[ m ][ k ] = 0;
}
}
// if we are looking for the inverse, then invert matrix
if( inverse )
matrix.invert();
// store matrix
storedMatrix.set( matrix );
}
protected:
ScalarDiscreteFunctionSpaceType scalarSpace_;
const int volumeQuadratureOrder_;
mutable std::vector< MatrixStorageType > matrices_;
LocalMassMatrixType localMassMatrix_;
mutable int sequence_ ;
};
/** \brief Pass applying the local inverse mass matrix on each element
*
* \ingroup Pass
*
* \tparam functionalId pass id of functional to convert
* \tparam PreviousPass type of previous pass
* \tparam id pass id
*/
template< int functionalId, class PreviousPass, int id, bool inverse >
class DGMassInverseMassPass
: public Dune::Fem::LocalPass< DGInverseMassPassDiscreteModel< functionalId, PreviousPass >, PreviousPass, id >
{
typedef DGMassInverseMassPass< functionalId, PreviousPass, id, inverse > ThisType;
typedef Dune::Fem::LocalPass< DGInverseMassPassDiscreteModel< functionalId, PreviousPass >, PreviousPass, id > BaseType;
public:
//! type of the discrete model used
typedef DGInverseMassPassDiscreteModel< functionalId, PreviousPass > DiscreteModelType;
//! pass ids up to here (tuple of integral constants)
typedef typename BaseType::PassIds PassIds;
//! \brief argument type
typedef typename BaseType::TotalArgumentType TotalArgumentType;
//! \brief destination type
typedef typename BaseType::DestinationType DestinationType;
//! \brief discrete function space type
typedef typename DiscreteModelType::Traits::DiscreteFunctionSpaceType DiscreteFunctionSpaceType;
// type of communication manager object which does communication
typedef typename DiscreteFunctionSpaceType::template ToNewDimRange< 1 >::Type ScalarDiscreteFunctionSpaceType;
typedef DGMassInverseMassImplementation< ScalarDiscreteFunctionSpaceType, inverse > MassInverseMassType ;
typedef typename MassInverseMassType :: KeyType KeyType;
typedef Fem::SingletonList< KeyType, MassInverseMassType > MassInverseMassProviderType;
private:
static const std::size_t functionalPosition = DiscreteModelType::functionalPosition;
typedef typename DiscreteFunctionSpaceType::GridPartType GridPartType;
typedef typename GridPartType::template Codim< 0 >::EntityType EntityType;
typedef typename DestinationType :: LocalFunctionType LocalFunctionType;
public:
using BaseType::passNumber;
using BaseType::space;
explicit DGMassInverseMassPass ( PreviousPass &previousPass,
const DiscreteFunctionSpaceType &spc )
: BaseType( previousPass, spc, "DGMassInverseMassPass" ),
massInverseMass_( MassInverseMassProviderType :: getObject( KeyType( spc.gridPart() ) ) ),
arg_( 0 ),
dest_( 0 )
{
}
//! constructor for use with thread pass
DGMassInverseMassPass ( const DiscreteModelType& discreteModel,
PreviousPass &previousPass,
const DiscreteFunctionSpaceType &spc,
const int volQuadOrd = -1,
const int faceQuadOrd = -1)
: BaseType( previousPass, spc, "DGMassInverseMassPass" ),
massInverseMass_( MassInverseMassProviderType :: getObject( KeyType( spc.gridPart() ) ) ),
arg_( 0 ),
dest_( 0 )
{
}
~DGMassInverseMassPass() { MassInverseMassProviderType :: removeObject( massInverseMass_ ); }
void printTexInfo ( std::ostream &out ) const
{
BaseType::printTexInfo( out );
out << "DGMassInverseMassPassname() :" << "\\\\" << std::endl;
}
//! this pass needs no communication
bool requireCommunication () const { return false ; }
//! interface method
void prepare( const TotalArgumentType &argument, DestinationType &destination ) const
{
prepare( argument, destination, true );
}
//! prepare for ThreadPass
void prepare( const TotalArgumentType &argument, DestinationType &destination, const bool doSetup ) const
{
arg_ = &argument ;
dest_ = &destination;
massInverseMass_.prepare( *(std::get< functionalPosition >( argument )), destination, doSetup );
}
//! finalize for ThreadPass
void finalize( const TotalArgumentType &argument, DestinationType &destination, const bool ) const
{
finalize( argument, destination );
}
//! interface method
void finalize( const TotalArgumentType &argument, DestinationType &destination ) const
{
massInverseMass_.finalize( *(std::get< functionalPosition >( argument )), destination );
arg_ = 0;
dest_ = 0;
}
//! apply inverse mass matrix locally
void applyLocal( const EntityType& entity ) const
{
assert( arg_ );
assert( dest_ );
massInverseMass_.applyLocal( entity, *(std::get< functionalPosition >( *arg_ )), *dest_ );
}
//! apply local with neighbor checker (not needed here)
template <class NBChecker>
void applyLocal( const EntityType& entity, const NBChecker& ) const
{
applyLocal( entity );
}
/** \brief apply local for all elements that do not need information from
* other processes (here all elements)
*/
template <class NBChecker>
void applyLocalInterior( const EntityType& entity, const NBChecker& ) const
{
applyLocal( entity );
}
/** \brief apply local for all elements that need information from
* other processes (here no elements)
*/
template <class NBChecker>
void applyLocalProcessBoundary( const EntityType& entity, const NBChecker& ) const
{
DUNE_THROW(InvalidStateException,"DGInverseMassPass does not need a second phase for ThreadPass");
}
protected:
void compute ( const TotalArgumentType &argument, DestinationType &destination ) const
{
massInverseMass_.compute( *(std::get< functionalPosition >( argument )), destination );
}
protected:
MassInverseMassType& massInverseMass_ ;
mutable const TotalArgumentType* arg_ ;
mutable DestinationType* dest_;
};
} // namespace Fem
} // namespace Dune
#endif // #ifndef DUNE_FEM_PASS_APPLYLOCALOPERATOR_HH