Newer
Older
#include <config.h>
#include <fenv.h>
// Includes for the ADOL-C automatic differentiation library
// Need to come before (almost) all others.
#include <adolc/adouble.h>
#include <adolc/drivers/drivers.h> // use of "Easy to Use" drivers
#include <adolc/taping.h>

Oliver Sander
committed
#include <dune/fufem/utilities/adolcnamespaceinjections.hh>
#include <dune/common/typetraits.hh>
#include <dune/common/bitsetvector.hh>
#include <dune/common/parametertree.hh>
#include <dune/common/parametertreeparser.hh>
#include <dune/common/tuplevector.hh>
#include <dune/grid/uggrid.hh>
#include <dune/grid/utility/structuredgridfactory.hh>
#include <dune/grid/io/file/gmshreader.hh>
#if HAVE_DUNE_FOAMGRID
#include <dune/foamgrid/foamgrid.hh>
#endif
#include <dune/functions/functionspacebases/lagrangebasis.hh>
#include <dune/functions/functionspacebases/compositebasis.hh>
#include <dune/functions/functionspacebases/powerbasis.hh>
#include <dune/fufem/functiontools/boundarydofs.hh>
#include <dune/fufem/dunepython.hh>
#include <dune/solvers/solvers/iterativesolver.hh>
#include <dune/solvers/norms/energynorm.hh>
#include <dune/gfe/localgeodesicfeadolcstiffness.hh>
#include <dune/gfe/mixedlocalgfeadolcstiffness.hh>
#include <dune/gfe/cosseratenergystiffness.hh>
#include <dune/gfe/nonplanarcosseratshellenergy.hh>
#include <dune/gfe/cosseratvtkwriter.hh>
#include <dune/gfe/cosseratvtkreader.hh>
#include <dune/gfe/geodesicfeassembler.hh>
#include <dune/gfe/riemanniantrsolver.hh>
#include <dune/gfe/embeddedglobalgfefunction.hh>
#include <dune/gfe/mixedgfeassembler.hh>
#include <dune/gfe/mixedriemanniantrsolver.hh>
#if HAVE_DUNE_VTK
#include <dune/vtk/vtkreader.hh>
#endif

Lisa Julia Nebel
committed
// grid dimension
const int dim = 2;
const int dimworld = 2;
//#define MIXED_SPACE
// Order of the approximation space for the displacement
const int displacementOrder = 2;
// Order of the approximation space for the microrotations
const int rotationOrder = 2;
#ifndef MIXED_SPACE
static_assert(displacementOrder==rotationOrder, "displacement and rotation order do not match!");
// Image space of the geodesic fe functions
typedef RigidBodyMotion<double,3> TargetSpace;
// Tangent vector of the image space
const int blocksize = TargetSpace::TangentVector::dimension;
using namespace Dune;
int main (int argc, char *argv[]) try
{
// initialize MPI, finalize is done automatically on exit

Oliver Sander
committed
Dune::MPIHelper& mpiHelper = MPIHelper::instance(argc, argv);
// Start Python interpreter
Python::start();
Python::Reference main = Python::import("__main__");
Python::run("import math");
//feenableexcept(FE_INVALID);
Python::runStream()
<< std::endl << "import sys"
<< std::endl << "import os"
<< std::endl << "sys.path.append(os.getcwd() + '/../../problems/')"
<< std::endl;
using namespace TypeTree::Indices;
#ifdef MIXED_SPACE
using SolutionType = TupleVector<std::vector<RealTuple<double,3> >,
std::vector<Rotation<double,3> > >;
#else
typedef std::vector<TargetSpace> SolutionType;
// parse data file
ParameterTree parameterSet;
DUNE_THROW(Exception, "Usage: ./cosserat-continuum <parameter file>");
ParameterTreeParser::readINITree(argv[1], parameterSet);
ParameterTreeParser::readOptions(argc, argv, parameterSet);
// read solver settings
const int numLevels = parameterSet.get<int>("numLevels");
int numHomotopySteps = parameterSet.get<int>("numHomotopySteps");
const double tolerance = parameterSet.get<double>("tolerance");
const int maxTrustRegionSteps = parameterSet.get<int>("maxTrustRegionSteps");
const double initialTrustRegionRadius = parameterSet.get<double>("initialTrustRegionRadius");
const int multigridIterations = parameterSet.get<int>("numIt");
const int nu1 = parameterSet.get<int>("nu1");
const int nu2 = parameterSet.get<int>("nu2");
const int mu = parameterSet.get<int>("mu");
const int baseIterations = parameterSet.get<int>("baseIt");
const double mgTolerance = parameterSet.get<double>("mgTolerance");
const double baseTolerance = parameterSet.get<double>("baseTolerance");
const bool instrumented = parameterSet.get<bool>("instrumented");
std::string resultPath = parameterSet.get("resultPath", "");
// ///////////////////////////////////////
// Create the grid
// ///////////////////////////////////////
#if HAVE_DUNE_FOAMGRID
typedef std::conditional<dim==dimworld,UGGrid<dim>, FoamGrid<dim,dimworld> >::type GridType;
#else
static_assert(dim==dimworld, "FoamGrid needs to be installed to allow problems with dim != dimworld.");
typedef UGGrid<dim> GridType;
#endif
std::shared_ptr<GridType> grid;
FieldVector<double,dimworld> lower(0), upper(1);
std::string structuredGridType = parameterSet["structuredGrid"];
if (structuredGridType == "cube") {
lower = parameterSet.get<FieldVector<double,dimworld> >("lower");
upper = parameterSet.get<FieldVector<double,dimworld> >("upper");

Oliver Sander
committed
auto elements = parameterSet.get<std::array<unsigned int,dim> >("elements");
grid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements);

Oliver Sander
committed
} else {
std::string path = parameterSet.get<std::string>("path");
std::string gridFile = parameterSet.get<std::string>("gridFile");
// Guess the grid file format by looking at the file name suffix
auto dotPos = gridFile.rfind('.');
if (dotPos == std::string::npos)
DUNE_THROW(IOError, "Could not determine grid input file format");
std::string suffix = gridFile.substr(dotPos, gridFile.length()-dotPos);
if (suffix == ".msh")
grid = std::shared_ptr<GridType>(GmshReader<GridType>::read(path + "/" + gridFile));
else if (suffix == ".vtu" or suffix == ".vtp")
#if HAVE_DUNE_VTK
grid = VtkReader<GridType>::createGridFromFile(path + "/" + gridFile);
#else
DUNE_THROW(NotImplemented, "Please install dune-vtk for VTK reading support!");
#endif

Oliver Sander
committed
}
grid->globalRefine(numLevels-1);
grid->loadBalance();
if (mpiHelper.rank()==0)
std::cout << "There are " << grid->leafGridView().comm().size() << " processes" << std::endl;
typedef GridType::LeafGridView GridView;
GridView gridView = grid->leafGridView();
using namespace Dune::Functions::BasisFactory;
#ifdef MIXED_SPACE
const int dimRotation = Rotation<double,dim>::embeddedDim;
auto compositeBasis = makeBasis(
gridView,
composite(
power<dim>(
lagrange<displacementOrder>()
),
power<dimRotation>(
lagrange<rotationOrder>()
)
));
typedef Dune::Functions::LagrangeBasis<GridView,displacementOrder> DeformationFEBasis;
typedef Dune::Functions::LagrangeBasis<GridView,rotationOrder> OrientationFEBasis;
DeformationFEBasis deformationFEBasis(gridView);
OrientationFEBasis orientationFEBasis(gridView);
#else
typedef Dune::Functions::LagrangeBasis<typename GridType::LeafGridView, displacementOrder> FEBasis;
FEBasis feBasis(gridView);
// /////////////////////////////////////////
// Read Dirichlet values
// /////////////////////////////////////////
BitSetVector<1> dirichletVertices(gridView.size(dim), false);
BitSetVector<1> neumannVertices(gridView.size(dim), false);
const GridView::IndexSet& indexSet = gridView.indexSet();

Oliver Sander
committed
// Make Python function that computes which vertices are on the Dirichlet boundary,
// based on the vertex positions.
std::string lambda = std::string("lambda x: (") + parameterSet.get<std::string>("dirichletVerticesPredicate") + std::string(")");
auto pythonDirichletVertices = Python::make_function<bool>(Python::evaluate(lambda));

Oliver Sander
committed
// Same for the Neumann boundary
lambda = std::string("lambda x: (") + parameterSet.get<std::string>("neumannVerticesPredicate", "0") + std::string(")");
auto pythonNeumannVertices = Python::make_function<bool>(Python::evaluate(lambda));

Oliver Sander
committed
for (auto&& vertex : vertices(gridView))
{
bool isDirichlet = pythonDirichletVertices(vertex.geometry().corner(0));
dirichletVertices[indexSet.index(vertex)] = isDirichlet;
bool isNeumann = pythonNeumannVertices(vertex.geometry().corner(0));
neumannVertices[indexSet.index(vertex)] = isNeumann;
BoundaryPatch<GridView> dirichletBoundary(gridView, dirichletVertices);
BoundaryPatch<GridView> neumannBoundary(gridView, neumannVertices);
if (mpiHelper.rank()==0)
std::cout << "Neumann boundary has " << neumannBoundary.numFaces() << " faces\n";
#ifdef MIXED_SPACE
BitSetVector<1> deformationDirichletNodes(deformationFEBasis.size(), false);
constructBoundaryDofs(dirichletBoundary,deformationFEBasis,deformationDirichletNodes);
BitSetVector<1> neumannNodes(deformationFEBasis.size(), false);
constructBoundaryDofs(neumannBoundary,deformationFEBasis,neumannNodes);
BitSetVector<3> deformationDirichletDofs(deformationFEBasis.size(), false);
for (size_t i=0; i<deformationFEBasis.size(); i++)
if (deformationDirichletNodes[i][0])
for (int j=0; j<3; j++)
deformationDirichletDofs[i][j] = true;
BitSetVector<1> orientationDirichletNodes(orientationFEBasis.size(), false);
constructBoundaryDofs(dirichletBoundary,orientationFEBasis,orientationDirichletNodes);
BitSetVector<3> orientationDirichletDofs(orientationFEBasis.size(), false);
for (size_t i=0; i<orientationFEBasis.size(); i++)
if (orientationDirichletNodes[i][0])
for (int j=0; j<3; j++)
orientationDirichletDofs[i][j] = true;
#else
BitSetVector<1> dirichletNodes(feBasis.size(), false);
constructBoundaryDofs(dirichletBoundary,feBasis,dirichletNodes);
BitSetVector<1> neumannNodes(feBasis.size(), false);
constructBoundaryDofs(neumannBoundary,feBasis,neumannNodes);
BitSetVector<blocksize> dirichletDofs(feBasis.size(), false);
for (size_t i=0; i<feBasis.size(); i++)
if (dirichletNodes[i][0])
for (int j=0; j<5; j++)
dirichletDofs[i][j] = true;
// //////////////////////////
// Initial iterate
// //////////////////////////
#ifdef MIXED_SPACE
SolutionType x;
x[_0].resize(deformationFEBasis.size());
lambda = std::string("lambda x: (") + parameterSet.get<std::string>("initialDeformation") + std::string(")");
auto pythonInitialDeformation = Python::make_function<FieldVector<double,3> >(Python::evaluate(lambda));
std::vector<FieldVector<double,3> > v;
Functions::interpolate(deformationFEBasis, v, pythonInitialDeformation);
for (size_t i=0; i<x[_0].size(); i++)
x[_0][i] = v[i];
x[_1].resize(orientationFEBasis.size());
#else

Sander, Oliver
committed
SolutionType x(feBasis.size());
if (parameterSet.hasKey("startFromFile"))
{
std::shared_ptr<GridType> initialIterateGrid;
if (parameterSet.get<bool>("structuredGrid"))
{
std::array<unsigned int,dim> elements = parameterSet.get<std::array<unsigned int,dim> >("elements");
initialIterateGrid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements);
}
else
{
std::string path = parameterSet.get<std::string>("path");
std::string initialIterateGridFilename = parameterSet.get<std::string>("initialIterateGridFilename");
initialIterateGrid = std::shared_ptr<GridType>(GmshReader<GridType>::read(path + "/" + initialIterateGridFilename));
}
SolutionType initialIterate;
GFE::CosseratVTKReader::read(initialIterate, parameterSet.get<std::string>("initialIterateFilename"));
typedef Dune::Functions::LagrangeBasis<typename GridType::LeafGridView, 2> InitialBasis;
InitialBasis initialBasis(initialIterateGrid->leafGridView());
#ifdef PROJECTED_INTERPOLATION
using LocalInterpolationRule = LocalProjectedFEFunction<dim, double, FEBasis::LocalView::Tree::FiniteElement, TargetSpace>;
#else
using LocalInterpolationRule = LocalGeodesicFEFunction<dim, double, FEBasis::LocalView::Tree::FiniteElement, TargetSpace>;
#endif
GFE::EmbeddedGlobalGFEFunction<InitialBasis,LocalInterpolationRule,TargetSpace> initialFunction(initialBasis,initialIterate);
std::vector<FieldVector<double,7> > v;
Dune::Functions::interpolate(feBasis,v,initialFunction);
DUNE_THROW(NotImplemented, "Replace scalar basis by power basis!");
for (size_t i=0; i<x.size(); i++)
x[i] = TargetSpace(v[i]);
lambda = std::string("lambda x: (") + parameterSet.get<std::string>("initialDeformation") + std::string(")");
auto pythonInitialDeformation = Python::make_function<FieldVector<double,3> >(Python::evaluate(lambda));
std::vector<FieldVector<double,3> > v;
Functions::interpolate(feBasis, v, pythonInitialDeformation);
for (size_t i=0; i<x.size(); i++)
x[i].r = v[i];
////////////////////////////////////////////////////////
// Main homotopy loop
////////////////////////////////////////////////////////
// Output initial iterate (of homotopy loop)
#ifdef MIXED_SPACE
CosseratVTKWriter<GridType>::writeMixed<DeformationFEBasis,OrientationFEBasis>(deformationFEBasis,x[_0],
orientationFEBasis,x[_1],
resultPath + "mixed-cosserat_homotopy_0");
#else
CosseratVTKWriter<GridType>::write<FEBasis>(feBasis,x, resultPath + "cosserat_homotopy_0");
for (int i=0; i<numHomotopySteps; i++) {
double homotopyParameter = (i+1)*(1.0/numHomotopySteps);
if (mpiHelper.rank()==0)
std::cout << "Homotopy step: " << i << ", parameter: " << homotopyParameter << std::endl;
// ////////////////////////////////////////////////////////////
// Create an assembler for the energy functional
// ////////////////////////////////////////////////////////////
const ParameterTree& materialParameters = parameterSet.sub("materialParameters");
FieldVector<double,3> neumannValues {0,0,0};
if (parameterSet.hasKey("neumannValues"))
neumannValues = parameterSet.get<FieldVector<double,3> >("neumannValues");
auto neumannFunction = [&]( FieldVector<double,dim> ) {
auto nV = neumannValues;
nV *= homotopyParameter;
return nV;
};
FieldVector<double,3> volumeLoadValues {0,0,0};
volumeLoadValues = parameterSet.get<FieldVector<double,3> >("volumeLoad");
auto volumeLoad = [&]( FieldVector<double,dim>) {
auto vL = volumeLoadValues;
vL *= homotopyParameter;
return vL;
};
if (mpiHelper.rank() == 0) {
std::cout << "Material parameters:" << std::endl;
materialParameters.report();
}
// Assembler using ADOL-C
#ifdef MIXED_SPACE
CosseratEnergyLocalStiffness<decltype(compositeBasis),
3,adouble> cosseratEnergyADOLCLocalStiffness(materialParameters,
&neumannBoundary,
neumannFunction,
volumeLoad);
MixedLocalGFEADOLCStiffness<decltype(compositeBasis),
RealTuple<double,3>,
Rotation<double,3> > localGFEADOLCStiffness(&cosseratEnergyADOLCLocalStiffness);
MixedGFEAssembler<decltype(compositeBasis),
RealTuple<double,3>,
Rotation<double,3> > assembler(compositeBasis, &localGFEADOLCStiffness);
#else
std::shared_ptr<GFE::LocalEnergy<FEBasis,RigidBodyMotion<adouble,3> > > localCosseratEnergy;

Sander, Oliver
committed
if (dim==dimworld)
{
localCosseratEnergy = std::make_shared<CosseratEnergyLocalStiffness<FEBasis,3,adouble> >(materialParameters,
&neumannBoundary,
neumannFunction,
volumeLoad);

Sander, Oliver
committed
}
else
{
localCosseratEnergy = std::make_shared<NonplanarCosseratShellEnergy<FEBasis,3,adouble> >(materialParameters,
nullptr,
&neumannBoundary,
neumannFunction,
volumeLoad);

Sander, Oliver
committed
}
LocalGeodesicFEADOLCStiffness<FEBasis,
TargetSpace> localGFEADOLCStiffness(localCosseratEnergy.get());
GeodesicFEAssembler<FEBasis,TargetSpace> assembler(gridView, localGFEADOLCStiffness);
// /////////////////////////////////////////////////
// Create a Riemannian trust-region solver
// /////////////////////////////////////////////////
#ifdef MIXED_SPACE
MixedRiemannianTrustRegionSolver<GridType,
decltype(compositeBasis),
DeformationFEBasis, RealTuple<double,3>,
OrientationFEBasis, Rotation<double,3> > solver;
#else
RiemannianTrustRegionSolver<FEBasis,TargetSpace> solver;
&assembler,
#ifdef MIXED_SPACE
deformationFEBasis,
orientationFEBasis,
#endif
#ifdef MIXED_SPACE
deformationDirichletDofs,
orientationDirichletDofs,
#else
dirichletDofs,
tolerance,
maxTrustRegionSteps,
initialTrustRegionRadius,
multigridIterations,
mgTolerance,
mu, nu1, nu2,
baseIterations,
baseTolerance,
instrumented);
solver.setScaling(parameterSet.get<FieldVector<double,6> >("trustRegionScaling"));
////////////////////////////////////////////////////////
// Set Dirichlet values
////////////////////////////////////////////////////////
Python::Reference dirichletValuesClass = Python::import(parameterSet.get<std::string>("problem") + "-dirichlet-values");

Oliver Sander
committed
Python::Callable C = dirichletValuesClass.get("DirichletValues");

Oliver Sander
committed
// Call a constructor.
Python::Reference dirichletValuesPythonObject = C(homotopyParameter);

Oliver Sander
committed
// Extract object member functions as Dune functions
auto deformationDirichletValues = Python::make_function<FieldVector<double,3> >(dirichletValuesPythonObject.get("deformation"));
auto orientationDirichletValues = Python::make_function<FieldMatrix<double,3,3> >(dirichletValuesPythonObject.get("orientation"));

Oliver Sander
committed
std::vector<FieldVector<double,3> > ddV;
std::vector<FieldMatrix<double,3,3> > dOV;
#ifdef MIXED_SPACE
Functions::interpolate(deformationFEBasis, ddV, deformationDirichletValues, deformationDirichletDofs);
Functions::interpolate(orientationFEBasis, dOV, orientationDirichletValues, orientationDirichletDofs);
for (size_t j=0; j<x[_0].size(); j++)
if (deformationDirichletNodes[j][0])
x[_0][j] = ddV[j];
for (size_t j=0; j<x[_1].size(); j++)
if (orientationDirichletNodes[j][0])
x[_1][j].set(dOV[j]);
#else
Functions::interpolate(feBasis, ddV, deformationDirichletValues, dirichletDofs);
Functions::interpolate(feBasis, dOV, orientationDirichletValues, dirichletDofs);
if (dirichletNodes[j][0])
{
x[j].r = ddV[j];
x[j].q.set(dOV[j]);
}
// /////////////////////////////////////////////////////
// Solve!
// /////////////////////////////////////////////////////
solver.setInitialIterate(x);
solver.solve();
x = solver.getSol();
// Output result of each homotopy step
std::stringstream iAsAscii;
iAsAscii << i+1;
#ifdef MIXED_SPACE
CosseratVTKWriter<GridType>::writeMixed<DeformationFEBasis,OrientationFEBasis>(deformationFEBasis,x[_0],
orientationFEBasis,x[_1],
resultPath + "mixed-cosserat_homotopy_" + iAsAscii.str());
#else
CosseratVTKWriter<GridType>::write<FEBasis>(feBasis,x, resultPath + "cosserat_homotopy_" + iAsAscii.str());
// //////////////////////////////
// Output result
// //////////////////////////////
#ifndef MIXED_SPACE
// Write the corresponding coefficient vector: verbatim in binary, to be completely lossless
// This data may be used by other applications measuring the discretization error
BlockVector<TargetSpace::CoordinateType> xEmbedded(x.size());
for (size_t i=0; i<x.size(); i++)
xEmbedded[i] = x[i].globalCoordinates();
std::ofstream outFile("cosserat-continuum-result-" + std::to_string(numLevels) + ".data", std::ios_base::binary);
MatrixVector::Generic::writeBinary(outFile, xEmbedded);
// finally: compute the average deformation of the Neumann boundary
// That is what we need for the locking tests
FieldVector<double,3> averageDef(0);
#ifdef MIXED_SPACE
for (size_t i=0; i<x[_0].size(); i++)
if (neumannNodes[i][0])
averageDef += x[_0][i].globalCoordinates();
#else
if (neumannNodes[i][0])
averageDef /= neumannNodes.count();
if (mpiHelper.rank()==0 and parameterSet.hasKey("neumannValues"))
std::cout << "Neumann values = " << parameterSet.get<FieldVector<double, 3> >("neumannValues") << " "
<< ", average deflection: " << averageDef << std::endl;
} catch (Exception& e)
{
std::cout << e.what() << std::endl;
}