Newer
Older

Oliver Sander
committed
#include <config.h>
//#define HARMONIC_ENERGY_FD_GRADIENT
//#define HARMONIC_ENERGY_FD_INNER_GRADIENT

Oliver Sander
committed

Oliver Sander
committed
#include <dune/common/bitsetvector.hh>
#include <dune/common/parametertree.hh>
#include <dune/common/parametertreeparser.hh>

Oliver Sander
committed
#include <dune/grid/uggrid.hh>
#include <dune/grid/onedgrid.hh>
#include <dune/grid/utility/structuredgridfactory.hh>
#include <dune/grid/io/file/vtk/vtkwriter.hh>
#include <dune/grid/io/file/amirameshreader.hh>
#include <dune/grid/io/file/gmshreader.hh>

Oliver Sander
committed
#include <dune/fufem/functionspacebases/p1nodalbasis.hh>
#include <dune/fufem/functionspacebases/p2nodalbasis.hh>
#include <dune/fufem/boundarypatch.hh>
#include <dune/fufem/assemblers/operatorassembler.hh>
#include <dune/fufem/assemblers/localassemblers/laplaceassembler.hh>
#include <dune/fufem/assemblers/localassemblers/massassembler.hh>
#include <dune/fufem/functiontools/boundarydofs.hh>

Oliver Sander
committed
#include <dune/fufem/functiontools/basisinterpolator.hh>
#include <dune/fufem/functions/vtkbasisgridfunction.hh>

Oliver Sander
committed
#include <dune/solvers/solvers/iterativesolver.hh>
#include <dune/solvers/norms/h1seminorm.hh>

Oliver Sander
committed
#include <dune/gfe/unitvector.hh>
#include <dune/gfe/harmonicenergystiffness.hh>
#include <dune/gfe/geodesicfeassembler.hh>
#include <dune/gfe/riemanniantrsolver.hh>
#include <dune/gfe/geodesicfefunctionadaptor.hh>
#include <dune/gfe/gfedifferencenormsquared.hh>

Oliver Sander
committed
// grid dimension

Oliver Sander
committed
typedef UnitVector<double,3> TargetSpace;

Oliver Sander
committed
typedef std::vector<TargetSpace> SolutionType;
const int blocksize = TargetSpace::TangentVector::dimension;

Oliver Sander
committed
using namespace Dune;
using std::string;

Oliver Sander
committed
struct DirichletFunction
: public Dune::VirtualFunction<FieldVector<double,dim>, TargetSpace::CoordinateType >

Oliver Sander
committed
{
void evaluate(const FieldVector<double, dim>& x, TargetSpace::CoordinateType& out) const {

Oliver Sander
committed

Oliver Sander
committed
FieldVector<double,3> axis;
axis[0] = x[0]; axis[1] = x[1]; axis[2] = 1;
Rotation<double,3> rotation(axis, x.two_norm()*M_PI*3);

Oliver Sander
committed
FieldMatrix<double,3,3> rMat;
rotation.matrix(rMat);
out = rMat[2];
#if 1 // This data seems to have the necessary smoothness to have optimal
// convergence order even for 3rd-order elements
double localX = (x[0] + 2)/4;
double localY = (x[1] + 1)/2;
double angleX = 0.5 * M_PI * sin(M_PI*localX);
double angleY = 0.5 * M_PI * sin(M_PI*localY);
// Rotation matrix around the x axis
FieldMatrix<double,TargetSpace::CoordinateType::dimension,TargetSpace::CoordinateType::dimension> rotationX(0);
rotationX[0][0] = 1;
rotationX[1][1] = cos(angleY);
rotationX[1][2] = -sin(angleY);
rotationX[2][1] = sin(angleY);
rotationX[2][2] = cos(angleY);
// Rotation matrix around the y axis
FieldMatrix<double,TargetSpace::CoordinateType::dimension,TargetSpace::CoordinateType::dimension> rotationY(0);
rotationY[0][0] = cos(angleX);
rotationY[0][2] = -sin(angleX);
rotationY[1][1] = 1;
rotationY[2][0] = sin(angleX);
rotationY[2][2] = cos(angleX);
//angle *= -4*x[1]*(x[1]-1);
TargetSpace::CoordinateType unitVector(0); unitVector[2] = 1;
TargetSpace::CoordinateType tmp;
rotationX.mv(unitVector,tmp);
rotationY.mv(tmp,out);
#endif

Oliver Sander
committed
}
};

Oliver Sander
committed
template <class GridType>
void solve (const shared_ptr<GridType>& grid,
SolutionType& x,
int numLevels,
const ParameterTree& parameters)

Oliver Sander
committed
{
// read solver setting
const double innerTolerance = parameters.get<double>("innerTolerance");
const double tolerance = parameters.get<double>("tolerance");
const int maxTrustRegionSteps = parameters.get<int>("maxTrustRegionSteps");
const double initialTrustRegionRadius = parameters.get<double>("initialTrustRegionRadius");
const int multigridIterations = parameters.get<int>("numIt");
// /////////////////////////////////////////
// Read Dirichlet values
// /////////////////////////////////////////
BitSetVector<1> allNodes(grid->size(dim));
allNodes.setAll();
BoundaryPatch<typename GridType::LeafGridView> dirichletBoundary(grid->leafGridView(), allNodes);

Oliver Sander
committed
#if defined THIRD_ORDER
typedef P3NodalBasis<typename GridType::LeafGridView,double> FEBasis;
#elif defined SECOND_ORDER
typedef P2NodalBasis<typename GridType::LeafGridView,double> FEBasis;
#else
typedef P1NodalBasis<typename GridType::LeafGridView,double> FEBasis;
#endif
BitSetVector<blocksize> dirichletNodes;
constructBoundaryDofs(dirichletBoundary,feBasis,dirichletNodes);

Oliver Sander
committed
// //////////////////////////

Oliver Sander
committed
// //////////////////////////

Oliver Sander
committed
BlockVector<TargetSpace::CoordinateType> dirichletFunctionValues;

Oliver Sander
committed
DirichletFunction dirichletFunction;
Functions::interpolate(feBasis, dirichletFunctionValues, dirichletFunction);

Oliver Sander
committed
TargetSpace::CoordinateType innerValue(0);

Oliver Sander
committed
innerValue[0] = 1;
innerValue[1] = 0;
for (size_t i=0; i<x.size(); i++)
x[i] = (dirichletNodes[i][0]) ? dirichletFunctionValues[i] : innerValue;

Oliver Sander
committed
// ////////////////////////////////////////////////////////////
// Create an assembler for the Harmonic Energy Functional
// ////////////////////////////////////////////////////////////
HarmonicEnergyLocalStiffness<typename GridType::LeafGridView,typename FEBasis::LocalFiniteElement, TargetSpace> harmonicEnergyLocalStiffness;
GeodesicFEAssembler<FEBasis,TargetSpace> assembler(grid->leafGridView(),
&harmonicEnergyLocalStiffness);

Oliver Sander
committed
// ///////////////////////////////////////////
// Create a solver for the rod problem
// ///////////////////////////////////////////
RiemannianTrustRegionSolver<GridType,TargetSpace> solver;
solver.setup(*grid,
&assembler,
x,
dirichletNodes,
tolerance,
maxTrustRegionSteps,
initialTrustRegionRadius,
multigridIterations,
innerTolerance,
1, 3, 3,
100, // iterations of the base solver
1e-8, // base tolerance
false); // instrumentation
// /////////////////////////////////////////////////////
// Solve!
// /////////////////////////////////////////////////////

Oliver Sander
committed
solver.solve();
x = solver.getSol();
}
int main (int argc, char *argv[]) try
{
// parse data file

Oliver Sander
committed
if (argc==2)
ParameterTreeParser::readINITree(argv[1], parameterSet);

Oliver Sander
committed
else
ParameterTreeParser::readINITree("harmonicmaps-eoc.parset", parameterSet);

Oliver Sander
committed
// read solver settings
const int numLevels = parameterSet.get<int>("numLevels");
// grid file
std::string gridFileName = parameterSet.get<std::string>("gridFile");

Oliver Sander
committed
const int numBaseElements = parameterSet.get<int>("numBaseElements");
FieldVector<double,dim> lowerLeft = parameterSet.get<FieldVector<double,dim> >("lowerLeft");
FieldVector<double,dim> upperRight = parameterSet.get<FieldVector<double,dim> >("upperRight");

Oliver Sander
committed
// ///////////////////////////////////////////////////////////
// First compute the 'exact' solution on a very fine grid
// ///////////////////////////////////////////////////////////
typedef std::conditional<dim==1,OneDGrid,UGGrid<dim> >::type GridType;
// Create the reference grid
shared_ptr<GridType> referenceGrid;
if (parameterSet.get<std::string>("gridType")=="structured") {
array<unsigned int,dim> elements;
elements.fill(numBaseElements);
referenceGrid = StructuredGridFactory<GridType>::createSimplexGrid(lowerLeft,
upperRight,
elements);
} else {
if (gridFileName.rfind(".msh")!=std::string::npos)
referenceGrid = shared_ptr<GridType>(GmshReader<GridType>::read(gridFileName));
else
referenceGrid = shared_ptr<GridType>(AmiraMeshReader<GridType>::read(gridFileName));

Oliver Sander
committed
referenceGrid->globalRefine(numLevels-1);
// Solve the rod Dirichlet problem
SolutionType referenceSolution;
solve(referenceGrid, referenceSolution, numLevels, parameterSet);
BlockVector<TargetSpace::CoordinateType> xEmbedded(referenceSolution.size());
for (size_t j=0; j<referenceSolution.size(); j++)
xEmbedded[j] = referenceSolution[j].globalCoordinates();
// Set up a basis of the underlying fe space
#ifdef THIRD_ORDER
typedef P3NodalBasis<GridType::LeafGridView,double> FEBasis;
#elif defined SECOND_ORDER
typedef P2NodalBasis<GridType::LeafGridView,double> FEBasis;
#else

Oliver Sander
committed
typedef P1NodalBasis<GridType::LeafGridView,double> FEBasis;
FEBasis referenceBasis(referenceGrid->leafGridView());
#if !defined THIRD_ORDER && ! defined SECOND_ORDER
VTKWriter<GridType::LeafGridView> vtkWriter(referenceGrid->leafGridView());
shared_ptr<VTKBasisGridFunction<FEBasis,BlockVector<TargetSpace::CoordinateType> > > vtkVectorField
= Dune::shared_ptr<VTKBasisGridFunction<FEBasis,BlockVector<TargetSpace::CoordinateType> > >
(new VTKBasisGridFunction<FEBasis,BlockVector<TargetSpace::CoordinateType> >(referenceBasis, xEmbedded, "unit vectors"));
vtkWriter.addVertexData(vtkVectorField);
vtkWriter.write("reference_result");
#endif
// //////////////////////////////////////////////////////////////////////
// Compute mass matrix and laplace matrix to emulate L2 and H1 norms
// //////////////////////////////////////////////////////////////////////
OperatorAssembler<FEBasis,FEBasis> operatorAssembler(referenceBasis, referenceBasis);

Oliver Sander
committed
LaplaceAssembler<GridType, FEBasis::LocalFiniteElement, FEBasis::LocalFiniteElement> laplaceLocalAssembler;
MassAssembler<GridType, FEBasis::LocalFiniteElement, FEBasis::LocalFiniteElement> massMatrixLocalAssembler;

Oliver Sander
committed
typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > ScalarMatrixType;
ScalarMatrixType laplace, massMatrix;
operatorAssembler.assemble(laplaceLocalAssembler, laplace);
operatorAssembler.assemble(massMatrixLocalAssembler, massMatrix);

Oliver Sander
committed
// ///////////////////////////////////////////////////////////
// Compute on all coarser levels, and compare
// ///////////////////////////////////////////////////////////
std::ofstream logFile("harmonicmaps-eoc.results");
logFile << "# mesh size, max-norm, L2-norm, h1-seminorm" << std::endl;
for (int i=1; i<numLevels; i++) {

Oliver Sander
committed
shared_ptr<GridType> grid;
if (parameterSet.get<std::string>("gridType")=="structured") {
array<unsigned int,dim> elements;
elements.fill(numBaseElements);
grid = StructuredGridFactory<GridType>::createSimplexGrid(lowerLeft,
upperRight,
elements);
} else {
if (gridFileName.rfind(".msh")!=std::string::npos)
grid = shared_ptr<GridType>(GmshReader<GridType>::read(gridFileName));
else
grid = shared_ptr<GridType>(AmiraMeshReader<GridType>::read(gridFileName));

Oliver Sander
committed
grid->globalRefine(i-1);
// compute again
SolutionType solution;
solve(grid, solution, i, parameterSet);
// write solution
std::stringstream numberAsAscii;
numberAsAscii << i;
BlockVector<TargetSpace::CoordinateType> xEmbedded(solution.size());
xEmbedded[j] = solution[j].globalCoordinates();
FEBasis feBasis(grid->leafGridView());
VTKWriter<GridType::LeafGridView> vtkWriter(grid->leafGridView());
shared_ptr<VTKBasisGridFunction<FEBasis,BlockVector<TargetSpace::CoordinateType> > > vtkVectorField
= Dune::shared_ptr<VTKBasisGridFunction<FEBasis,BlockVector<TargetSpace::CoordinateType> > >
(new VTKBasisGridFunction<FEBasis,BlockVector<TargetSpace::CoordinateType> >(feBasis, xEmbedded, "unit vectors"));
vtkWriter.addVertexData(vtkVectorField);
vtkWriter.write("harmonic_result_" + numberAsAscii.str());
double newL2 = GFEDifferenceNormSquared<FEBasis,TargetSpace>::compute(*referenceGrid, referenceSolution,
*grid, solution,
&massMatrixLocalAssembler);
newL2 = std::sqrt(newL2);
double newH1 = GFEDifferenceNormSquared<FEBasis,TargetSpace>::compute(*referenceGrid, referenceSolution,
*grid, solution,
&laplaceLocalAssembler);
newH1 = std::sqrt(newH1);

Oliver Sander
committed
// Prolong solution to the very finest grid
for (int j=i; j<numLevels; j++) {
#if defined THIRD_ORDER || defined SECOND_ORDER
GeodesicFEFunctionAdaptor<FEBasis,TargetSpace>::higherOrderGFEFunctionAdaptor<order>(basis, *grid, solution);
GeodesicFEFunctionAdaptor<FEBasis,TargetSpace>::geodesicFEFunctionAdaptor(*grid, solution);
}
// Interpret TargetSpace as isometrically embedded into an R^m, because this is
// how the corresponding Sobolev spaces are defined.
BlockVector<TargetSpace::CoordinateType> difference(referenceSolution.size());
for (size_t j=0; j<referenceSolution.size(); j++)
difference[j] = solution[j].globalCoordinates() - referenceSolution[j].globalCoordinates();

Oliver Sander
committed
H1SemiNorm< BlockVector<TargetSpace::CoordinateType> > h1Norm(laplace);
H1SemiNorm< BlockVector<TargetSpace::CoordinateType> > l2Norm(massMatrix);

Oliver Sander
committed
// Compute max-norm difference
std::cout << "Level: " << i-1 << " vertices: " << xEmbedded.size() << std::endl;
std::cout << "h: " << std::pow(0.5, i-1) << std::endl;

Oliver Sander
committed
std::cout << "Level: " << i-1
<< ", max-norm error: " << difference.infinity_norm()
<< std::endl;
std::cout << "Level: " << i-1
<< ", L2 error: " << l2Norm(difference) << ", new: " << newL2

Oliver Sander
committed
<< std::endl;
std::cout << "Level: " << i-1
<< ", H1 error: " << h1Norm(difference) << ", new: " << newH1

Oliver Sander
committed
<< std::endl;
logFile << std::pow(0.5, i-1) << " " << difference.infinity_norm()
<< " " << l2Norm(difference)
<< " " << h1Norm(difference)
<< std::endl;

Oliver Sander
committed
}
} catch (Exception e) {
std::cout << e << std::endl;
}