Newer
Older
#include <iostream>
#include <dune/common/fvector.hh>
#include <dune/grid/common/quadraturerules.hh>
#include <dune/gfe/rotation.hh>
#include <dune/gfe/realtuple.hh>
#include <dune/gfe/unitvector.hh>
#include <dune/gfe/localgeodesicfefunction.hh>
/** \brief N-dimensional multi-index
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
*/
template <int N>
class MultiIndex
: public array<unsigned int,N>
{
// The range of each component
unsigned int limit_;
public:
/** \brief Constructor with a given range for each digit */
MultiIndex(unsigned int limit)
: limit_(limit)
{
std::fill(this->begin(), this->end(), 0);
}
/** \brief Increment the MultiIndex */
MultiIndex& operator++() {
for (int i=0; i<N; i++) {
// Augment digit
(*this)[i]++;
// If there is no carry-over we can stop here
if ((*this)[i]<limit_)
break;
(*this)[i] = 0;
}
return *this;
}
/** \brief Compute how many times you can call operator++ before getting to (0,...,0) again */
size_t cycle() const {
size_t result = 1;
for (int i=0; i<N; i++)
result *= limit_;
return result;
}
};
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>
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;
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);

Oliver Sander
committed
/** \brief Test whether interpolation is invariant under permutation of the simplex vertices
*/
template <int domainDim, class TargetSpace>

Oliver Sander
committed
void testPermutationInvariance(const std::vector<TargetSpace>& corners)
// works only for 2d domains
assert(domainDim==2);
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,TargetSpace> f0(corners);
LocalGeodesicFEFunction<2,double,TargetSpace> f1(cornersRotated1);
LocalGeodesicFEFunction<2,double,TargetSpace> f2(cornersRotated2);

Oliver Sander
committed
// A quadrature rule as a set of test points
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++) {

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 std::vector<TargetSpace>& corners)
{
// Make local fe function to be tested
LocalGeodesicFEFunction<domainDim,double,TargetSpace> f(corners);
// 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();
Dune::FieldMatrix<double, TargetSpace::EmbeddedTangentVector::size, domainDim> derivative = f.evaluateDerivative(quadPos);
// evaluate fd approximation of derivative
Dune::FieldMatrix<double, TargetSpace::EmbeddedTangentVector::size, domainDim> fdDerivative = f.evaluateDerivativeFD(quadPos);
Dune::FieldMatrix<double, TargetSpace::EmbeddedTangentVector::size, domainDim> diff = derivative;
diff -= fdDerivative;
if ( diff.infinity_norm() > 100*eps ) {
std::cout << className(corners[0]) << ": 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 testDerivativeOfGradientWRTCoefficients(const std::vector<TargetSpace>& corners)
{
// Make local fe function to be tested
LocalGeodesicFEFunction<domainDim,double,TargetSpace> f(corners);
// 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();
// loop over the coefficients
for (size_t i=0; i<corners.size(); i++) {
// evaluate actual derivative
Tensor3<double, TargetSpace::EmbeddedTangentVector::size, TargetSpace::EmbeddedTangentVector::size, domainDim> derivative;
f.evaluateDerivativeOfGradientWRTCoefficient(quadPos, i, derivative);
// evaluate fd approximation of derivative
Tensor3<double, TargetSpace::EmbeddedTangentVector::size, TargetSpace::EmbeddedTangentVector::size, domainDim> fdDerivative;
for (int j=0; j<TargetSpace::EmbeddedTangentVector::size; j++) {
std::vector<TargetSpace> cornersPlus = corners;
std::vector<TargetSpace> cornersMinus = corners;
typename TargetSpace::CoordinateType aPlus = corners[i].globalCoordinates();
typename TargetSpace::CoordinateType aMinus = corners[i].globalCoordinates();
aPlus[j] += eps;
aMinus[j] -= eps;
cornersPlus[i] = TargetSpace(aPlus);
cornersMinus[i] = TargetSpace(aMinus);
LocalGeodesicFEFunction<domainDim,double,TargetSpace> fPlus(cornersPlus);
LocalGeodesicFEFunction<domainDim,double,TargetSpace> fMinus(cornersMinus);
FieldMatrix<double,TargetSpace::EmbeddedTangentVector::size,domainDim> hPlus = fPlus.evaluateDerivative(quadPos);
FieldMatrix<double,TargetSpace::EmbeddedTangentVector::size,domainDim> hMinus = fMinus.evaluateDerivative(quadPos);
fdDerivative[j] = hPlus;
fdDerivative[j] -= hMinus;
fdDerivative[j] /= 2*eps;
}
if ( (derivative - fdDerivative).infinity_norm() > eps ) {
std::cout << className(corners[0]) << ": Analytical derivative of gradient does not match fd approximation." << std::endl;
std::cout << "gfe: ";
for (int j=0; j<domainDim+1; j++)
std::cout << ", " << corners[j];
std::cout << std::endl;
std::cout << "Analytical:\n " << derivative << std::endl;
std::cout << "FD :\n " << fdDerivative << std::endl;
}
//testDerivativeTangentiality(f.evaluate(quadPos), derivative);
}
}
}
std::cout << " --- Testing RealTuple<1>, domain dimension: " << domainDim << " ---" << std::endl;
typedef RealTuple<1> TargetSpace;
std::vector<TargetSpace> corners = {TargetSpace(1),
TargetSpace(2),
TargetSpace(3)};
testPermutationInvariance<domainDim>(corners);
testDerivative<domainDim>(corners);
std::cout << " --- Testing UnitVector<2>, domain dimension: " << domainDim << " ---" << std::endl;
int nTestPoints = 10;
double testPoints[10][2] = {{1,0}, {0.5,0.5}, {0,1}, {-0.5,0.5}, {-1,0}, {-0.5,-0.5}, {0,-1}, {0.5,-0.5}, {0.1,1}, {1,.1}};
// Set up elements of S^1
std::vector<TargetSpace> corners(domainDim+1);
MultiIndex<domainDim+1> index(nTestPoints);
int numIndices = index.cycle();
for (int i=0; i<numIndices; i++, ++index) {
for (int j=0; j<domainDim+1; j++) {
Dune::array<double,2> w = {testPoints[index[j]][0], testPoints[index[j]][1]};
corners[j] = UnitVector<2>(w);
}
bool spreadOut = false;
for (int j=0; j<domainDim+1; j++)
for (int k=0; k<domainDim+1; k++)
if (UnitVector<2>::distance(corners[j],corners[k]) > M_PI*0.98)
spreadOut = true;
if (spreadOut)
continue;
//testPermutationInvariance(corners);
testDerivative<domainDim>(corners);
testDerivativeOfGradientWRTCoefficients<domainDim>(corners);
std::cout << " --- Testing UnitVector<3>, domain dimension: " << domainDim << " ---" << std::endl;
int nTestPoints = 10;
double testPoints[10][3] = {{1,0,0}, {0,1,0}, {-0.838114,0.356751,-0.412667},
{-0.490946,-0.306456,0.81551},{-0.944506,0.123687,-0.304319},
{-0.6,0.1,-0.2},{0.45,0.12,0.517},
{-0.1,0.3,-0.1},{-0.444506,0.123687,0.104319},{-0.7,-0.123687,-0.304319}};
// Set up elements of S^1
std::vector<TargetSpace> corners(domainDim+1);
MultiIndex<domainDim+1> index(nTestPoints);
int numIndices = index.cycle();
for (int i=0; i<numIndices; i++, ++index) {
for (int j=0; j<domainDim+1; j++) {
Dune::array<double,3> w = {testPoints[index[j]][0], testPoints[index[j]][1], testPoints[index[j]][2]};
corners[j] = UnitVector<3>(w);
}
//testPermutationInvariance(corners);
testDerivative<domainDim>(corners);
testDerivativeOfGradientWRTCoefficients<domainDim>(corners);

Oliver Sander
committed
{
std::cout << " --- Testing Rotation<3>, domain dimension: " << domainDim << " ---" << std::endl;

Oliver Sander
committed
typedef Rotation<3,double> TargetSpace;
FieldVector<double,3> xAxis(0);
xAxis[0] = 1;
FieldVector<double,3> yAxis(0);
yAxis[1] = 1;
FieldVector<double,3> zAxis(0);
zAxis[2] = 1;
std::vector<TargetSpace> corners(domainDim+1);

Oliver Sander
committed
corners[0] = Rotation<3,double>(xAxis,0.1);
corners[1] = Rotation<3,double>(yAxis,0.1);
corners[2] = Rotation<3,double>(zAxis,0.1);
testPermutationInvariance<domainDim>(corners);
// choke on NaN
feenableexcept(FE_INVALID);
//testRealTuples<1>();
testUnitVector2d<1>();
testUnitVector3d<1>();
testUnitVector2d<2>();
testUnitVector3d<2>();