Skip to content
Snippets Groups Projects
dgprimalfluxes.hh 46.65 KiB
#ifndef DUNE_FEM_DG_DGPRIMALFLUXES_HH
#define DUNE_FEM_DG_DGPRIMALFLUXES_HH

#include <dune/common/version.hh>

#include <dune/fem/misc/fmatrixconverter.hh>
#include <dune/fem/operator/1order/localmassmatrix.hh>
#include <dune/fem/pass/localdg/discretemodel.hh>
#include <dune/fem-dg/pass/context.hh>
#include <dune/fem/quadrature/cachingquadrature.hh>
#include <dune/fem/solver/timeprovider.hh>
#include <dune/fem/space/common/arrays.hh>

#include <dune/fem-dg/pass/dgmasspass.hh>
#include "fluxbase.hh"

namespace Dune
{
namespace Fem
{

  // DGPrimalDiffusionFluxImpl
  //----------------------

  /**
   * \brief diffusion flux
   *
   * \ingroup DiffusionFluxes
   */
  template <class DiscreteFunctionSpaceImp,
            class Model,
            class FluxParameterImp >
  class DGPrimalDiffusionFluxImpl
   : public DGDiffusionFluxBase< DiscreteFunctionSpaceImp, Model, FluxParameterImp >
  {
    typedef DGDiffusionFluxBase< DiscreteFunctionSpaceImp, Model, FluxParameterImp >      BaseType;

  public:
    typedef DiscreteFunctionSpaceImp DiscreteFunctionSpaceType;

    enum { dimDomain = DiscreteFunctionSpaceType :: dimDomain };
    enum { dimRange  = DiscreteFunctionSpaceType :: dimRange };
    enum { dimGradRange = dimDomain * dimRange };
    enum { polOrd = DiscreteFunctionSpaceType :: polynomialOrder };

    typedef typename DiscreteFunctionSpaceType :: RangeFieldType        RangeFieldType;
    typedef typename DiscreteFunctionSpaceType :: DomainFieldType       DomainFieldType;
    typedef FieldVector< DomainFieldType, dimDomain-1 >                 FaceDomainType;
    typedef typename DiscreteFunctionSpaceType :: DomainType            DomainType;
    typedef typename DiscreteFunctionSpaceType :: RangeType             RangeType;
    typedef typename DiscreteFunctionSpaceType :: JacobianRangeType     JacobianRangeType;


    typedef typename DiscreteFunctionSpaceType :: GridPartType          GridPartType;
    typedef typename GridPartType :: IntersectionIteratorType           IntersectionIterator;
    typedef typename IntersectionIterator :: Intersection               Intersection;
    typedef typename GridPartType :: GridType                           GridType;
    typedef typename DiscreteFunctionSpaceType :: EntityType            EntityType;
    typedef typename GridPartType::template Codim< 0 >::IteratorType    IteratorType;
    typedef typename GridPartType::IntersectionIteratorType
                                                                        IntersectionIteratorType;

    typedef typename BaseType :: DiscreteGradientSpaceType              DiscreteGradientSpaceType;
    typedef typename DiscreteGradientSpaceType :: RangeType             GradientType;
    typedef Fem::TemporaryLocalFunction< DiscreteGradientSpaceType >    LiftingFunctionType;

    typedef Fem::CachingQuadrature< GridPartType, 0>                    VolumeQuadratureType ;

    typedef Fem::LocalMassMatrix
      < DiscreteGradientSpaceType, VolumeQuadratureType >               LocalMassMatrixType;

    class Lifting
    {
    protected:
#ifdef USE_CACHED_INVERSE_MASSMATRIX
#warning "Using cached inverse local mass matrix"
      // type of communication manager object which does communication
      typedef typename DiscreteGradientSpaceType::template ToNewDimRange< 1 >::Type ScalarDiscreteFunctionSpaceType;
      typedef Fem::DGMassInverseMassImplementation< ScalarDiscreteFunctionSpaceType, true > MassInverseMassType ;
      typedef typename MassInverseMassType :: KeyType KeyType;
      typedef Fem::SingletonList< KeyType, MassInverseMassType >  InverseMassProviderType;
      typedef MassInverseMassType&  LocalMassMatrixStorageType ;
#else
      typedef LocalMassMatrixType  LocalMassMatrixStorageType;
#endif

      const DiscreteGradientSpaceType& gradSpc_;
      LiftingFunctionType r_e_;
      LocalMassMatrixStorageType localMassMatrix_;
      unsigned char isInitialized_;

      // prohibit copying
      Lifting( const Lifting& );
    public:
      explicit Lifting( const DiscreteGradientSpaceType& grdSpace )
        : gradSpc_( grdSpace )
        , r_e_( gradSpc_ )
#ifdef USE_CACHED_INVERSE_MASSMATRIX
        , localMassMatrix_( InverseMassProviderType :: getObject( KeyType( gradSpc_.gridPart() ) ) )
#else
        , localMassMatrix_( gradSpc_, 2*gradSpc_.order() )
#endif
        , isInitialized_( 0 )
      {
        assert( Fem::ThreadManager::singleThreadMode() );
      }

      bool isInitialized() const { return isInitialized_ == 2 ; }

      void initialize( const EntityType& entity )
      {
        assert( isInitialized_ != 1 );
        r_e_.init( entity );
        r_e_.clear();
        isInitialized_ = 1;
      }

      LiftingFunctionType& function()
      {
        return r_e_;
      }

      void finalize()
      {
        assert( isInitialized_ == 1 );
        localMassMatrix_.applyInverse( r_e_ );
        isInitialized_ = 2;
      }
    };

  protected:
    using BaseType :: determineDirection;
    using BaseType :: model_;
    using BaseType :: cflDiffinv_;
    using BaseType :: dimensionFactor_;
    using BaseType :: nonconformingFactor_;
    using BaseType :: numericalFlux ;
    using BaseType :: upwind_ ;

  public:
    typedef typename BaseType::ParameterType  ParameterType;
  private:
    typedef typename BaseType::IdEnum         EnumType;
    typedef typename BaseType::LiftingEnum    LiftingEnum;
  public:

    using BaseType :: parameter ;

    bool initAreaSwitch() const
    {
      if( method_ == EnumType::cdg2 )
      {
        // when default value is used, then use areSwitch
        if( ( upwind_ - BaseType :: upwindDefault() ).two_norm2() < 1e-10 )
          return true ;
      }
      return upwind_.two_norm2() < 1e-10 ;
    }

  public:
    enum { evaluateJacobian = true };

    /**
     * \brief constructor reading parameters
     */
    DGPrimalDiffusionFluxImpl( GridPartType& gridPart,
                               const Model& model,
                               const ParameterType& parameters,
                               const EnumType staticMethod ) :
      BaseType( model, true, parameters ),
      gridPart_( gridPart ),
      method_( staticMethod == EnumType::general ? parameters.getMethod() : staticMethod ),
      penalty_( parameter().penalty() ),
      nipgFactor_( (method_ == EnumType::nipg) ||
                   (method_ == EnumType::bo)
                   ? 0.5 : -0.5 ),
      liftFactor_( parameter().liftfactor() ),
      liftingMethod_( parameter().getLifting() ),
      penaltyTerm_( method_ == EnumType::ip || ((std::abs(  penalty_ ) > 0) &&
                    method_ != EnumType::br2 &&
                    method_ != EnumType::bo )),
      gradSpc_( gridPart_ ),
      LeMinusLifting_( hasLifting() ? new Lifting( gradSpc_ ) : 0 ),
      LePlusLifting_( ( method_ == EnumType::br2 ) ? new Lifting( gradSpc_ ) : 0 ),
#ifdef LOCALDEBUG
      LeMinusLifting2_( ( method_ <= EnumType::cdg ) ? new Lifting( gradSpc_ ) : 0 ),
#endif
      insideIsInflow_ ( true ),
      areaSwitch_( initAreaSwitch() ),
      useTheoryParams_( false ),
      initialized_ ( false )
    {
      // calculate maxNeighborVolumeRatio_
      maxNeighborsVolumeRatio_ = 1.;

      double theoryFactor = parameter().theoryparameters();
      useTheoryParams_ = (theoryFactor > 0.);

      double n_k = DiscreteFunctionSpaceType :: polynomialOrder ;
      ainsworthFactor_ = theoryFactor * 0.5 * n_k * ( n_k + 1.0 );

      int maxNumFaces = 0 ;
      int maxNumOutflowFaces = 0;
      if ( useTheoryParams_ )
      {
        const IteratorType itend = gridPart.template end<0>();
        for( IteratorType it = gridPart.template begin<0>(); it != itend; ++it )
        {
          const EntityType& entity = * it ;
          const double insideVol = entity.geometry().volume();
          int numFaces = 0;
          int numOutflowFaces = 0;
          const IntersectionIteratorType intitend = gridPart.iend( entity );
          for(IntersectionIteratorType intit = gridPart.ibegin( entity );
              intit != intitend; ++intit )
          {
            const Intersection& intersection = * intit ;

            ++numFaces ;
            if ( intersection.neighbor() )
            {
#if DUNE_VERSION_NEWER(DUNE_GRID,2,4)
              double outsideVol = intersection.outside().geometry().volume();
#else
              double outsideVol = intersection.outside()->geometry().volume();
#endif
              numOutflowFaces += (determineDirection(areaSwitch_, insideVol,outsideVol,intersection) ? 1 : 0);
              if ( !areaSwitch_ || insideVol/outsideVol < 1)
                maxNeighborsVolumeRatio_ = std::max( maxNeighborsVolumeRatio_, insideVol/outsideVol );
            }
            else
              ++numOutflowFaces;
          }
          maxNumFaces = std::max( maxNumFaces, numFaces );
          maxNumOutflowFaces = std::max( maxNumOutflowFaces, numOutflowFaces );
        }

        initialized_ = true;
        liftFactor_ = 0.0;
        penalty_ = 0.;
        if (method_ == EnumType::cdg2)
        {
          liftFactor_ = theoryFactor * 0.25* ((double) maxNumFaces); // max number of faces here
          //if( ! areaSwitch_ )
          liftFactor_ *= (1.+maxNeighborsVolumeRatio_);
        }
        else if (method_ == EnumType::cdg)
        {
          liftFactor_ = theoryFactor * maxNumOutflowFaces;
        }
        else if (method_ == EnumType::br2)
        {
          liftFactor_ = theoryFactor * maxNumFaces;
        }
        else if( method_ == EnumType::nipg )
        {
          std::cerr << "ERROR: No theory parameters for NIPG" << std::endl;
          DUNE_THROW(InvalidStateException,"No theory parameters for NIPG");
        }
      }

      if( Fem::Parameter :: verbose() )
      {
        std::cout <<"Diff. flux: ";
        diffusionFluxName( std::cout );

        std::cout <<", penalty: ";
        if ( useTheoryParams_ && (method_ == EnumType::ip) )
        {
          std::cout <<"theory (";
          diffusionFluxPenalty( std::cout );
          std::cout << ")";
        }
        else
          diffusionFluxPenalty( std::cout );

        std::cout <<", max neigh. vol. ratio: " <<maxNeighborsVolumeRatio_;
        std::cout <<", liftfactor: " <<liftFactor_;
        std::cout <<", max inflow faces: " <<maxNumOutflowFaces;
        std::cout <<std::endl;
      }
    }

    //! copy constructor (needed for thread parallel programs)
    DGPrimalDiffusionFluxImpl( const DGPrimalDiffusionFluxImpl& other ) :
      BaseType( other ),
      gridPart_( other.gridPart_ ),
      method_( other.method_ ),
      penalty_( other.penalty_ ),
      nipgFactor_( other.nipgFactor_ ),
      liftFactor_( other.liftFactor_ ),
      liftingMethod_( other.liftingMethod_ ),
      penaltyTerm_( other.penaltyTerm_ ),
      gradSpc_( gridPart_ ),
      LeMinusLifting_( hasLifting() ? new Lifting( gradSpc_ ) : 0 ),
      LePlusLifting_( ( method_ == EnumType::br2 ) ? new Lifting( gradSpc_ ) : 0 ),
#ifdef LOCALDEBUG
      LeMinusLifting2_( ( method_ <= EnumType::cdg ) ? new Lifting( gradSpc_ ) : 0 ),
#endif
      maxNeighborsVolumeRatio_( other.maxNeighborsVolumeRatio_ ),
      ainsworthFactor_( other.ainsworthFactor_ ),
      insideIsInflow_ ( other.insideIsInflow_ ),
      areaSwitch_( other.areaSwitch_ ), // used area based switch
      useTheoryParams_( other.useTheoryParams_ ),
      initialized_( other.initialized_ )
    {
    }

    // return reference to gradient discrete function space
    const DiscreteGradientSpaceType& gradientSpace() const { return gradSpc_; }

    double maxNeighborsVolumeRatio() const
    {
      assert( initialized_ );
      return maxNeighborsVolumeRatio_;
    }

    void diffusionFluxName( std::ostream& out ) const
    {
      out << ParameterType::methodNames( method_ );
      if( areaSwitch_ )
        out <<"(area)";
      else
        out <<"(upwind)";
    }

    void diffusionFluxPenalty( std::ostream& out ) const
    {
      out << penalty_;
    }

    void diffusionFluxLiftFactor( std::ostream& out ) const
    {
      out <<liftFactor_;
    }

    //! returns true if lifting has to be calculated
    bool hasLifting () const { return ( method_ <= EnumType::br2 ); }

  protected:
    Lifting& LePlusLifting() const
    {
      assert( LePlusLifting_ );
      return *LePlusLifting_;
    }

    Lifting& LeMinusLifting() const
    {
      assert( LeMinusLifting_ );
      return *LeMinusLifting_;
    }
#ifdef LOCALDEBUG
    Lifting& LeMinusLifting2() const
    {
      assert( LeMinusLifting2_ );
      return *LeMinusLifting2_;
    }
#endif

  public:
    void initialize( const DiscreteFunctionSpaceType &space )
    {
    }

    template <class LocalEvaluation, class ArgumentTupleVector >
    void initializeIntersection(const LocalEvaluation& left,
                                const LocalEvaluation& right,
                                const ArgumentTupleVector& uLeftVec,
                                const ArgumentTupleVector& uRightVec)
    {
      if( hasLifting() )
      {
        computeLiftings( left.intersection(), left.entity(), right.entity(), left.time(),
                         left.quadrature(), right.quadrature(),
                         uLeftVec, uRightVec,
                         (method_ == EnumType::br2 ) );
      }
    }

    template <class QuadratureImp, class ArgumentTupleVector >
    void initializeIntersection(const Intersection& intersection,
                                const EntityType& inside,
                                const EntityType& outside,
                                const double time,
                                const QuadratureImp& quadInner,
                                const QuadratureImp& quadOuter,
                                const ArgumentTupleVector& uLeftVec,
                                const ArgumentTupleVector& uRightVec)
    {
      if( hasLifting() )
      {
        computeLiftings( intersection, inside, outside, time,
                         quadInner, quadOuter,
                         uLeftVec, uRightVec,
                         (method_ == EnumType::br2 ) );
      }
    }

    template <class QuadratureImp, class ArgumentTupleVector >
    void computeLiftings(const Intersection& intersection,
                         const EntityType& inside,
                         const EntityType& outside,
                         const double time,
                         const QuadratureImp& quadInner,
                         const QuadratureImp& quadOuter,
                         const ArgumentTupleVector& uLeftVec,
                         const ArgumentTupleVector& uRightVec,
                         const bool computeBoth )
    {
      if( hasLifting() || computeBoth )
      {
        if ( ! LeMinusLifting_ )
          LeMinusLifting_.reset( new Lifting( gradSpc_ ) );

        // define for an intersection e
        //  Ke+ := { e in bnd(Ke+), s * n_Ke+ < 0 }
        //  Ke- := { e in bnd(Ke-), s * n_Ke- > 0 }
        // Notice
        //  l_e = r_e on Ke-
        //  l_e = -r_e on Ke+
        // so that
        //  L_e = 2*r_e on Ke-
        //  L_e = 0 elsewhere

        // get Ke- in entity
        insideIsInflow_ = determineDirection( areaSwitch_, inside.geometry().volume(),
                                              outside.geometry().volume(),
                                              intersection );

        const EntityType& entity = ( insideIsInflow_ ) ? outside : inside;
        const ArgumentTupleVector& u = ( insideIsInflow_ ) ? uRightVec : uLeftVec;

        const size_t quadNoInp = quadInner.nop();
        liftingEvalLeMinus_.resize( quadNoInp );

        // get the right quadrature for the lifting entity
        const QuadratureImp& faceQuad = ( insideIsInflow_ ) ? quadOuter : quadInner;
  /*
#ifdef LOCALDEBUG
        const size_t quadNoOutp = quadOuter.nop();
        double sum = 0;
        double sum2 = 0;

        // get Ke+ in entity2
        // calculate L_e=2*r_e on Ke+
        const EntityType& entity2 = ( insideIsInflow_ ) ? inside : outside;
        LeMinusLifting().initialize( entity2 );

        {
          // get the right quadrature for the lifting entity
          const QuadratureImp& faceQuad2 = ( insideIsInflow_ ) ? quadInner : quadOuter;

          for(size_t qp = 0; qp < quadNoInp; ++qp )
          {
            // get value of 2*r_e in quadrature point
            addLifting(intersection, time, faceQuad2, qp,
                       uLeftVec[ qp ], uRightVec[ qp ], liftingEvalLeMinus_[ qp ] );
          }
          // add to local function
          LeMinusLifting().function().axpyQuadrature( faceQuad, liftingEvalLeMinus_ );

          LeMinusLifting().finalize();
        }

        // calculate 4*\int_Ke+(r_e*r_e)
        double term1;
        term1 = integrateLifting( LeMinusLifting().function(),
                                  LeMinusLifting().function().entity().geometry() );
        const double interiorFactor =
          ( insideIsInflow_ ? -1. : 1. );

        // set sum += -4*\int_Ke+(r_e*r_e)
        sum += interiorFactor*term1;

        // calculate L_e=2*r_e on Ke-
        LeMinusLifting2().initialize( entity );

        for(size_t qp = 0; qp < quadNoOutp; ++qp )
        {
          // get value of 2*r_e in quadrature point
          addLifting(intersection, time, faceQuad, qp,
                     uLeftVec[ qp ], uRightVec[ qp ], liftingEvalLeMinus_ );
        }
        LeMinusLifting2().function().axpyQuadrature( faceQuad, liftingEvalLeMinus_ );
        LeMinusLifting2().finalize();

        // calculate 4*\int_Ke-(r_e*r_e)
        double term2;
        term2 = integrateLifting( LeMinusLifting2().function(),
                                  LeMinusLifting2().function().entity().geometry() );

        sum2 = sum;

        // put 4*(\int_Ke-(r_e*r_e) - \int_Ke+(r_e*r_e))
        //  = 4*\int_e(r_e*l_e) in sum
        // put 4*(\int_Ke-(r_e*r_e) + \int_Ke+(r_e*r_e))
        //  = 4*\int_e(r_e*r_e) in sum2
        sum -= interiorFactor*term2;
        sum2 += std::abs( term2 );

        if (sim2 > 1e-20)
        {
          localMaxRatio_ = std::max( localMaxRatio_, std::abs(sum/sum2) );
          localMinRatio_ = std::min( localMinRatio_, std::abs(sum/sum2) );
        } else
          localMaxRatio_ = std::numeric_limits<double>();

        if ( (std::abs(sum) > sum2) )
        {
          std::cout <<"sum: " <<sum <<" sum2: " <<sum2 <<std::endl;
          std::cout <<"term1: " <<term1 <<std::endl;
          std::cout <<"term2: " <<term2 <<std::endl <<std::endl;
          abort();
        }

        // add to final sum
        //    \sum_e (\int_Ke+ r_e*r_e - \int_Ke- r_e*r_e)
        //    \sum_e (\int_Ke+ r_e*r_e + \int_Ke- r_e*r_e)
        sum_ += sum;
        sum2_ += sum2;
#endif
        */

        // back to the real computation, entity=Ke-
        LeMinusLifting().initialize( entity );

        // calculate real lifting
        for(size_t qp = 0; qp < quadNoInp; ++qp )
        {
          addLifting(intersection, entity, u.tuple( qp ), u[ qp ], time, faceQuad,  qp,
                     uLeftVec[ qp ], uRightVec[ qp ],
                     liftingEvalLeMinus_[ qp ] );
        }
        // add to local function
        LeMinusLifting().function().axpyQuadrature( faceQuad, liftingEvalLeMinus_ );

        // LeMinusLifting_ has L_e=2*r_e on Ke-
        LeMinusLifting().finalize( );

        // already evaluate for all quadrature points
        LeMinusLifting().function().evaluateQuadrature( faceQuad, liftingEvalLeMinus_ );

        if ( computeBoth )
        {
          if ( ! LePlusLifting_ )
            LePlusLifting_.reset( new Lifting( gradSpc_ ) );

          // get Ke+ in entity2
          // calculate 2*r_e on Ke+
          const EntityType& entity2 = ( insideIsInflow_ ) ? inside : outside;
          const ArgumentTupleVector& u2 = ( insideIsInflow_ ) ? uLeftVec : uRightVec;
          LePlusLifting().initialize( entity2 );

          // get the right quadrature for the lifting entity
          const QuadratureImp& faceQuad2 = ( insideIsInflow_ ) ? quadInner : quadOuter;

          const size_t quadNoOutp = quadOuter.nop();
          liftingEvalLePlus_.resize( quadNoOutp );
          for(size_t qp = 0; qp < quadNoOutp; ++qp )
          {
            // get value of 2*r_e in quadrature point
            // use correct order on interface quadratures!
            addLifting(intersection, entity2, u2.tuple( qp ), u2[ qp ], time, faceQuad2,  qp,
                       uLeftVec[ qp ], uRightVec[ qp ], liftingEvalLePlus_[ qp ] );
          }

          // add to local function
          LePlusLifting().function().axpyQuadrature( faceQuad2, liftingEvalLePlus_ );

          // LePlusLifting_ carries 2*r_e on Ke+
          LePlusLifting().finalize( );

          // already evaluate for all quadrature points, reuse liftTmp here
          LeMinusLifting().function().evaluateQuadrature( faceQuad, liftingEvalLePlus_ );
        }
      }
    }

#ifdef LOCALDEBUG
    template <class LiftingFunction , class Geometry >
    double integrateLifting( const LiftingFunction& lifting, const Geometry& geometry ) const
    {
      typedef typename LiftingFunction :: RangeType RangeType;
      VolumeQuadratureType quad( lifting.entity(), 2 * lifting.order() + 2 );
      const int quadNop = quad.nop();
      RangeType val;
      double sum = 0.0;
      for( int qp = 0; qp < quadNop; ++qp )
      {
        const double weight = quad.weight( qp ) *
          geometry.integrationElement( quad.point( qp ) );
        lifting.evaluate( quad[ qp ], val );
        sum += weight * (val * val);
      }
      return sum;
    }
#endif

    template <class LocalEvaluation, class ArgumentTupleVector>
    void initializeBoundary(const LocalEvaluation& local,
                            const ArgumentTupleVector& uLeftVec,
                            const std::vector< RangeType >& uRight)
    {
      initializeBoundary( local.intersection(), local.entity(), local.time(), local.quadrature(), uLeftVec, uRight );
    }


    template <class QuadratureImp, class ArgumentTupleVector>
    void initializeBoundary(const Intersection& intersection,
                            const EntityType& entity,
                            const double time,
                            const QuadratureImp& quadInner,
                            const ArgumentTupleVector& uLeftVec,
                            const std::vector< RangeType >& uRight)
    {
      if( hasLifting() )
      {
        insideIsInflow_ = true;

        LeMinusLifting().initialize( entity );

        const size_t quadNop = quadInner.nop();
        liftingEvalLeMinus_.resize( quadNop );
        for(size_t qp = 0; qp < quadNop; ++qp )
        {
          addLifting(intersection, entity, uLeftVec.tuple( qp ), uLeftVec[ qp ], time, quadInner, qp,
                     uLeftVec[ qp ], uRight[ qp ] , liftingEvalLeMinus_[ qp ] );
        }
        // add to local function
        LeMinusLifting().function().axpyQuadrature( quadInner, liftingEvalLeMinus_ );
        // finalize function
        LeMinusLifting().finalize();
        // already evaluate all liftings
        LeMinusLifting().function().evaluateQuadrature( quadInner, liftingEvalLeMinus_ );
      }
    }

  protected:
    template <class QuadratureImp, class ArgumentTuple, class LiftingFunction >
    void addLifting(const Intersection& intersection,
                    const EntityType &entity,
                    const ArgumentTuple &uTuple,
                    const RangeType& u,
                    const double time,
                    const QuadratureImp& faceQuad,
                    const int quadPoint,
                    const RangeType& uLeft,
                    const RangeType& uRight,
                    LiftingFunction& func ) const
    {
      IntersectionQuadraturePointContext< Intersection, EntityType, QuadratureImp, ArgumentTuple, ArgumentTuple >
        local( intersection, entity, faceQuad, uTuple, uTuple, quadPoint, time, entity.geometry().volume() );

      const FaceDomainType& x = faceQuad.localPoint( quadPoint );
      DomainType normal = intersection.integrationOuterNormal( x );

      JacobianRangeType jumpUNormal;
      for(int r = 0; r < dimRange; ++r)
      {
        for(int j=0; j<dimDomain; ++j)
          jumpUNormal[ r ][ j ] = normal[ j ] * (uLeft[ r ] - uRight[ r ]);
      }

      Fem::FieldMatrixConverter< GradientType, JacobianRangeType> func1( func );
      if (liftingMethod_ == LiftingEnum::id_id)
        func1 = jumpUNormal;
      else
      {
        JacobianRangeType AJumpUNormal;
        model_.diffusion( local, u, jumpUNormal, AJumpUNormal );
        func1 = AJumpUNormal;
      }
      func *= -faceQuad.weight( quadPoint );
    }

    /** \return A(u)L_e*n */
    template <class LocalEvaluation>
    void applyLifting(const LocalEvaluation& local,
                      const DomainType& normal,
                      const RangeType& u,
                      const GradientType& sigma,
                      RangeType& lift) const
    {
      // convert sigma into JacobianRangeType
      Fem::FieldMatrixConverter< GradientType, JacobianRangeType> gradient( sigma );

      if (liftingMethod_ != LiftingEnum::id_A)
      {
        JacobianRangeType mat;
        // set mat = G(u)L_e
        model_.diffusion( local, u, gradient, mat );
        // set lift = G(u)L_e*n
        mat.mv( normal, lift );
      }
      else
      {
        // just apply gradient
        gradient.mv( normal, lift );
      }
    }

    /** \brief calculate \f$\sum_{e\in\partial K} \Lambda_e |e|^2\f$
     *
     *  \note \f$\Lambda_e = 1\f$ for Dirichlet face @a e, \f$\Lambda_e = 0.5\f$
     *        for interface, and \f$\Lambda_e = 0\f$ for Neumann face
     */
    double sumFaceVolumeSqr( const EntityType& entity ) const
    {
      double sumFaceVolSqr  = 0.0;

      const IntersectionIteratorType intitend = gridPart_.iend( entity );
      for(IntersectionIteratorType intit = gridPart_.ibegin( entity );
          intit != intitend; ++intit )
      {
        const Intersection& intersection = * intit ;
        const double faceVol = intersection.geometry().volume();

        // !!!!! forget about Neumann for now
        // 1/2 for interior intersections
        if ( intersection.neighbor() )
          sumFaceVolSqr += 0.5 * faceVol * faceVol;
        else
          sumFaceVolSqr += faceVol * faceVol;
      }

      return sumFaceVolSqr;
    }

  public:
    /**
     * \brief flux function on interfaces between cells
     *
     * \param intersection intersection
     * \param time current time given by TimeProvider
     * \param x coordinate of required evaluation local to \c intersection
     * \param uLeft DOF evaluation on this side of \c intersection
     * \param uRight DOF evaluation on the other side of \c intersection
     * \param gLeft result for this side of \c intersection
     * \param gRight result for the other side of \c intersection
     *
     * \note The total numerical flux for multiplication with phi
     *       is given with
     *        CDG2:
     *          gLeft = numflux(f(u)) - {G(u)grad(u)}*n
     *            + C_cdg2/h {G(u)}[u]*n
     *            + liftFactor*(G(u)L_e)|Ke-
     *        CDG:
     *          gLeft = numflux(f(u)) - {G(u)grad(u)}*n
     *            - beta*n[G(u)grad(u)] + C_cdg/h {G(u)}[u]*n
     *            + liftFactor*(G(u)L_e)|Ke-
     *        BR2:
     *          gLeft = numflux(f(u)) - {G(u)grad(u)}*n + C_br2 {G(u)r_e([u])}*n
     *        IP:
     *          gLeft = numflux(f(u)) - {G(u)grad(u)}*n + C_ip/h {G(u)}[u]*n
     *
     *       The total numerical flux for multiplication with grad(phi) is
     *        NIPG, BO:
     *          gDiffLeft = 0.5*G(u-)[u]
     *        IP, BR2, CDG2:
     *          gDiffLeft = -0.5*G(u-)[u]
     *        CDG:
     *          gDiffLeft = -0.5*G(u-)[u] - beta*G(u)[u]
     *       where h = min(|entity+|,|entity-|) / |intersection|.
     *       In this method we need to return
     *          in gLeft:      same like above, but without numflux(f(u))
     *          in gDiffLeft:  same like above
     *
     * \return wave speed estimate (multiplied with the integration element of the intersection).
     *         To estimate the time step |T|/wave is used
     */
    template <class LocalEvaluation>
    double numericalFlux(const LocalEvaluation& left,
                         const LocalEvaluation& right,
                         const RangeType& uLeft,
                         const RangeType& uRight,
                         const JacobianRangeType& jacLeft,
                         const JacobianRangeType& jacRight,
                         RangeType& gLeft,
                         RangeType& gRight,
                         JacobianRangeType& gDiffLeft,
                         JacobianRangeType& gDiffRight )
    {
      gLeft  = 0;
      gRight = 0;

      const FaceDomainType& x = left.localPosition();
      const DomainType normal = left.intersection().integrationOuterNormal( x );

      const double faceLengthSqr = normal.two_norm2();

      JacobianRangeType diffmatrix ;
      RangeType diffflux ;

      // for all methods except CDG we need to evaluate {G(u)grad(u)}
      if (method_ != EnumType::cdg)
      {
        // G(u-)grad(u-) for multiplication with phi
        // call on inside
        model_.diffusion( left, uLeft, jacLeft, diffmatrix );

        // diffflux=G(u-)grad(u-)*n
        diffmatrix.mv( normal, diffflux );

        // G(u+)grad(u+) for multiplication with phi
        // call on outside
        model_.diffusion( right, uRight, jacRight, diffmatrix );

        // diffflux = 2{G(u)grad(u)}*n
        diffmatrix.umv( normal, diffflux );

        // gLeft = gRight = -{G(u)grad(u)}*n
        gLeft.axpy ( -0.5, diffflux );
        gRight.axpy( -0.5, diffflux );
      }
      else
      // for CDG we need G(u)grad(u) on Ke-
      {
        ////////////////////////////////
        // [ phi ] * [ G(u)grad(u) ] ...
        ///////////////////////////////
        if ( ! insideIsInflow_ )
          model_.diffusion( left, uLeft, jacLeft, diffmatrix );
        else
          model_.diffusion( right, uRight, jacRight, diffmatrix );

        // diffflux=(G(u)grad(u)*n)|Ke-
        diffmatrix.mv( normal, diffflux );

        // gLeft = gRight = -(G(u)grad(u)*n)|Ke-
        gLeft.axpy ( -1., diffflux );
        gRight.axpy( -1., diffflux );
      }

      // jumpUNormal = [u]*n
      RangeType jumpUNormal( uLeft );
      jumpUNormal -= uRight ;

      // set jumpU = [u]
      JacobianRangeType jumpU;
      for( int r = 0; r < dimRange; ++r )
      {
        jumpU[ r ]  = normal;
        jumpU[ r ] *= jumpUNormal[ r ];
      }

      // get G(u-)[u] in gDiffLeft
      // get G(u+)[u] in gDiffRight
      // this is not the final value for gDiffLeft, gDiffRight
      // G(u-)[u], G(u+)[u] are needed in the penalty term
      model_.diffusion( left,  uLeft,  jumpU, gDiffLeft );
      model_.diffusion( right, uRight, jumpU, gDiffRight );

      ////////////////////////////////////////////////
      //  start penalty term
      ///////////////////////////////////////////////
      // BR2 has its own special and BO doesn't have penalty term
      // every other method has this penalty term
      if ( penaltyTerm_ )
      {
        RangeType penaltyTerm ;

        if( (method_ == EnumType::ip) && useTheoryParams_ )
        {
          // penaltyTerm
          // = ainsworthFactor * maxEigenValue(A(u)) * FaceEntityVolumeRatio * [u] * n
          RangeType maxLeft, maxRight;
          model_.eigenValues( left,  uLeft, maxLeft );
          model_.eigenValues( right, uRight, maxRight );
          double maxEigenValue = 0.;
          for( int r = 0; r<dimRange; ++r )
          {
            maxEigenValue = std::max( maxEigenValue, maxLeft[r] );
            maxEigenValue = std::max( maxEigenValue, maxRight[r] );
          }

          // calculate penalty factor
          const double penaltyFactorInside = sumFaceVolumeSqr( left.entity() ) / left.volume();
          const double penaltyFactorOutside = sumFaceVolumeSqr( right.entity() ) / right.volume();
          penalty_ = std::max( penaltyFactorInside, penaltyFactorOutside );
          penalty_ *= ainsworthFactor_ * maxEigenValue ;

          jumpU.mv( normal, penaltyTerm );
        }
        else
        {
          // \int C_IP {G(u)}[u][phi] dx
          //    = \int C_IP (G(u_L)[u]*n + G(u_R)[u]*n)/2 phi
          // so penaltyTerm = C_IP (G(u_L)[u]*n + G(u_R)[u]*n)/2

          // apply with normal
          gDiffLeft.mv( normal, penaltyTerm );
          gDiffRight.umv( normal, penaltyTerm );

          penaltyTerm *= 0.5 ;
        }

        double minvol =
          std::min( left.volume(), right.volume() );
        penaltyTerm /= minvol;

        // add to fluxes
        gLeft.axpy( penalty_, penaltyTerm );
        gRight.axpy( penalty_, penaltyTerm );
      }
      ////////////////////////////////////////////////
      //  end penalty term
      ///////////////////////////////////////////////


      // gDiffLeft = -0.5 G(u-)[u] (IP,BR2)
      // gDiffLeft = 0.5 G(u-)[u] (NIPG,BO)
      gDiffLeft *= nipgFactor_;

      // current entity gets (i.e. for IP)
      //    (numflux(f(u))*n+{G(u)grad(u)}*n-C11[u]*n)phi
      //    + 0.5G(u-)[u]*grad(phi)
      // and neighbor gets
      //    (numflux(f(u))*(-n)+{G(u)grad(u)}*(-n)-C11[u]*(-n))phi
      //    + 0.5G(u-)[u]*grad(phi)
      // so term 0.5G(u-)[u]*grad(phi) stays the same therefore:
      gDiffRight *= (-nipgFactor_);

      ////////////////////////////////////////////////
      //  begin lifting terms
      ///////////////////////////////////////////////
      if( hasLifting() )
      {
        // ... and the values of u from Ke-
        const RangeType& u = ( insideIsInflow_ ) ? uRight : uLeft;
        const LocalEvaluation& local = ( insideIsInflow_ ) ? right : left ;

        RangeType lift;
        // LiftingFunctionType& LeMinus = LeMinusLifting().function();
        // get value of G(u-)L_e-*n into liftTotal
        applyLifting( local, normal, u, liftingEvalLeMinus_[ local.index() ], lift );

        // only for CDG-type methods
        if (method_ != EnumType::br2)
        {
          lift   *= liftFactor_ ;
          gLeft  -= lift;
          gRight -= lift;
        }

        if( method_ == EnumType::cdg )
        {
          const RangeFieldType C_12 = ( insideIsInflow_ ) ? 0.5 : -0.5;
          JacobianRangeType resU;

          ////////////////////////////////
          // [ u ] * [ G(u)grad(phi) ] ...
          ///////////////////////////////

          resU = gDiffLeft;
          resU *= C_12/nipgFactor_;

          // save gDiffLeft
          gDiffLeft += resU;

          resU  = gDiffRight;
          resU *= C_12/(-nipgFactor_);

          // save gDiffLeft
          gDiffRight += resU;
        }

        if (method_ == EnumType::br2)
        {
          // BR2 hasn't had penalty term until now
          // so we add it at this place.
          // \int C_BR2 {G(u)r_e([u])}[phi] dx
          //    = \int C_BR2 (G(u_L)r_e([u])*n + G(u_R)r_e([u])*n)/2 phi
          // so penaltyTerm = C_BR2 (G(u_L)r_e([u])*n + G(u_R)r_e([u])*n)/2

          // get correct quadrature, the one from Ke+
          //const QuadratureImp& faceQuadPlus =
          //  ( ! insideIsInflow_ ) ? faceQuadOuter : faceQuadInner;
          // ... and the values of u from Ke+
          const RangeType& uPlus = ( ! insideIsInflow_ ) ? uRight : uLeft;
          const LocalEvaluation& local = ( ! insideIsInflow_ ) ? right : left ;

          RangeType liftTotal;
          // LiftingFunctionType& LePlus = LePlusLifting().function();

          // get value of G(u+)L_e+*n into liftTotal
          applyLifting( local, normal, uPlus, liftingEvalLePlus_[ local.index() ], liftTotal );

          // set liftTotal = {G(u)r_e}*n = 0.25*(G(u+)L_e+*n + G(u-)L_e-*n)
          liftTotal += lift;
          // add penalty coefficient
          liftTotal *= 0.25 * liftFactor_;

          gLeft -= liftTotal;
          gRight -= liftTotal;
        }
      }
      ////////////////////////////////////////////////
      //  end lifting terms
      ///////////////////////////////////////////////

      //////////////////////////////////////////////////////////
      //
      //  --Time step calculation
      //
      //////////////////////////////////////////////////////////
      const double faceVolumeEstimate = dimensionFactor_ *
        (left.intersection().conforming() ? faceLengthSqr
          : (nonconformingFactor_ * faceLengthSqr));

      const double diffTimeLeft =
        model_.diffusionTimeStep( left, faceVolumeEstimate, uLeft );

      const double diffTimeRight =
        model_.diffusionTimeStep( right, faceVolumeEstimate, uRight );

      // take minimum to proceed
      const double diffTimeStep = std::max( diffTimeLeft, diffTimeRight );

      // timestep restict to diffusion timestep
      // WARNING: reconsider this
      return diffTimeStep * cflDiffinv_;
    }


    /**
     * \brief same as numericalFlux() but for fluxes over boundary interfaces
     */
    template <class LocalEvaluation>
    double boundaryFlux(const LocalEvaluation& left,
                        const RangeType& uLeft,
                        const RangeType& uRight,
                        const JacobianRangeType& jacLeft,
                        RangeType& gLeft,
                        JacobianRangeType& gDiffLeft )
    {
      // get local point
      const FaceDomainType& x = left.localPosition();
      const DomainType normal = left.intersection().integrationOuterNormal( x );

      const double faceLengthSqr = normal.two_norm2();

      JacobianRangeType diffmatrix;

      // diffmatrix = G(u-)grad(u-)
      model_.diffusion( left, uLeft, jacLeft, diffmatrix);

      // gLeft = -G(u-)grad(u-)*n
      diffmatrix.mv( normal, gLeft );
      gLeft *= -1.0;
      if( hasLifting() )
      {
        // LiftingFunctionType& LeMinus = LeMinusLifting().function();

        RangeType lift;
        // get value of G(u)L_e*n into lift
        applyLifting( left, normal, uRight, liftingEvalLeMinus_[ left.index() ], lift );

        if( method_ == EnumType::br2 )
        {
          // set liftTotal = G(u)r_e*n = 0.5*G(u_in)L_e_in*n
          lift *= (0.5*liftFactor_);
        }
        else
        {
          // only for CDG-type methods
          lift *= liftFactor_ ;
        }

        gLeft -= lift;
      }

      /****************************/
      /* Diffusion                 *
       ****************************/
      const double bndNipgFactor = 2.0 * nipgFactor_ ;

      // G(u)[u] = G(u)(u-g_D)*n (SIPG)
      // -G(u)[u] = -G(u)(u-g_D)*n (NIPG)
      // for multiplication with grad(phi)
      JacobianRangeType bndJumpU ;
      for( int r = 0; r < dimRange; ++r )
      {
        bndJumpU[r]  = normal;
        bndJumpU[r] *= (uLeft[r] - uRight[r]);
      }


      // get G(u-)[u] in gDiffLeft
      // this is not hte final value for gDiffLeft
      // but it's used in the penalty term
      model_.diffusion( left, uLeft, bndJumpU, gDiffLeft );

      // add penalty term
      if ( penaltyTerm_ )
      {
        // penalty term for IP
        RangeType penaltyTerm;
        const double enVolInv = 1./left.volume();

        if( (method_ == EnumType::ip) && useTheoryParams_ )
        {
          // penaltyTerm
          // = ainsworthFactor * maxEigenValue(A(u)) * FaceEntityVolumeRatio * [u] * n
          RangeType maxInside;
          model_.eigenValues( left, uLeft, maxInside );
          double maxEigenValue = 0.;
          for( int r = 0; r<dimRange; ++r )
            maxEigenValue = std::max( maxEigenValue, maxInside[r] );

          // calculate penalty factor
          penalty_  = sumFaceVolumeSqr( left.entity() ) * enVolInv;
          penalty_ *= ainsworthFactor_ * maxEigenValue ;

          bndJumpU.mv( normal, penaltyTerm );
        }
        else
        {
          /*
          double penFac = model_.penaltyBoundary( inside, time,
                                                  xglInside, uLeft );
          bndJumpU.mv( normal, penaltyTerm );
          penaltyTerm *= penFac ;
          */
          // \int C_IP {G(u)}[u][phi] dx
          //    = \int C_IP G(u_L)[u]*n phi
          // so penaltyTerm = C_IP G(u_L)[u]*n

          // apply with normal
          gDiffLeft.mv( normal, penaltyTerm );
        }

        // scale with 1/|e|, because normal is not unit normal
        penaltyTerm *= enVolInv;

        gLeft.axpy( penalty_, penaltyTerm );
      }

      // gDiffLeft = G(u-)[u]  (SIPG)
      // gDiffLeft = -G(u-)[u] (NIPG)
      gDiffLeft *= bndNipgFactor;

      ////////////////////////////////////////////////////
      //
      //  --Time step boundary
      //
      ////////////////////////////////////////////////////
      const double diffTimeStep =
        model_.diffusionTimeStep( left, faceLengthSqr, uLeft );

      return diffTimeStep * cflDiffinv_;
    }

  protected:
    GridPartType&                 gridPart_;
    const EnumType                method_;
    double                        penalty_;
    const double                  nipgFactor_;
    double                        liftFactor_;
    LiftingEnum                   liftingMethod_;
    const bool                    penaltyTerm_;
    DiscreteGradientSpaceType  gradSpc_;
    std::unique_ptr< Lifting > LeMinusLifting_;
    std::unique_ptr< Lifting > LePlusLifting_;
#ifdef LOCALDEBUG
    std::unique_ptr< Lifting > LeMinusLifting2_;
#endif
    mutable Fem::MutableArray< GradientType > liftingEvalLeMinus_ ;
    mutable Fem::MutableArray< GradientType > liftingEvalLePlus_ ;

    double            maxNeighborsVolumeRatio_; // for CDG2 only
    double            ainsworthFactor_;
    bool              insideIsInflow_;
    const bool        areaSwitch_;
    bool              useTheoryParams_;
    bool              initialized_;
  }; // end DGPrimalDiffusionFluxImpl



  //////////////////////////////////////////////////////////
  //
  //  extended flux for matrix assembly
  //
  //////////////////////////////////////////////////////////
  template <class DiscreteFunctionSpaceImp,
            class Model,
            class FluxParametersImp = DGPrimalDiffusionFluxParameters >
  class ExtendedDGPrimalDiffusionFlux
   : public DGPrimalDiffusionFluxImpl< DiscreteFunctionSpaceImp, Model, FluxParametersImp >
  {
    typedef DGPrimalDiffusionFluxImpl< DiscreteFunctionSpaceImp, Model, FluxParametersImp >      BaseType;

  public:
    typedef typename BaseType::GridPartType        GridPartType;
    typedef typename BaseType::Intersection        Intersection;
    typedef typename BaseType::EntityType          EntityType;
    typedef typename BaseType::RangeType           RangeType;
    typedef typename BaseType::JacobianRangeType   JacobianRangeType;
    typedef typename BaseType::GradientType        GradientType;
    typedef typename BaseType::DomainType          DomainType;

    typedef typename BaseType :: ParameterType  ParameterType;

    ExtendedDGPrimalDiffusionFlux( GridPartType& gridPart,
                                   const Model& model,
                                   const ParameterType& parameters = ParameterType() )
      : BaseType( gridPart, model, parameters )
    { }

    //! copy constructor (needed for thread parallel programs)
    ExtendedDGPrimalDiffusionFlux( const ExtendedDGPrimalDiffusionFlux& other ) :
      BaseType( other )
    {
    }

    using BaseType::initializeIntersection;
    template <class LocalEvaluation, class ArgumentTupleVector>
    void initializeIntersection(const LocalEvaluation& left,
                                const LocalEvaluation& right,
                                const ArgumentTupleVector& uLeftVec,
                                const ArgumentTupleVector& uRightVec,
                                bool computeBoth)
    {
      this->computeLiftings(left.intersection(), left.entity(), right.entity(), left.time(),
                            left.quadrature(), right.quadrature(),
                            uLeftVec,uRightVec,
                            computeBoth );
    }

    template <class QuadratureImp, class ArgumentTupleVector >
    void initializeIntersection(const Intersection& intersection,
                                const EntityType& inside,
                                const EntityType& outside,
                                const double time,
                                const QuadratureImp& quadInner,
                                const QuadratureImp& quadOuter,
                                const ArgumentTupleVector& uLeftVec,
                                const ArgumentTupleVector& uRightVec,
                                const bool computeBoth )
    {
      this->computeLiftings(intersection, inside, outside, time,
                            quadInner, quadOuter,
                            uLeftVec,uRightVec,
                            computeBoth );
    }

    // return AL_e.n on element and neighbor
    template <class LocalEvaluation>
    void evaluateLifting(const LocalEvaluation& left,
                         const LocalEvaluation& right,
                         const RangeType& uEn,
                         const RangeType& uNb,
                         JacobianRangeType& liftEn,
                         JacobianRangeType& liftNb) const
    {
      assert( this->LePlusLifting().isInitialized() );
      assert( this->LeMinusLifting().isInitialized() );
      const int quadPoint = left.index();
      if ( this->insideIsInflow_)
      {
        applyLifting( this->LePlusLifting().function().entity(), time,
                      left.quadrature(), quadPoint, uEn, liftingEvalLePlus_[quadPoint], liftEn );
        applyLifting( this->LeMinusLifting().function().entity(), time,
                      right.quadrature(), quadPoint, uNb, liftingEvalLeMinus_[quadPoint], liftNb );
      }
      else
      {
        applyLifting( this->LeMinusLifting().function().entity(), time,
                      right.quadrature(), quadPoint, uEn, liftingEvalLeMinus_[quadPoint], liftEn );
        applyLifting( this->LePlusLifting().function().entity(), time,
                      left.quadrature(), quadPoint, uNb, liftingEvalLePlus_[quadPoint], liftNb );
      }
      assert( liftEn == liftEn );
      assert( liftNb == liftNb );
    }

    // return AL_e.n on element and neighbor
    const typename BaseType::LiftingFunctionType &getInsideLifting() const
    {
      assert( this->LePlusLifting().isInitialized() );
      assert( this->LeMinusLifting().isInitialized() );
      if ( this->insideIsInflow_ )
      {
        return this->LePlusLifting().function();
      }
      else
      {
        return this->LeMinusLifting().function();
      }
    }

  protected:
    using BaseType :: liftingEvalLeMinus_;
    using BaseType :: liftingEvalLePlus_;

    /*
    template <class QuadratureImp>
    void applyLifting(const EntityType& entity,
                      const double time,
                      const QuadratureImp& quad,
                      const int qp,
                      const DomainType& normal,
                      const RangeType& u,
                      const GradientType& sigma,
                      JacobianRangeType& mat) const
    {
      ElementQuadraturePointContext< EntityType, QuadratureImp, RangeType,
      Fem::FieldMatrixConverter< GradientType, JacobianRangeType> gradient( sigma );
      // set mat = G(u)L_e
      this->model_.diffusion( entity, time, x, u, gradient, mat );
    }
    */
  }; // end ExtendedDGPrimalDiffusionFlux

} // end namespace
} // end namespace
#endif