From 025574680c87a4e9e1cdc0e58c14c57602be13c3 Mon Sep 17 00:00:00 2001 From: Oliver Sander <sander@igpm.rwth-aachen.de> Date: Wed, 7 Oct 2015 20:57:50 +0200 Subject: [PATCH] Add test for the LocalProjectedFEFunction class --- test/CMakeLists.txt | 1 + test/localprojectedfefunctiontest.cc | 285 +++++++++++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 test/localprojectedfefunctiontest.cc diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4b0d35ff..3362732e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,6 +9,7 @@ set(TESTS localgeodesicfefunctiontest localgeodesicfestiffnesstest localgfetestfunctiontest + localprojectedfefunctiontest nestednesstest nonconvexitytest nonconvexitytest_simple diff --git a/test/localprojectedfefunctiontest.cc b/test/localprojectedfefunctiontest.cc new file mode 100644 index 00000000..cc2e5adc --- /dev/null +++ b/test/localprojectedfefunctiontest.cc @@ -0,0 +1,285 @@ +#include <config.h> + +#include <fenv.h> +#include <iostream> +#include <iomanip> + +#include <dune/common/fvector.hh> + +#include <dune/geometry/quadraturerules.hh> +#include <dune/geometry/type.hh> +#include <dune/geometry/referenceelements.hh> + +#include <dune/localfunctions/lagrange/pqkfactory.hh> + +#include <dune/gfe/rotation.hh> +#include <dune/gfe/realtuple.hh> +#include <dune/gfe/unitvector.hh> + +#include <dune/gfe/localprojectedfefunction.hh> +#include "multiindex.hh" +#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 dim, class ctype, class LocalFunction> +auto +evaluateDerivativeFD(const LocalFunction& f, const Dune::FieldVector<ctype, dim>& local) +-> decltype(f.evaluateDerivative(local)) +{ + double eps = 1e-6; + static const int embeddedDim = LocalFunction::TargetSpace::embeddedDim; + Dune::FieldMatrix<ctype, embeddedDim, dim> result; + + for (int i=0; i<dim; i++) { + + Dune::FieldVector<ctype, dim> forward = local; + Dune::FieldVector<ctype, dim> backward = local; + + forward[i] += eps; + backward[i] -= eps; + + auto fdDer = f.evaluate(forward).globalCoordinates() - f.evaluate(backward).globalCoordinates(); + fdDer /= 2*eps; + + for (int j=0; j<embeddedDim; j++) + result[j][i] = fdDer[j]; + + } + + return result; +} + + +template <int domainDim> +void testDerivativeTangentiality(const RealTuple<double,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> +void testDerivativeTangentiality(const UnitVector<double,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; + 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<double,vectorDim-1>& x, + const FieldMatrix<double,vectorDim,domainDim>& derivative) +{ +} + +// the columns of the derivative must be tangential to the manifold +template <int domainDim, int vectorDim> +void testDerivativeTangentiality(const RigidBodyMotion<double,3>& 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]; + + GFE::LocalProjectedFEFunction<2,double,LocalFiniteElement,TargetSpace> f0(feCache.get(simplex), corners); + GFE::LocalProjectedFEFunction<2,double,LocalFiniteElement,TargetSpace> f1(feCache.get(simplex), cornersRotated1); + GFE::LocalProjectedFEFunction<2,double,LocalFiniteElement,TargetSpace> f2(feCache.get(simplex), cornersRotated2); + + // A quadrature rule as a set of test points + 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; + + l1[0] = quadPos[1]; + l1[1] = 1-quadPos[0]-quadPos[1]; + + l2[0] = 1-quadPos[0]-quadPos[1]; + l2[1] = quadPos[0]; + + // evaluate the three functions + TargetSpace v0 = f0.evaluate(l0); + TargetSpace v1 = f1.evaluate(l1); + TargetSpace v2 = f2.evaluate(l2); + + // Check that they are all equal + assert(TargetSpace::distance(v0,v1) < eps); + assert(TargetSpace::distance(v0,v2) < eps); + + } + +} + +template <int domainDim, class TargetSpace> +void testDerivative(const GFE::LocalProjectedFEFunction<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 auto& 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(); + + // evaluate actual derivative + Dune::FieldMatrix<double, embeddedDim, domainDim> derivative = f.evaluateDerivative(quadPos); + + // evaluate fd approximation of derivative + Dune::FieldMatrix<double, embeddedDim, domainDim> fdDerivative = evaluateDerivativeFD(f,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 <class TargetSpace, int domainDim> +void test(const GeometryType& element) +{ + std::cout << " --- Testing " << className<TargetSpace>() << ", domain dimension: " << element.dim() << " ---" << std::endl; + + std::vector<TargetSpace> testPoints; + ValueFactory<TargetSpace>::get(testPoints); + + int nTestPoints = testPoints.size(); + size_t nVertices = Dune::ReferenceElements<double,domainDim>::general(element).size(domainDim); + + // Set up elements of the target space + std::vector<TargetSpace> corners(nVertices); + + MultiIndex index(nVertices, nTestPoints); + int numIndices = index.cycle(); + + for (int i=0; i<numIndices; i++, ++index) { + + for (size_t j=0; j<nVertices; 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; + + GFE::LocalProjectedFEFunction<domainDim,double,LocalFiniteElement,TargetSpace> f(feCache.get(element),corners); + + //testPermutationInvariance(corners); + testDerivative<domainDim>(f); + + } + +} + + +int main() +{ + // 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); + + GeometryType element; + + //////////////////////////////////////////////////////////////// + // Test functions on 1d elements + //////////////////////////////////////////////////////////////// + element.makeSimplex(1); + + test<RealTuple<double,1>,1>(element); + test<UnitVector<double,2>,1>(element); + test<UnitVector<double,3>,1>(element); + test<Rotation<double,3>,1>(element); +// test<RigidBodyMotion<double,3>,1>(element); + + //////////////////////////////////////////////////////////////// + // Test functions on 2d simplex elements + //////////////////////////////////////////////////////////////// + element.makeSimplex(2); + + test<RealTuple<double,1>,2>(element); + test<UnitVector<double,2>,2>(element); + test<UnitVector<double,3>,2>(element); + test<Rotation<double,3>,2>(element); +// test<RigidBodyMotion<double,3>,2>(element); + + //////////////////////////////////////////////////////////////// + // Test functions on 2d quadrilateral elements + //////////////////////////////////////////////////////////////// + element.makeCube(2); + + test<RealTuple<double,1>,2>(element); + test<UnitVector<double,2>,2>(element); + test<UnitVector<double,3>,2>(element); + test<Rotation<double,3>,2>(element); +// test<RigidBodyMotion<double,3>,2>(element); + +} -- GitLab