Skip to content
Snippets Groups Projects
localgeodesicfefunctiontest.cc 11.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include <config.h>
    
    
    #include <fenv.h>
    
    #include <iostream>
    
    #include <dune/common/fvector.hh>
    #include <dune/grid/common/quadraturerules.hh>
    
    
    #include <dune/localfunctions/lagrange/pqkfactory.hh>
    
    
    #include <dune/gfe/rotation.hh>
    #include <dune/gfe/realtuple.hh>
    #include <dune/gfe/unitvector.hh>
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
    #include <dune/gfe/localgeodesicfefunction.hh>
    
    #include "multiindex.hh"
    
    Oliver Sander's avatar
    Oliver Sander committed
    #include "valuefactory.hh"
    
    const double eps = 1e-6;
    
    
    using namespace Dune;
    
    
    /** \brief Computes the diameter of a set */
    template <class TargetSpace>
    double diameter(const std::vector<TargetSpace>& v)
    {
        double d = 0;
        for (size_t i=0; i<v.size(); i++)
            for (size_t j=0; j<v.size(); j++)
                d = std::max(d, TargetSpace::distance(v[i],v[j]));
        return d;
    }
    
    
    
    template <int domainDim>
    
    void testDerivativeTangentiality(const RealTuple<1>& x,
    
                                     const FieldMatrix<double,1,domainDim>& derivative)
    
    {
        // By construction, derivatives of RealTuples are always tangent
    }
    
    // the columns of the derivative must be tangential to the manifold
    
    template <int domainDim, int vectorDim>
    
    Oliver Sander's avatar
    Oliver Sander committed
    void testDerivativeTangentiality(const UnitVector<vectorDim>& x,
    
                                     const FieldMatrix<double,vectorDim,domainDim>& derivative)
    
        for (int i=0; i<domainDim; i++) {
    
    
            // The i-th column is a tangent vector if its scalar product with the global coordinates
            // of x vanishes.
            double sp = 0;
    
    Oliver Sander's avatar
    Oliver Sander committed
            for (int j=0; j<vectorDim; j++)
    
                sp += x.globalCoordinates()[j] * derivative[j][i];
    
    
            if (std::fabs(sp) > 1e-8)
                DUNE_THROW(Dune::Exception, "Derivative is not tangential: Column: " << i << ",  product: " << sp);
    
    // the columns of the derivative must be tangential to the manifold
    template <int domainDim, int vectorDim>
    void testDerivativeTangentiality(const Rotation<vectorDim-1,double>& x,
                                     const FieldMatrix<double,vectorDim,domainDim>& derivative)
    {
    }
    
    
    Oliver Sander's avatar
    Oliver Sander committed
    // the columns of the derivative must be tangential to the manifold
    template <int domainDim, int vectorDim>
    void testDerivativeTangentiality(const RigidBodyMotion<3,double>& x,
                                     const FieldMatrix<double,vectorDim,domainDim>& derivative)
    {
    }
    
    
    /** \brief Test whether interpolation is invariant under permutation of the simplex vertices
    
     * \todo Implement this for all dimensions
    
    template <int domainDim, class TargetSpace>
    
    void testPermutationInvariance(const std::vector<TargetSpace>& corners)
    
        // works only for 2d domains
    
        if (domainDim!=2)
            return;
    
    
        PQkLocalFiniteElementCache<double,double,domainDim,1> feCache;
        typedef typename PQkLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement;
    
        GeometryType simplex;
        simplex.makeSimplex(domainDim);
    
        //
    
        std::vector<TargetSpace> cornersRotated1(domainDim+1);
        std::vector<TargetSpace> cornersRotated2(domainDim+1);
    
        cornersRotated1[0] = cornersRotated2[2] = corners[1];
        cornersRotated1[1] = cornersRotated2[0] = corners[2];
        cornersRotated1[2] = cornersRotated2[1] = corners[0];
        
    
        LocalGeodesicFEFunction<2,double,LocalFiniteElement,TargetSpace> f0(feCache.get(simplex), corners);
        LocalGeodesicFEFunction<2,double,LocalFiniteElement,TargetSpace> f1(feCache.get(simplex), cornersRotated1);
        LocalGeodesicFEFunction<2,double,LocalFiniteElement,TargetSpace> f2(feCache.get(simplex), cornersRotated2);
    
        // A quadrature rule as a set of test points
    
    Oliver Sander's avatar
    Oliver Sander committed
        int quadOrder = 3;
    
        const Dune::QuadratureRule<double, domainDim>& quad 
    
            = Dune::QuadratureRules<double, domainDim>::rule(simplex, quadOrder);
    
        
        for (size_t pt=0; pt<quad.size(); pt++) {
    
            const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position();
    
            Dune::FieldVector<double,domainDim> l0 = quadPos;
            Dune::FieldVector<double,domainDim> l1, l2;
            
    
    Oliver Sander's avatar
    Oliver Sander committed
            l1[0] = quadPos[1];
            l1[1] = 1-quadPos[0]-quadPos[1];
    
    Oliver Sander's avatar
    Oliver Sander committed
            l2[0] = 1-quadPos[0]-quadPos[1];
            l2[1] = quadPos[0];
    
    Oliver Sander's avatar
    Oliver Sander committed
            // evaluate the three functions
            TargetSpace v0 = f0.evaluate(l0);
            TargetSpace v1 = f1.evaluate(l1);
            TargetSpace v2 = f2.evaluate(l2);
    
    Oliver Sander's avatar
    Oliver Sander committed
            // Check that they are all equal
    
            assert(TargetSpace::distance(v0,v1) < eps);
            assert(TargetSpace::distance(v0,v2) < eps);
    
    Oliver Sander's avatar
    Oliver Sander committed
    
    
    template <int domainDim, class TargetSpace>
    
    void testDerivative(const LocalGeodesicFEFunction<domainDim,double,typename PQkLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType, TargetSpace>& f)
    
        static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension;
        
    
        // A quadrature rule as a set of test points
        int quadOrder = 3;
        
    
        const Dune::QuadratureRule<double, domainDim>& quad 
            = Dune::QuadratureRules<double, domainDim>::rule(GeometryType(GeometryType::simplex,domainDim), quadOrder);
    
        
        for (size_t pt=0; pt<quad.size(); pt++) {
            
    
            const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position();
    
    
            // evaluate actual derivative
    
            Dune::FieldMatrix<double, embeddedDim, domainDim> derivative = f.evaluateDerivative(quadPos);
    
    
            // evaluate fd approximation of derivative
    
            Dune::FieldMatrix<double, embeddedDim, domainDim> fdDerivative = f.evaluateDerivativeFD(quadPos);
    
            Dune::FieldMatrix<double, embeddedDim, domainDim> diff = derivative;
    
            diff -= fdDerivative;
            
            if ( diff.infinity_norm() > 100*eps ) {
    
                std::cout << className<TargetSpace>() << ": Analytical gradient does not match fd approximation." << std::endl;
    
                std::cout << "Analytical: " << derivative << std::endl;
                std::cout << "FD        : " << fdDerivative << std::endl;
            }
    
    
            testDerivativeTangentiality(f.evaluate(quadPos), derivative);
    
        }
    }
    
    
    
    template <int domainDim, class TargetSpace>
    
    void testDerivativeOfValueWRTCoefficients(const LocalGeodesicFEFunction<domainDim,double,typename PQkLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType, TargetSpace>& f)
    
        static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension;
        
    
        // A quadrature rule as a set of test points
    
        
        const Dune::QuadratureRule<double, domainDim>& quad 
    
            = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder);
    
        
        for (size_t pt=0; pt<quad.size(); pt++) {
            
            Dune::FieldVector<double,domainDim> quadPos = quad[pt].position();
            
            // loop over the coefficients
    
            for (size_t i=0; i<f.size(); i++) {
    
    
                // evaluate actual derivative
    
                FieldMatrix<double, embeddedDim, embeddedDim> derivative;
    
                f.evaluateDerivativeOfValueWRTCoefficient(quadPos, i, derivative);
    
                // evaluate fd approximation of derivative
    
                FieldMatrix<double, embeddedDim, embeddedDim> fdDerivative;
    
                f.evaluateFDDerivativeOfValueWRTCoefficient(quadPos, i, fdDerivative);
    
                if ( (derivative - fdDerivative).infinity_norm() > eps ) {
    
                    std::cout << className<TargetSpace>() << ": Analytical derivative of value does not match fd approximation." << std::endl;
    
                    std::cout << "coefficient: " << i << std::endl;
                    std::cout << "quad pos: " << quadPos << std::endl;
                    std::cout << "gfe: ";
    
                    for (size_t j=0; j<f.size(); j++)
                        std::cout << ",   " << f.coefficient(j);
    
                    std::cout << std::endl;
                    std::cout << "Analytical:\n " << derivative << std::endl;
                    std::cout << "FD        :\n " << fdDerivative << std::endl;
                    assert(false);
                }
    
                //testDerivativeTangentiality(f.evaluate(quadPos), derivative);
    
            }
            
        }
    }
    
    
    template <int domainDim, class TargetSpace>
    
    void testDerivativeOfGradientWRTCoefficients(const LocalGeodesicFEFunction<domainDim,double,typename PQkLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType, TargetSpace>& f)
    
        static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension;
        
    
        // A quadrature rule as a set of test points
        int quadOrder = 3;
        
    
        const Dune::QuadratureRule<double, domainDim>& quad 
    
            = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder);
       
    
        for (size_t pt=0; pt<quad.size(); pt++) {
            
    
            const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position();
    
            for (size_t i=0; i<f.size(); i++) {
    
                Tensor3<double, embeddedDim, embeddedDim, domainDim> derivative;
    
                f.evaluateDerivativeOfGradientWRTCoefficient(quadPos, i, derivative);
    
                // evaluate fd approximation of derivative
    
                Tensor3<double, embeddedDim, embeddedDim, domainDim> fdDerivative;
    
                f.evaluateFDDerivativeOfGradientWRTCoefficient(quadPos, i, fdDerivative);
    
    
                if ( (derivative - fdDerivative).infinity_norm() > eps ) {
    
                    std::cout << className<TargetSpace>() << ": Analytical derivative of gradient does not match fd approximation." << std::endl;
    
                    std::cout << "coefficient: " << i << std::endl;
                    std::cout << "quad pos: " << quadPos << std::endl;
    
                    for (size_t j=0; j<f.size(); j++)
                        std::cout << ",   " << f.coefficient(j);
    
                    std::cout << std::endl;
    
                    std::cout << "Analytical:\n " << derivative << std::endl;
                    std::cout << "FD        :\n " << fdDerivative << std::endl;
    
    Oliver Sander's avatar
    Oliver Sander committed
    template <class TargetSpace, int domainDim>
    void test()
    
    Oliver Sander's avatar
    Oliver Sander committed
        std::cout << " --- Testing " << className<TargetSpace>() << ", domain dimension: " << domainDim << " ---" << std::endl;
    
    Oliver Sander's avatar
    Oliver Sander committed
        std::vector<TargetSpace> testPoints;
        ValueFactory<TargetSpace>::get(testPoints);
    
    Oliver Sander's avatar
    Oliver Sander committed
        
        int nTestPoints = testPoints.size();
    
        // Set up elements of the target space
    
        std::vector<TargetSpace> corners(domainDim+1);
    
        MultiIndex index(domainDim+1, nTestPoints);
    
        int numIndices = index.cycle();
    
        for (int i=0; i<numIndices; i++, ++index) {
            
    
    Oliver Sander's avatar
    Oliver Sander committed
            for (int j=0; j<domainDim+1; j++)
                corners[j] = testPoints[index[j]];
    
            
            if (diameter(corners) > 0.5*M_PI)
                continue;
    
            
            // Make local gfe function to be tested
            PQkLocalFiniteElementCache<double,double,domainDim,1> feCache;
            typedef typename PQkLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement;
        
            GeometryType simplex;
            simplex.makeSimplex(domainDim);
    
            LocalGeodesicFEFunction<domainDim,double,LocalFiniteElement,TargetSpace> f(feCache.get(simplex),corners);
    
    
            //testPermutationInvariance(corners);
    
            testDerivative<domainDim>(f);
            testDerivativeOfValueWRTCoefficients<domainDim>(f);
            testDerivativeOfGradientWRTCoefficients<domainDim>(f);
    
    Oliver Sander's avatar
    Oliver Sander committed
    int main()
    {
    
    Oliver Sander's avatar
    Oliver Sander committed
        // choke on NaN -- don't enable this by default, as there are
        // a few harmless NaN in the loopsolver
        //feenableexcept(FE_INVALID);
    
        std::cout << std::setw(15) << std::setprecision(12);
        
    
        test<RealTuple<1>,1>();
        test<UnitVector<2>,1>();
    
    Oliver Sander's avatar
    Oliver Sander committed
        test<Rotation<3,double>,1>();
        test<Rotation<3,double>,2>();
    
    Oliver Sander's avatar
    Oliver Sander committed
        
        test<RigidBodyMotion<3,double>,1>();
        test<RigidBodyMotion<3,double>,2>();