Newer
Older
#include <iostream>
#include <dune/common/fvector.hh>
#include <dune/geometry/quadraturerules.hh>
#include <dune/geometry/type.hh>
#include <dune/geometry/referenceelements.hh>
#include <dune/localfunctions/lagrange/lagrangelfecache.hh>
#include <dune/gfe/spaces/productmanifold.hh>
#include <dune/gfe/spaces/realtuple.hh>
#include <dune/gfe/spaces/rotation.hh>
#include <dune/gfe/spaces/unitvector.hh>
#include <dune/gfe/localgeodesicfefunction.hh>
/** \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;
}

Sander, Oliver
committed
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-8;
//static const int embeddedDim = LocalFunction::TargetSpace::embeddedDim;
decltype(f.evaluateDerivative(local)) 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 (size_t j=0; j<result.N(); j++)
result[j][i] = fdDer[j];
}
return result;
}

Oliver Sander
committed
/** \brief Test whether interpolation is invariant under permutation of the simplex vertices
* \todo Implement this for all dimensions

Oliver Sander
committed
*/
template <int domainDim, class TargetSpace>

Oliver Sander
committed
void testPermutationInvariance(const std::vector<TargetSpace>& corners)
// works only for 2d domains
if (domainDim!=2)
return;
LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache;
typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement;
GeometryType simplex = GeometryTypes::simplex(domainDim);
std::vector<TargetSpace> cornersRotated1(domainDim+1);
std::vector<TargetSpace> cornersRotated2(domainDim+1);

Oliver Sander
committed
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);

Oliver Sander
committed
// A quadrature rule as a set of test points
const Dune::QuadratureRule<double, domainDim>& quad
= Dune::QuadratureRules<double, domainDim>::rule(simplex, quadOrder);
for (size_t pt=0; pt<quad.size(); pt++) {

Oliver Sander
committed
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);
assert(TargetSpace::distance(v0,v1) < eps);
assert(TargetSpace::distance(v0,v2) < eps);

Oliver Sander
committed
}
template <int domainDim, class TargetSpace>
void testDerivative(const LocalGeodesicFEFunction<domainDim,double,typename LagrangeLocalFiniteElementCache<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();
Dune::FieldMatrix<double, embeddedDim, domainDim> derivative = f.evaluateDerivative(quadPos);
// evaluate fd approximation of derivative

Sander, Oliver
committed
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;
}
template <int domainDim, class TargetSpace>
void testDerivativeOfValueWRTCoefficients(const LocalGeodesicFEFunction<domainDim,double,typename LagrangeLocalFiniteElementCache<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++) {
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);
}
}
}
}
template <int domainDim, class TargetSpace>
void testDerivativeOfGradientWRTCoefficients(const LocalGeodesicFEFunction<domainDim,double,typename LagrangeLocalFiniteElementCache<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();
// loop over the coefficients
for (size_t i=0; i<f.size(); i++) {
// evaluate actual derivative
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;
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;
}
}
}
}
void test(const GeometryType& element)

Oliver Sander
committed
{
std::cout << " --- Testing " << className<TargetSpace>() << ", domain dimension: " << element.dim() << " ---" << std::endl;

Oliver Sander
committed
std::vector<TargetSpace> testPoints;
ValueFactory<TargetSpace>::get(testPoints);
size_t nVertices = Dune::ReferenceElements<double,domainDim>::general(element).size(domainDim);

Oliver Sander
committed
// 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++)
if (diameter(corners) > 0.5*M_PI)
continue;
// Make local gfe function to be tested
LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache;
typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement;
LocalGeodesicFEFunction<domainDim,double,LocalFiniteElement,TargetSpace> f(feCache.get(element),corners);
testDerivative<domainDim>(f);
testDerivativeOfValueWRTCoefficients<domainDim>(f);
testDerivativeOfGradientWRTCoefficients<domainDim>(f);
// 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 functions on 1d elements
////////////////////////////////////////////////////////////////
test<RealTuple<double,1>,1>(GeometryTypes::simplex(1));
test<UnitVector<double,2>,1>(GeometryTypes::simplex(1));
test<UnitVector<double,3>,1>(GeometryTypes::simplex(1));
test<Rotation<double,3>,1>(GeometryTypes::simplex(1));
test<RigidBodyMotion<double,3>,1>(GeometryTypes::simplex(1));
typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
test<CrazyManifold,1>(GeometryTypes::simplex(1));
////////////////////////////////////////////////////////////////
// Test functions on 2d simplex elements
////////////////////////////////////////////////////////////////
test<RealTuple<double,1>,2>(GeometryTypes::simplex(2));
test<UnitVector<double,2>,2>(GeometryTypes::simplex(2));
test<UnitVector<double,3>,2>(GeometryTypes::simplex(2));
test<Rotation<double,3>,2>(GeometryTypes::simplex(2));
test<RigidBodyMotion<double,3>,2>(GeometryTypes::simplex(2));
typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
test<CrazyManifold,2>(GeometryTypes::simplex(2));
////////////////////////////////////////////////////////////////
// Test functions on 2d quadrilateral elements
////////////////////////////////////////////////////////////////
test<RealTuple<double,1>,2>(GeometryTypes::cube(2));
test<UnitVector<double,2>,2>(GeometryTypes::cube(2));
test<UnitVector<double,3>,2>(GeometryTypes::cube(2));
test<Rotation<double,3>,2>(GeometryTypes::cube(2));
test<RigidBodyMotion<double,3>,2>(GeometryTypes::cube(2));
typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
test<CrazyManifold,2>(GeometryTypes::cube(2));