diff --git a/dune/tectonic/factories/twoblocksfactory.cc b/dune/tectonic/factories/twoblocksfactory.cc index f58defd7d1f47874e282569ec9b8ddc228173cf3..39c47cd7ab87bb3721d74ef8d84417628d59f2d6 100644 --- a/dune/tectonic/factories/twoblocksfactory.cc +++ b/dune/tectonic/factories/twoblocksfactory.cc @@ -35,8 +35,6 @@ void TwoBlocksFactory<HostGridType, VectorTEMPLATE>::setBodies() { cuboidGeometries_[1] = std::make_shared<CuboidGeometry>(origins[1], lengths[1], heights[1], depths[1]); cuboidGeometries_[1]->addWeakeningPatch(frictionParset, origins[1], {origins[1][0] + lengths[1], origins[1][1], 0}); - cuboidGeometries_[1]->addWeakeningPatch(frictionParset, origins[1], {origins[1][0] + lengths[1], origins[1][1] + heights[1], 0}); - #elif MY_DIM == 2 origins[0] = {0, 0}; origins[1] = {lengths[0]/2.0, origins[0][1] + heights[0]}; @@ -45,8 +43,7 @@ void TwoBlocksFactory<HostGridType, VectorTEMPLATE>::setBodies() { cuboidGeometries_[0]->addWeakeningPatch(frictionParset, {origins[0][0], origins[0][1]+ heights[0]}, {origins[0][0] + lengths[0], origins[0][1]+ heights[0]}); cuboidGeometries_[1] = std::make_shared<CuboidGeometry>(origins[1], lengths[1], heights[1]); - cuboidGeometries_[1]->addWeakeningPatch(frictionParset, origins[1], {origins[1][0] + lengths[1], origins[1][1]}); - cuboidGeometries_[1]->addWeakeningPatch(frictionParset, {origins[1][0] + lengths[1], origins[1][1]}, {origins[1][0] + lengths[1], origins[1][1] + heights[1]}); + cuboidGeometries_[1]->addWeakeningPatch(frictionParset, {origins[1][0] + 0.2*lengths[1], origins[1][1]}, {origins[1][0] + 0.8*lengths[1], origins[1][1]}); #else #error CuboidGeometry only supports 2D and 3D!" #endif @@ -55,13 +52,15 @@ void TwoBlocksFactory<HostGridType, VectorTEMPLATE>::setBodies() { gridConstructor_ = std::make_unique<GridsConstructor<HostGridType>>(cuboidGeometries_); auto& grids = gridConstructor_->getGrids(); + std::array<double, 2> smallestDiameter = {this->parset_.template get<double>("body0.smallestDiameter"), this->parset_.template get<double>("body1.smallestDiameter")}; + for (size_t i=0; i<this->bodyCount_; i++) { const auto& cuboidGeometry = *cuboidGeometries_[i]; // define weak patch and refine grid const auto& weakeningRegions = cuboidGeometry.weakeningRegions(); for (size_t j=0; j<weakeningRegions.size(); j++) { - refine(*grids[i], weakeningRegions[j], this->parset_.template get<double>("boundary.friction.smallestDiameter"), CuboidGeometry::lengthScale()); + refine(*grids[i], weakeningRegions[j], smallestDiameter[i], CuboidGeometry::lengthScale()); } // determine minDiameter and maxDiameter @@ -177,7 +176,7 @@ void TwoBlocksFactory<HostGridType, VectorTEMPLATE>::setBoundaryConditions() { // body1: Neumann boundary (upper) // normal load - std::shared_ptr<Dune::BitSetVector<dim>> loadNeumannNodes = std::make_shared<Dune::BitSetVector<dim>>(leafVertexCount1); + /*std::shared_ptr<Dune::BitSetVector<dim>> loadNeumannNodes = std::make_shared<Dune::BitSetVector<dim>>(leafVertexCount1); for (int j=0; j<leafVertexCount1; j++) { if (leafFaces_[1]->upper.containsVertex(j)) (*loadNeumannNodes)[j][1] = true; @@ -193,7 +192,7 @@ void TwoBlocksFactory<HostGridType, VectorTEMPLATE>::setBoundaryConditions() { loadNeumannBoundary->setBoundaryPatch(body1->gridView(), loadNeumannNodes); loadNeumannBoundary->setBoundaryFunction(constantFunction); - body1->addBoundaryCondition(loadNeumannBoundary); + body1->addBoundaryCondition(loadNeumannBoundary);*/ } #include "twoblocksfactory_tmpl.cc" diff --git a/dune/tectonic/spatial-solving/CMakeLists.txt b/dune/tectonic/spatial-solving/CMakeLists.txt index 75fd6237669e0022c8304dc463775b465c0887b7..e7b68c8644354e5aa46db465b9dd7ef895df2e4c 100644 --- a/dune/tectonic/spatial-solving/CMakeLists.txt +++ b/dune/tectonic/spatial-solving/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory("contact") add_subdirectory("tnnmg") add_subdirectory("preconditioners") diff --git a/dune/tectonic/spatial-solving/contact/CMakeLists.txt b/dune/tectonic/spatial-solving/contact/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b651632d706f6b55dee76d0cd346e8f7e353238c --- /dev/null +++ b/dune/tectonic/spatial-solving/contact/CMakeLists.txt @@ -0,0 +1,15 @@ +add_custom_target(tectonic_dune_spatial-solving_contact SOURCES + dualmortarcoupling.hh + dualmortarcoupling.cc + nbodyassembler.hh + nbodyassembler.cc + nbodycontacttransfer.hh + nbodycontacttransfer.cc +) + +#install headers +install(FILES + dualmortarcoupling.hh + nbodyassembler.hh + nbodycontacttransfer.hh +DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/tectonic) diff --git a/dune/tectonic/spatial-solving/contact/dualmortarcoupling.cc b/dune/tectonic/spatial-solving/contact/dualmortarcoupling.cc new file mode 100644 index 0000000000000000000000000000000000000000..9f4daa82f20240a3232672eacee18d587555175b --- /dev/null +++ b/dune/tectonic/spatial-solving/contact/dualmortarcoupling.cc @@ -0,0 +1,344 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set ts=8 sw=4 et sts=4: +#include <dune/common/exceptions.hh> + +#include <dune/istl/matrixindexset.hh> +#include <dune/istl/io.hh> + +#include <dune/geometry/quadraturerules.hh> +#include <dune/geometry/type.hh> +#include <dune/geometry/referenceelements.hh> + +#include <dune/localfunctions/lagrange/pqkfactory.hh> + + +#include <dune/grid-glue/merging/contactmerge.hh> +//#include <dune/grid-glue/adapter/gridgluevtkwriter.hh> +//#include <dune/grid-glue/adapter/gridglueamirawriter.hh> + +#include <dune/contact/common/dualbasisadapter.hh> +#include <dune/matrix-vector/addtodiagonal.hh> + +namespace Dune { +namespace Contact { + +template <class field_type, class GridType0, class GridType1> +void DualMortarCoupling<field_type, GridType0, GridType1>::assembleNonmortarLagrangeMatrix() +{ + // Create mapping from the global set of block dofs to the ones on the contact boundary + std::vector<int> globalToLocal; + nonmortarBoundary_.makeGlobalToLocal(globalToLocal); + + // clear matrix + nonmortarLagrangeMatrix_ = Dune::BDMatrix<MatrixBlock>(nonmortarBoundary_.numVertices()); + nonmortarLagrangeMatrix_ = 0; + + const auto& indexSet = grid0_->leafIndexSet(); + + // loop over all faces of the boundary patch + for (const auto& nIt : nonmortarBoundary_) { + + const auto& geometry = nIt.geometry(); + const field_type numCorners = geometry.corners(); + + ctype intElem = geometry.integrationElement(Dune::FieldVector<ctype,dim-1>(0)); + + field_type sfI = (numCorners==3) ? intElem/6.0 : intElem/numCorners; + + // turn scalar element mass matrix into vector-valued one + // and add element mass matrix to the global matrix + + // Get global node ids + const auto& inside = nIt.inside(); + const auto& refElement = Dune::ReferenceElements<ctype, dim>::general(inside.type()); + + // Add localMatrix to nonmortarLagrangeMat + for (int i=0; i<refElement.size(nIt.indexInInside(),1,dim); i++) { + // we can use subEntity here because we add all indices anyway + int v = globalToLocal[indexSet.subIndex(inside,refElement.subEntity(nIt.indexInInside(),1, + i,dim),dim)]; + Dune::MatrixVector::addToDiagonal(nonmortarLagrangeMatrix_[v][v],sfI); + } + + } + +} + +template <class field_type, class GridType0, class GridType1> +void DualMortarCoupling<field_type, GridType0,GridType1>::setup() +{ + typedef typename GridType0::LeafGridView GridView0; + typedef typename GridType1::LeafGridView GridView1; + + typedef Dune::PQkLocalFiniteElementCache<typename GridType1::ctype, field_type, GridType1::dimension,1> FiniteElementCache1; + + // cache for the dual functions on the boundary + using DualCache = Dune::Contact::DualBasisAdapter<GridView0, field_type>; + + using Element0 = typename GridView0::template Codim<0>::Entity; + using Element1 = typename GridView1::template Codim<0>::Entity; + + auto desc0 = [&] (const Element0& e, unsigned int face) { + return nonmortarBoundary_.contains(e,face); + }; + + auto desc1 = [&] (const Element1& e, unsigned int face) { + return mortarBoundary_.contains(e,face); + }; + + GridView0 gridView0 = grid0_->leafGridView(); + GridView1 gridView1 = grid1_->leafGridView(); + + auto extract0 = std::make_shared<Extractor0>(gridView0,desc0); + auto extract1 = std::make_shared<Extractor1>(gridView1,desc1); + + if (!gridGlueBackend_) + gridGlueBackend_ = std::make_shared< Dune::GridGlue::ContactMerge<dimworld, ctype> >(overlap_); + glue_ = std::make_shared<Glue>(extract0, extract1, gridGlueBackend_); + auto& glue = *glue_; + glue.build(); + + //std::cout << glue.size() << " remote intersections found." << std::endl; + //GridGlueAmiraWriter::write<GlueType>(glue,debugPath_); + + // Restrict the hasObstacle fields to the part of the nonmortar boundary + // where we could actually create a contact mapping + LeafBoundaryPatch0 boundaryWithMapping(gridView0); + + const auto& indexSet0 = gridView0.indexSet(); + const auto& indexSet1 = gridView1.indexSet(); + + /////////////////////////////////// + // reducing nonmortar boundary + ///////////////////////////////// + + // Get all fine grid boundary segments that are totally covered by the grid-glue segments + typedef std::pair<int,int> Pair; + std::map<Pair,ctype> coveredArea, fullArea; + + // initialize with area of boundary faces + for (const auto& bIt : nonmortarBoundary_) { + const Pair p(indexSet0.index(bIt.inside()),bIt.indexInInside()); + fullArea[p] = bIt.geometry().volume(); + coveredArea[p] = 0; + } + + // sum up the remote intersection areas to find out which are totally covered + for (const auto& rIs : intersections(glue)) + coveredArea[Pair(indexSet0.index(rIs.inside()),rIs.indexInInside())] += rIs.geometry().volume(); + + // add all fine grid faces that are totally covered by the contact mapping + for (const auto& bIt : nonmortarBoundary_) { + const auto& inside = bIt.inside(); + if(coveredArea[Pair(indexSet0.index(inside),bIt.indexInInside())]/ + fullArea[Pair(indexSet0.index(inside),bIt.indexInInside())] >= coveredArea_) + boundaryWithMapping.addFace(inside, bIt.indexInInside()); + } + + //writeBoundary(boundaryWithMapping,debugPath_ + "relevantNonmortar"); + + + /** \todo replace by all fine grid segments which are totally covered by the RemoteIntersections. */ + //for (const auto& rIs : intersections(glue)) + // boundaryWithMapping.addFace(rIs.inside(),rIs.indexInInside()); + + /*printf("contact mapping could be built for %d of %d boundary segments.\n", + boundaryWithMapping.numFaces(), nonmortarBoundary_.numFaces()); */ + + nonmortarBoundary_ = boundaryWithMapping; + mortarBoundary_.setup(gridView1); + for (const auto& rIs : intersections(glue)) + if (nonmortarBoundary_.contains(rIs.inside(),rIs.indexInInside())) + mortarBoundary_.addFace(rIs.outside(),rIs.indexInOutside()); + + + // Assemble the diagonal matrix coupling the nonmortar side with the lagrange multiplyers there + assembleNonmortarLagrangeMatrix(); + + // The weak obstacle vector + weakObstacle_.resize(nonmortarBoundary_.numVertices()); + weakObstacle_ = 0; + + // /////////////////////////////////////////////////////////// + // Get the occupation structure for the mortar matrix + // /////////////////////////////////////////////////////////// + + /** \todo Also restrict mortar indices and don't use the whole grid level. */ + Dune::MatrixIndexSet mortarIndices(nonmortarBoundary_.numVertices(), grid1_->size(dim)); + + // Create mapping from the global set of block dofs to the ones on the contact boundary + std::vector<int> globalToLocal; + nonmortarBoundary_.makeGlobalToLocal(globalToLocal); + + // loop over all intersections + for (const auto& rIs : intersections(glue)) { + + if (!nonmortarBoundary_.contains(rIs.inside(),rIs.indexInInside())) + continue; + + const auto& inside = rIs.inside(); + const auto& outside = rIs.outside(); + + const auto& domainRefElement = Dune::ReferenceElements<ctype, dim>::general(inside.type()); + const auto& targetRefElement = Dune::ReferenceElements<ctype, dim>::general(outside.type()); + + int nDomainVertices = domainRefElement.size(dim); + int nTargetVertices = targetRefElement.size(dim); + + for (int j=0; j<nDomainVertices; j++) { + + int localDomainIdx = globalToLocal[indexSet0.subIndex(inside,j,dim)]; + + // if the vertex is not contained in the restricted contact boundary then dismiss it + if (localDomainIdx == -1) + continue; + + for (int k=0; k<nTargetVertices; k++) { + int globalTargetIdx = indexSet1.subIndex(outside,k,dim); + if (!mortarBoundary_.containsVertex(globalTargetIdx)) + continue; + + mortarIndices.add(localDomainIdx, globalTargetIdx); + } + } + } + + mortarIndices.exportIdx(mortarLagrangeMatrix_); + + // Clear it + mortarLagrangeMatrix_ = 0; + + + //cache of local bases + FiniteElementCache1 cache1; + + std::unique_ptr<DualCache> dualCache; + dualCache = std::make_unique< Dune::Contact::DualBasisAdapterGlobal<GridView0, field_type> >(); + + std::vector<Dune::FieldVector<ctype,dim> > avNormals; + avNormals = nonmortarBoundary_.getNormals(); + + // loop over all intersections and compute the matrix entries + for (const auto& rIs : intersections(glue)) { + + + const auto& inside = rIs.inside(); + + if (!nonmortarBoundary_.contains(rIs.inside(),rIs.indexInInside())) + continue; + + const auto& outside = rIs.outside(); + + // types of the elements supporting the boundary segments in question + Dune::GeometryType nonmortarEType = inside.type(); + Dune::GeometryType mortarEType = outside.type(); + + const auto& domainRefElement = Dune::ReferenceElements<ctype, dim>::general(nonmortarEType); + + const auto& targetRefElement = Dune::ReferenceElements<ctype, dim>::general(mortarEType); + + int noOfMortarVec = targetRefElement.size(dim); + + Dune::GeometryType nmFaceType = domainRefElement.type(rIs.indexInInside(),1); + Dune::GeometryType mFaceType = targetRefElement.type(rIs.indexInOutside(),1); + + // Select a quadrature rule + // 2 in 2d and for integration over triangles in 3d. If one (or both) of the two faces involved + // are quadrilaterals, then the quad order has to be risen to 3 (4). + int quadOrder = 2 + (!nmFaceType.isSimplex()) + (!mFaceType.isSimplex()); + const auto& quadRule = Dune::QuadratureRules<ctype, dim-1>::rule(rIs.type(), quadOrder); + + //const typename FiniteElementCache0::FiniteElementType& nonmortarFiniteElement = cache0.get(nonmortarEType); + const auto& mortarFiniteElement = cache1.get(mortarEType); + dualCache->bind(inside, rIs.indexInInside()); + + std::vector<Dune::FieldVector<field_type,1> > mortarQuadValues, dualQuadValues; + + const auto& rGeom = rIs.geometry(); + const auto& rGeomOutside = rIs.geometryOutside(); + const auto& rGeomInInside = rIs.geometryInInside(); + const auto& rGeomInOutside = rIs.geometryInOutside(); + + int nNonmortarFaceNodes = domainRefElement.size(rIs.indexInInside(),1,dim); + std::vector<int> nonmortarFaceNodes; + for (int i=0; i<nNonmortarFaceNodes; i++) { + int faceIdxi = domainRefElement.subEntity(rIs.indexInInside(), 1, i, dim); + nonmortarFaceNodes.push_back(faceIdxi); + } + + for (const auto& quadPt : quadRule) { + + // compute integration element of overlap + ctype integrationElement = rGeom.integrationElement(quadPt.position()); + + // quadrature point positions on the reference element + Dune::FieldVector<ctype,dim> nonmortarQuadPos = rGeomInInside.global(quadPt.position()); + Dune::FieldVector<ctype,dim> mortarQuadPos = rGeomInOutside.global(quadPt.position()); + + // The current quadrature point in world coordinates + Dune::FieldVector<field_type,dim> nonmortarQpWorld = rGeom.global(quadPt.position()); + Dune::FieldVector<field_type,dim> mortarQpWorld = rGeomOutside.global(quadPt.position());; + + // the gap direction (normal * gapValue) + Dune::FieldVector<field_type,dim> gapVector = mortarQpWorld - nonmortarQpWorld; + + //evaluate all shapefunctions at the quadrature point + //nonmortarFiniteElement.localBasis().evaluateFunction(nonmortarQuadPos,nonmortarQuadValues); + mortarFiniteElement.localBasis().evaluateFunction(mortarQuadPos,mortarQuadValues); + dualCache->evaluateFunction(nonmortarQuadPos,dualQuadValues); + + // loop over all Lagrange multiplier shape functions + for (int j=0; j<nNonmortarFaceNodes; j++) { + + int globalDomainIdx = indexSet0.subIndex(inside,nonmortarFaceNodes[j],dim); + int rowIdx = globalToLocal[globalDomainIdx]; + + weakObstacle_[rowIdx][0] += integrationElement * quadPt.weight() + * dualQuadValues[nonmortarFaceNodes[j]] * (gapVector*avNormals[globalDomainIdx]); + + // loop over all mortar shape functions + for (int k=0; k<noOfMortarVec; k++) { + + int colIdx = indexSet1.subIndex(outside, k, dim); + if (!mortarBoundary_.containsVertex(colIdx)) + continue; + + // Integrate over the product of two shape functions + field_type mortarEntry = integrationElement* quadPt.weight()* dualQuadValues[nonmortarFaceNodes[j]]* mortarQuadValues[k]; + + Dune::MatrixVector::addToDiagonal(mortarLagrangeMatrix_[rowIdx][colIdx], mortarEntry); + + } + + } + + } + + } + + // /////////////////////////////////////// + // Compute M = D^{-1} \hat{M} + // /////////////////////////////////////// + + Dune::BCRSMatrix<MatrixBlock>& M = mortarLagrangeMatrix_; + Dune::BDMatrix<MatrixBlock>& D = nonmortarLagrangeMatrix_; + + // First compute D^{-1} + D.invert(); + + // Then the matrix product D^{-1} \hat{M} + for (auto rowIt = M.begin(); rowIt != M.end(); ++rowIt) { + const auto rowIndex = rowIt.index(); + for (auto& entry : *rowIt) + entry.leftmultiply(D[rowIndex][rowIndex]); + } + + // weakObstacles in transformed basis = D^{-1}*weakObstacle_ + for(size_t rowIdx=0; rowIdx<weakObstacle_.size(); rowIdx++) + weakObstacle_[rowIdx] *= D[rowIdx][rowIdx][0][0]; + + gridGlueBackend_->clear(); +} + +} /* namespace Contact */ +} /* namespace Dune */ diff --git a/dune/tectonic/spatial-solving/contact/dualmortarcoupling.hh b/dune/tectonic/spatial-solving/contact/dualmortarcoupling.hh new file mode 100644 index 0000000000000000000000000000000000000000..cfd8ed5f25e46034afadc2acdbccd56f386a8e48 --- /dev/null +++ b/dune/tectonic/spatial-solving/contact/dualmortarcoupling.hh @@ -0,0 +1,192 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set ts=8 sw=4 et sts=4: +#ifndef DUNE_CONTACT_ASSEMBLERS_DUAL_MORTAR_COUPLING_HH +#define DUNE_CONTACT_ASSEMBLERS_DUAL_MORTAR_COUPLING_HH + +#include <memory> + +#include <dune/istl/bcrsmatrix.hh> +#include <dune/istl/bdmatrix.hh> +#include <dune/istl/bvector.hh> + +#include <dune/fufem/boundarypatch.hh> +#include <dune/fufem/boundarypatchprolongator.hh> + +#include <dune/grid-glue/gridglue.hh> +#include <dune/grid-glue/extractors/codim1extractor.hh> +#include <dune/grid-glue/merging/merger.hh> + +namespace Dune { +namespace Contact { + +/** \brief Assembles the transfer operator for two-body contact + */ +template<class field_type, class GridType0, class GridType1=GridType0> +class DualMortarCoupling { + + // ///////////////////// + // private types + // ///////////////////// + + enum {dim = GridType0::dimension}; + + enum {dimworld = GridType0::dimensionworld}; + + protected: + typedef Dune::FieldMatrix<field_type, dim, dim> MatrixBlock; + typedef Dune::BCRSMatrix<MatrixBlock> MatrixType; + using ObstacleVector = Dune::BlockVector<Dune::FieldVector<field_type, 1> >; + + typedef typename GridType0::ctype ctype; + + typedef BoundaryPatch<typename GridType0::LeafGridView> LeafBoundaryPatch0; + typedef BoundaryPatch<typename GridType1::LeafGridView> LeafBoundaryPatch1; + + typedef BoundaryPatch<typename GridType0::LevelGridView> LevelBoundaryPatch0; + typedef BoundaryPatch<typename GridType1::LevelGridView> LevelBoundaryPatch1; + + using Extractor0 = Dune::GridGlue::Codim1Extractor<typename GridType0::LeafGridView>; + using Extractor1 = Dune::GridGlue::Codim1Extractor<typename GridType1::LeafGridView>; + +public: + + using Glue = Dune::GridGlue::GridGlue<Extractor0, Extractor1>; + + DualMortarCoupling(field_type overlap = 1e-2, field_type coveredArea = 1.0 - 1e-2) + : overlap_(overlap), gridGlueBackend_(nullptr), coveredArea_(coveredArea) + {} + + DualMortarCoupling(const GridType0& grid0, const GridType1& grid1, + field_type overlap = 1e-2, field_type coveredArea = 1.0 - 1e-2) + : grid0_(&grid0), grid1_(&grid1), overlap_(overlap), + gridGlueBackend_(nullptr), coveredArea_(coveredArea) + {} + + virtual ~DualMortarCoupling() { + /* Nothing. */ + } + + /** \brief Sets up the contact coupling operator on the grid leaf level */ + virtual void setup(); + + /** \brief Assemble nonmortar matrix on the leaf level. */ + void assembleNonmortarLagrangeMatrix(); + + /** \brief Get the dual mortar coupling matrix of the leaf level. */ + virtual const MatrixType& mortarLagrangeMatrix() const { + return mortarLagrangeMatrix_; + } + + /** \brief Get the dual mortar coupling matrix of the leaf level. */ + virtual MatrixType& mortarLagrangeMatrix() { + return mortarLagrangeMatrix_; + } + + /** \brief Get the non-mortar matrix. */ + const BDMatrix<MatrixBlock>& nonmortarLagrangeMatrix() const { + return nonmortarLagrangeMatrix_; + } + + /** \brief Setup leaf nonmortar and mortar patches. */ + virtual void setupContactPatch(const LevelBoundaryPatch0& coarseNonmortar, + const LevelBoundaryPatch1& coarseMortar) { + + BoundaryPatchProlongator<GridType0>::prolong(coarseNonmortar,nonmortarBoundary_); + BoundaryPatchProlongator<GridType1>::prolong(coarseMortar,mortarBoundary_); + } + + /** \brief Return the leafnonmortar boundary. */ + const LeafBoundaryPatch0& nonmortarBoundary() const { + return nonmortarBoundary_; + } + + /** \brief Return the leafmortar boundary. */ + const LeafBoundaryPatch1& mortarBoundary() const { + return mortarBoundary_; + } + + /** \brief Set the non-mortar and mortar grid. */ + void setGrids(const GridType0& grid0, const GridType1& grid1) { + grid0_ = &grid0; + grid1_ = &grid1; + } + + /** \brief Get non-mortar grid. */ + const GridType0* getGrid0() const { + return grid0_; + } + + /** \brief Get mortar grid. */ + const GridType1* getGrid1() const { + return grid1_; + } + + /** \brief Set the percentage of covered area each face must at least have + * by the grid-glue projection. + */ + void setCoveredArea(field_type coveredArea) { + coveredArea_ = coveredArea; + } + + void setOverlap(field_type overlap) { + overlap_ = overlap; + } + + /** \brief Get the GridGlue. */ + std::shared_ptr<Glue> getGlue() { + return glue_; + } + + const ObstacleVector& getWeakObstacle() const { + return weakObstacle_; + } + + // ///////////////////////////////////////////// + // Data members + // ///////////////////////////////////////////// + + /** \brief The two grids involved in the two-body contact problem. */ + const GridType0* grid0_; + const GridType1* grid1_; + + /** \brief For each dof a bit specifying whether the dof carries an obstacle or not. */ + LeafBoundaryPatch0 nonmortarBoundary_; + + /** \brief The mortar boundary. */ + LeafBoundaryPatch1 mortarBoundary_; + + /** \brief The crosspoints of the nonmortar body. */ + std::set<size_t> crosspoints_; + + /** \brief The matrix coupling mortar side and Lagrange multipliers. */ + MatrixType mortarLagrangeMatrix_; + + /** \brief The diagonal matrix coupling nonmortar side and Lagrange multipliers */ + Dune::BDMatrix<MatrixBlock> nonmortarLagrangeMatrix_; + + /** \brief The weak obstacles. */ + ObstacleVector weakObstacle_; + + /** \brief Allow some small penetration of the bodies. */ + field_type overlap_; + + /** \brief A grid-glue merger. If this is not set ContactMerge is used. */ + std::shared_ptr< Dune::GridGlue::Merger<field_type,dim-1,dim-1,dim> > gridGlueBackend_; + + // Path where to write the merged grids and the reduced contact boundaries + //std::string debugPath_; + + std::shared_ptr<Glue> glue_; + + /** \brief Dismiss all faces that are not at least covered by the grid-glue projection for this + * much percentage ranging between one - for total coverage and zero for taking all faces. + */ + field_type coveredArea_; +}; + +} /* namespace Contact */ +} /* namespace Dune */ + +#include "dualmortarcoupling.cc" + +#endif diff --git a/dune/tectonic/spatial-solving/contact/nbodyassembler.cc b/dune/tectonic/spatial-solving/contact/nbodyassembler.cc new file mode 100644 index 0000000000000000000000000000000000000000..2d75582fa4c6cd16ae67ce340f055ccfcb10dafe --- /dev/null +++ b/dune/tectonic/spatial-solving/contact/nbodyassembler.cc @@ -0,0 +1,519 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set ts=8 sw=4 et sts=4: +#include <memory> + +#include <dune/istl/preconditioners.hh> +#include <dune/istl/solvers.hh> +#include <dune/matrix-vector/transpose.hh> +#include <dune/matrix-vector/blockmatrixview.hh> + +#include <dune/fufem/boundarypatchprolongator.hh> +#include <dune/contact/projections/normalprojection.hh> + +namespace Dune { +namespace Contact { + +template <class GridType, class VectorType> +void NBodyAssembler<GridType, VectorType>::assembleTransferOperator() +{ + + // ///////////////////////////////////////////////////////////////// + // Check if contact data is present + // ///////////////////////////////////////////////////////////////// + + for (int i=0; i<nCouplings(); i++) { + + + if (!coupling_[i].obsPatch_) + DUNE_THROW(Dune::Exception, "You have to supply a nonmortar patch for the " << i << "-th coupling!"); + + if (!coupling_[i].mortarPatch_) + DUNE_THROW(Dune::Exception, "You have to supply a mortar patch for the " << i << "-th coupling!"); + } + + // //////////////////////////////////////////////////// + // Set up Mortar element transfer operators + // /////////////////////////////////////////////////// + + //std::cout<<"Setup mortar transfer operators\n"; + + for (size_t i=0; i<contactCoupling_.size(); i++) { + contactCoupling_[i]->setGrids(*grids_[coupling_[i].gridIdx_[0]],*grids_[coupling_[i].gridIdx_[1]]); + contactCoupling_[i]->setupContactPatch(*coupling_[i].patch0(),*coupling_[i].patch1()); + contactCoupling_[i]->gridGlueBackend_ = coupling_[i].backend(); + contactCoupling_[i]->setCoveredArea(coveredArea_); + contactCoupling_[i]->setup(); + } + + // setup Householder reflections + assembleHouseholderReflections(); + + // compute the mortar transformation matrix + computeTransformationMatrix(); +} + +template <class GridType, class VectorType> +void NBodyAssembler<GridType, VectorType>::assembleHouseholderReflections() +{ + // ////////////////////////////////////////////////////////////////// + // Compute local coordinate systems for the dofs with obstacles + // ////////////////////////////////////////////////////////////////// + + std::vector<std::vector<MatrixBlock> > coordinateSystems(nCouplings()); + + for (int i=0; i<nCouplings(); i++) { + + double dist = coupling_[i].obsDistance_; + auto projection = coupling_[i].projection(); + + if (!projection) + DUNE_THROW(Dune::Exception, "You have to supply a contact projection for the " << i << "-th coupling!"); + + std::vector<Dune::FieldVector<field_type,dim> > directions; + + const auto& nonmortarBoundary = contactCoupling_[i]->nonmortarBoundary(); + const auto& mortarBoundary = contactCoupling_[i]->mortarBoundary(); + + projection->project(nonmortarBoundary, mortarBoundary,dist); + projection->getObstacleDirections(directions); + + this->computeLocalCoordinateSystems(nonmortarBoundary,coordinateSystems[i], directions); + } + + // /////////////////////////////////////////////////////////////// + // Combine the coordinate systems for each grid to one long array + // /////////////////////////////////////////////////////////////// + this->localCoordSystems_.resize(numDofs()); + + // initialise with identity + Dune::ScaledIdentityMatrix<field_type,dim> id(1); + for (size_t i=0; i<this->localCoordSystems_.size(); i++) + this->localCoordSystems_[i] = id; + + for (int j=0; j<nCouplings(); j++) { + + int grid0Idx = coupling_[j].gridIdx_[0]; + + // compute offset + int idx = 0; + for (int k=0;k<grid0Idx;k++) + idx += grids_[k]->size(dim); + + const auto& nonmortarBoundary = contactCoupling_[j]->nonmortarBoundary(); + + // if a body is non-mortar more then once, one has to be careful with not overwriting entries + for (std::size_t k=0; k<coordinateSystems[j].size(); k++) + if(nonmortarBoundary.containsVertex(k)) + this->localCoordSystems_[idx+k] = coordinateSystems[j][k]; + } +} + +template <class GridType, class VectorType> +void NBodyAssembler<GridType, VectorType>::assembleObstacle() +{ + + std::vector<std::vector<int> > globalToLocals(nCouplings()); + for (std::size_t i = 0; i < globalToLocals.size(); ++i) + contactCoupling_[i]->nonmortarBoundary().makeGlobalToLocal(globalToLocals[i]); + + /////////////////////////////////////// + // Set the obstacle values + /////////////////////////////////////// + + totalHasObstacle_.resize(numDofs(),false); + + for (int j=0; j<nCouplings(); j++) { + + int grid0Idx = coupling_[j].gridIdx_[0]; + const auto& nonmortarBoundary = contactCoupling_[j]->nonmortarBoundary(); + + // compute offset + int idx = 0; + for (int k=0;k<grid0Idx;k++) + idx += grids_[k]->size(dim); + + for (int k=0; k<grids_[grid0Idx]->size(dim); k++) + if (nonmortarBoundary.containsVertex(k)) + totalHasObstacle_[idx+k] = true; + } + + // ////////////////////////////////////////////////////////////////// + // Set the obstacle distances + // ////////////////////////////////////////////////////////////////// + + // Combine the obstacle values for each grid to one long array + totalObstacles_.resize(numDofs()); + + for (size_t j=0; j<totalObstacles_.size(); j++) + totalObstacles_[j].clear(); + + // Init the obstacle values + for (int i=0; i<nCouplings(); i++) { + + int grid0Idx = coupling_[i].gridIdx_[0]; + + // compute offset + int idx = 0; + for (int k=0;k<grid0Idx;k++) + idx += grids_[k]->size(dim); + + // The grids involved in this coupling + const auto& indexSet = grids_[grid0Idx]->leafIndexSet(); + + // the obstacles are stored using local indices + const std::vector<int>& globalToLocal = globalToLocals[i]; + + // the weak obstacles + const auto& obs = contactCoupling_[i]->weakObstacle_; + + // get strong obstacles, the projection method of the correct level has already been called + //std::vector<field_type> obs; + //coupling_[i].projection()->getObstacles(obs); + + const auto& leafView = grids_[grid0Idx]->leafGridView(); + + for (const auto& v : vertices(leafView)) { + + int vIdx = indexSet.index(v); + if (globalToLocal[vIdx]<0) + continue; + + // Set the obstacle + switch (coupling_[i].type_) { + + case CouplingPairBase::CONTACT: + totalObstacles_[idx+vIdx].upper(0) = obs[globalToLocal[vIdx]]; + break; + + case CouplingPairBase::STICK_SLIP: + totalObstacles_[idx+vIdx].lower(0) = 0; + totalObstacles_[idx+vIdx].upper(0) = 0; + break; + + case CouplingPairBase::GLUE: + for (int j=0; j<dim; j++) { + totalObstacles_[idx+vIdx].lower(j) = 0; + totalObstacles_[idx+vIdx].upper(j) = 0; + } + break; + + case CouplingPairBase::NONE: + break; + default: + DUNE_THROW(Dune::NotImplemented, "Coupling type not handled yet!"); + } + } + } + +} + +template <class GridType, class VectorType> +void NBodyAssembler<GridType, VectorType>:: +assembleJacobian(const std::vector<const MatrixType*>& submat, + MatrixType& totalMatrix) const +{ + // Create a block view of the grand matrix + Dune::MatrixVector::BlockMatrixView<MatrixType> blockView(submat); + + int nRows = blockView.nRows(); + int nCols = blockView.nCols(); + + // Create the index set of B \hat{A} B^T + Dune::MatrixIndexSet indicesBABT(nRows, nCols); + + for (int i=0; i<nGrids(); i++) { + + for (size_t iRow = 0; iRow<submat[i]->N(); iRow++) { + + const RowType& row = (*submat[i])[iRow]; + + // Loop over all columns of the stiffness matrix + ConstColumnIterator j = row.begin(); + ConstColumnIterator jEnd = row.end(); + + for (; j!=jEnd; ++j) { + + ConstColumnIterator k = BT_[blockView.row(i,iRow)].begin(); + ConstColumnIterator kEnd = BT_[blockView.row(i,iRow)].end(); + for (; k!=kEnd; ++k) { + + ConstColumnIterator l = BT_[blockView.col(i,j.index())].begin(); + ConstColumnIterator lEnd = BT_[blockView.col(i,j.index())].end(); + + for (; l!=lEnd; ++l) + indicesBABT.add(k.index(), l.index()); + + } + + } + + } + + } + + // //////////////////////////////////////////////////////////////////// + // Multiply transformation matrix to the global stiffness matrix + // //////////////////////////////////////////////////////////////////// + + indicesBABT.exportIdx(totalMatrix); + totalMatrix = 0; + + for (int i=0; i<nGrids(); i++) { + + for (size_t iRow = 0; iRow<submat[i]->N(); iRow++) { + + const RowType& row = (*submat[i])[iRow]; + + // Loop over all columns of the stiffness matrix + ConstColumnIterator j = row.begin(); + ConstColumnIterator jEnd = row.end(); + + for (; j!=jEnd; ++j) { + + ConstColumnIterator k = BT_[blockView.row(i,iRow)].begin(); + ConstColumnIterator kEnd = BT_[blockView.row(i,iRow)].end(); + + for (; k!=kEnd; ++k) { + + ConstColumnIterator l = BT_[blockView.col(i,j.index())].begin(); + ConstColumnIterator lEnd = BT_[blockView.col(i,j.index())].end(); + + for (; l!=lEnd; ++l) { + + //BABT[k][l] += BT[i][k] * mat_[i][j] * BT[j][l]; + MatrixBlock m_ij = *j; + + MatrixBlock BT_ik_trans; + Dune::MatrixVector::transpose(*k, BT_ik_trans); + + m_ij.leftmultiply(BT_ik_trans); + m_ij.rightmultiply(*l); + + totalMatrix[k.index()][l.index()] += m_ij; + + } + + } + + } + + } + + } +} + +template <class GridType, class VectorType> +template <class VectorTypeContainer> +void NBodyAssembler<GridType, VectorType>:: +assembleRightHandSide(const VectorTypeContainer& rhs, + VectorType& totalRhs) const +{ + // Concatenate the two rhs vectors to a large one + VectorType untransformedTotalRhs(BT_.M()); + + int idx = 0; + + for (size_t i=0; i<rhs.size(); i++) { + + for (size_t j=0; j<rhs[i].size(); j++) + untransformedTotalRhs[idx++] = rhs[i][j]; + + } + + if ((int) BT_.M() != idx) + DUNE_THROW(Dune::Exception, "assembleRightHandSide: vector size and matrix size don't match!"); + + // Transform the basis of the ansatz space + totalRhs.resize(untransformedTotalRhs.size()); + totalRhs = 0; + BT_.umtv(untransformedTotalRhs, totalRhs); +} + + +template <class GridType, class VectorType> +template <class VectorTypeContainer> +void NBodyAssembler<GridType, VectorType>:: +postprocess(const VectorType& totalX, VectorTypeContainer& x) const +{ + // /////////////////////////////////////////////////////// + // Transform the solution vector to the nodal basis + // /////////////////////////////////////////////////////// + + VectorType nodalBasisTotalX(totalX.size()); + BT_.mv(totalX, nodalBasisTotalX); + + + // /////////////////////////////////////////////////////// + // Split the total solution vector into the parts + // corresponding to the grids. + // /////////////////////////////////////////////////////// + + int idx = 0; + for (int i=0; i<nGrids(); i++) { + + x[i].resize(grids_[i]->size(dim)); + + for (size_t j=0; j<x[i].size(); j++, idx++) + x[i][j] = nodalBasisTotalX[idx]; + } +} + +template <class GridType, class VectorType> +template <class VectorTypeContainer> +void NBodyAssembler<GridType, VectorType>:: +concatenateVectors(const VectorTypeContainer& parts, VectorType& whole) +{ + int totalSize = 0; + for (size_t i=0; i<parts.size(); i++) + totalSize += parts[i].size(); + + whole.resize(totalSize); + + int idx = 0; + for (size_t i=0; i<parts.size(); i++) + for (size_t j=0; j<parts[i].size(); j++) + whole[idx++] = parts[i][j]; + +} + +// //////////////////////////////////////////////////////////////////////////// +// Turn the initial solutions from the nodal basis to the transformed basis +// //////////////////////////////////////////////////////////////////////////// +template <class GridType, class VectorType> +template <class VectorTypeContainer> +void NBodyAssembler<GridType, VectorType>:: +nodalToTransformed(const VectorTypeContainer& x, + VectorType& transformedX) const +{ + VectorType canonicalTotalX; + + concatenateVectors(x, canonicalTotalX); + + // Make small cg solver + Dune::MatrixAdapter<MatrixType,VectorType,VectorType> op(getTransformationMatrix()); + + // A preconditioner + Dune::SeqILU<MatrixType,VectorType,VectorType> ilu0(getTransformationMatrix(),1.0); + + // A cg solver for nonsymmetric matrices + Dune::BiCGSTABSolver<VectorType> bicgstab(op,ilu0,1E-4,100,0); + + // Object storing some statistics about the solving process + Dune::InverseOperatorResult statistics; + + // Solve! + transformedX = canonicalTotalX; // seems to be a good initial value + bicgstab.apply(transformedX, canonicalTotalX, statistics); +} + +template <class GridType, class VectorType> +void NBodyAssembler<GridType, VectorType>:: +computeTransformationMatrix() +{ + //std::cout<<"Setup transformation matrix..."; + // compute offsets for the different grids + std::vector<int> offsets(grids_.size()); + offsets[0] = 0; + + // P1 elements are hard-wired here + size_t k; + for (k=0; k<grids_.size()-1; k++) + offsets[k+1] = offsets[k] + grids_[k]->size(dim); + + int nRows = offsets[k] + grids_[k]->size(dim); + int nCols = nRows; + + // ///////////////////////////////////////////////////////////////// + // First create the index structure + // ///////////////////////////////////////////////////////////////// + + Dune::MatrixIndexSet indicesBT(nRows, nCols); + + // BT_ is the identity plus some off-diagonal elements + for (size_t i=0; i<indicesBT.rows(); i++) + indicesBT.add(i,i); + + std::vector<std::vector<int> > nonmortarToGlobal(nCouplings()); + // Enter all the off-diagonal entries + for (int i=0; i<nCouplings(); i++) { + + const auto& nonmortarBoundary = contactCoupling_[i]->nonmortarBoundary(); + // If the contact mapping could not be built at all then skip this coupling + if (nonmortarBoundary.numVertices() == 0) + continue; + + // The grids involved in this coupling + int grid0Idx = coupling_[i].gridIdx_[0]; + int grid1Idx = coupling_[i].gridIdx_[1]; + + // The mapping from nonmortar vertex indices to grid indices + nonmortarToGlobal[i].resize(nonmortarBoundary.numVertices()); + int idx = 0; + for (int j=0; j<grids_[grid0Idx]->size(dim); j++) + if (nonmortarBoundary.containsVertex(j)) + nonmortarToGlobal[i][idx++] = j; + + // the off-diagonal part + const MatrixType& M = contactCoupling_[i]->mortarLagrangeMatrix(); + for (size_t j=0; j<M.N(); j++) { + + const RowType& row = M[j]; + + ConstColumnIterator cIt = row.begin(); + ConstColumnIterator cEndIt = row.end(); + + for (; cIt!=cEndIt; ++cIt) + indicesBT.add(offsets[grid0Idx] + nonmortarToGlobal[i][j], + offsets[grid1Idx] + cIt.index()); + + } + + } + + // //////////////////////////////////////////////////////////// + // Enter the values of the different couplings + // //////////////////////////////////////////////////////////// + + indicesBT.exportIdx(BT_); + BT_ = 0; + + // Enter identity part + for (int i=0; i<nRows; i++) + for (int j=0; j<dim; j++) + BT_[i][i][j][j] = 1; + + for (int i=0; i<nCouplings(); i++) { + + const auto& nonmortarBoundary = contactCoupling_[i]->nonmortarBoundary(); + if (nonmortarBoundary.numVertices() == 0) + continue; + + // The grids involved in this coupling + int grid0Idx = coupling_[i].gridIdx_[0]; + int grid1Idx = coupling_[i].gridIdx_[1]; + + // the off-diagonal part + const MatrixType& M = contactCoupling_[i]->mortarLagrangeMatrix(); + for (size_t j=0; j<M.N(); j++) { + + const RowType& row = M[j]; + + ConstColumnIterator cIt = row.begin(); + ConstColumnIterator cEndIt = row.end(); + + for (; cIt!=cEndIt; ++cIt) + BT_[offsets[grid0Idx] + nonmortarToGlobal[i][j]][offsets[grid1Idx] +cIt.index()] = *cIt; + + } + + int offset = offsets[grid0Idx]; + + // modify non-mortar dofs and rotate them in normal and tangential coordinates + for (int k=0; k<grids_[grid0Idx]->size(dim); k++) + if(nonmortarBoundary.containsVertex(k)) + BT_[offset + k][offset+k] =this->localCoordSystems_[offset + k]; + } +} + +} /* namespace Contact */ +} /* namespace Dune */ diff --git a/dune/tectonic/spatial-solving/contact/nbodyassembler.hh b/dune/tectonic/spatial-solving/contact/nbodyassembler.hh new file mode 100644 index 0000000000000000000000000000000000000000..55a5d6335df2de661260620094798ff8b671a973 --- /dev/null +++ b/dune/tectonic/spatial-solving/contact/nbodyassembler.hh @@ -0,0 +1,206 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set ts=8 sw=4 et sts=4: +#ifndef DUNE_CONTACT_ASSEMBLERS_N_BODY_MORTAR_ASSEMBLER_HH +#define DUNE_CONTACT_ASSEMBLERS_N_BODY_MORTAR_ASSEMBLER_HH + +#include <dune/common/bitsetvector.hh> +#include <dune/common/promotiontraits.hh> +#include <dune/common/fmatrix.hh> +#include <dune/istl/bcrsmatrix.hh> +#include <dune/solvers/common/boxconstraint.hh> +#include <dune/fufem/boundarypatch.hh> +#include <dune/contact/assemblers/dualmortarcouplinghierarchy.hh> +#include <dune/contact/assemblers/dualmortarcoupling.hh> +#include <dune/contact/assemblers/contactassembler.hh> +#include <dune/contact/common/couplingpair.hh> +#include <dune/contact/projections/contactprojection.hh> + +#ifdef HAVE_DUNE_GRID_GLUE +#include <dune/grid-glue/merging/merger.hh> +#endif + +namespace Dune { +namespace Contact { + +/** \brief Assembler for mortar discretized contact problems with arbitrary number of bodies. + * + * \tparam GridType Type of the corresponding grids. + * \tparam VectorType The vector type for the displacements. + */ +template <class GridType, class VectorType> +class NBodyAssembler : public ContactAssembler<GridType::dimension, typename VectorType::field_type> +{ +protected: + enum {dim = GridType::dimension}; + + using field_type = typename PromotionTraits<typename VectorType::field_type, + typename GridType::ctype>::PromotedType; + + using CouplingType = DualMortarCoupling<field_type, GridType>; + using HierarchyCouplingType = DualMortarCouplingHierarchy<field_type, GridType>; + + using MatrixBlock = Dune::FieldMatrix<field_type, dim, dim>; + using MatrixType = Dune::BCRSMatrix<MatrixBlock>; + + using RowType = typename MatrixType::row_type; + using ConstColumnIterator = typename RowType::ConstIterator; + + using LeafBoundaryPatch = BoundaryPatch<typename GridType::LeafGridView>; + +public: + /** \brief Construct assembler from number of couplings and grids. + * + * \param nGrids The number of involved bodies. + * \param nCouplings The number of couplings. + * \param hierarchyCoupling This boolean determines if the whole hierarchy of mortar matrices should be setup + */ + NBodyAssembler(int nGrids, int nCouplings, bool hierarchyCoupling=false, field_type coveredArea = 0.99) : + coveredArea_(coveredArea) + { + grids_.resize(nGrids); + + coupling_.resize(nCouplings); + contactCoupling_.resize(nCouplings); + // initialise the couplings with DualMortarCoupling + for (int i=0; i<nCouplings; i++) + if (hierarchyCoupling) + contactCoupling_[i] = std::make_shared<HierarchyCouplingType>(); + else + contactCoupling_[i] = std::make_shared<CouplingType>(); + } + + /** \brief Get the number of couplings.*/ + int nCouplings() const {return coupling_.size();} + + /** \brief Get the number of involved bodies.*/ + int nGrids() const {return grids_.size();} + + /** \brief Get the total number of degrees of freedom of the leaf grids. */ + int numDofs() const { + int n=0; + for (int i=0; i<nGrids(); i++) + n += grids_[i]->size(dim); + return n; + } + + /** \brief Setup the patches for the contact boundary and compute the obstacles. */ + void assembleObstacle(); + + /** \brief Assemble the mortar matrices and compute the basis transformation.*/ + void assembleTransferOperator(); + + /** \brief Turn initial solutions from nodal basis to the transformed basis. + * i.e. transformedX = O*B^{-T}nodalX + * \param x Initial solution vector containing nodal coefficients. + * \param transformedX Coefficient vector for all bodies in mortar transformed coordinates. + */ + template <class VectorTypeContainer> + void nodalToTransformed(const VectorTypeContainer& x, + VectorType& transformedX) const; + + /** \brief Compute stiffness matrices in mortar transformed coordinates. + * i.e. transformedA = O*B*nodalA*B^T*O^T + * \param submat Stiffness matrices w.r.t the nodal finite element basis. + * \param totalMatrix Reference to mortar transformed stiffness matrix for all bodies. + */ + void assembleJacobian(const std::vector<const MatrixType*>& submat, + MatrixType& totalMatrix) const; + + /** \brief Compute the right hand side in mortar transformed coordinates. + * i.e. totalRhs = O*B*nodalRhs + * \param rhs Right hand side coefficients w.r.t. the nodal finite element basis. + * \param totalRhs Reference to coefficient vector w.r.t. the transformed basis. + */ + template <class VectorTypeContainer> + void assembleRightHandSide(const VectorTypeContainer& rhs, + VectorType& totalRhs) const; + + /** \brief Transform a vector from local coordinates to canonical coordinates. + * + * \param totalX Coefficient vector of the mortar transformed basis. + * \param x Reference to target vector for the standard nodal coefficients of each body. + */ + template <class VectorTypeContainer> + void postprocess(const VectorType& totalX, VectorTypeContainer& x) const; + + /** \brief Concatenate a family of vectors . + * + * \param parts A vector of vectors. + * \param whole The vector to contain the concatenated family + */ + template <class VectorTypeContainer> + static void concatenateVectors(const VectorTypeContainer& parts, VectorType& whole); + + /** \brief Get the contact couplings. */ + const auto& getContactCouplings() const { + return contactCoupling_; + } + + /** \brief Get the contact couplings. */ + auto& getContactCouplings() { + return contactCoupling_; + } + + /** \brief Set the contact couplings. */ + void setContactCouplings(std::vector<std::shared_ptr<CouplingType> >& contactCouplings) { + + contactCoupling_.resize(contactCouplings.size()); + for (size_t i=0; i<contactCouplings.size(); i++) + contactCoupling_[i] = contactCouplings[i]; + } + + /** \brief Get the transposed of the mortar transformation matrix B^T.*/ + const MatrixType& getTransformationMatrix() const {return BT_;} + + /** \brief Get the grids. */ + const std::vector<const GridType*> getGrids() const { return grids_; } + + /** \brief Set the grids. */ + void setGrids(const std::vector<const GridType*>& grids) {grids_ = grids;} + + /** \brief Set grids. */ + void setCoupling(const CouplingPair<GridType, GridType, field_type>& coupling, size_t i) { + coupling_[i] = coupling; + } + + /** \brief Get reference to i'th coupling. */ + const auto& getCoupling(size_t i) const {return coupling_[i];} + +protected: + /** \brief Compute the transposed mortar transformation matrix. */ + void computeTransformationMatrix(); + + /** \brief Setup the Householder reflections. */ + void assembleHouseholderReflections(); + +public: + /** \brief Bitvector for all bodies whose flags are set if a dof has an obstacle.*/ + Dune::BitSetVector<dim> totalHasObstacle_; + + /** \brief Obstacles for all bodies on the leafview.*/ + std::vector<BoxConstraint<field_type,dim> > totalObstacles_; + + /** \brief The mortar couplings between grid pairs */ + std::vector<std::shared_ptr<CouplingType> > contactCoupling_; + + /** \brief The coupling pairs. */ + std::vector<CouplingPair<GridType,GridType,field_type> > coupling_; + + /** \brief Vector containing the involved grids. */ + std::vector<const GridType*> grids_; + + /** \brief The transposed of the mortar transformation matrix. */ + MatrixType BT_; + + /** \brief Dismiss all faces that are not at least covered by the grid-glue projection for this + * much percentage ranging between one - for total coverage and zero for taking all faces. + */ + field_type coveredArea_; +}; + +} /* namespace Contact */ +} /* namespace Dune */ + +#include "nbodyassembler.cc" + +#endif diff --git a/dune/tectonic/spatial-solving/preconditioners/nbodycontacttransfer.cc b/dune/tectonic/spatial-solving/contact/nbodycontacttransfer.cc similarity index 100% rename from dune/tectonic/spatial-solving/preconditioners/nbodycontacttransfer.cc rename to dune/tectonic/spatial-solving/contact/nbodycontacttransfer.cc diff --git a/dune/tectonic/spatial-solving/preconditioners/nbodycontacttransfer.hh b/dune/tectonic/spatial-solving/contact/nbodycontacttransfer.hh similarity index 100% rename from dune/tectonic/spatial-solving/preconditioners/nbodycontacttransfer.hh rename to dune/tectonic/spatial-solving/contact/nbodycontacttransfer.hh diff --git a/dune/tectonic/spatial-solving/preconditioners/CMakeLists.txt b/dune/tectonic/spatial-solving/preconditioners/CMakeLists.txt index bcff7b9ff1be92af3c892c7ac15e61a368a73c23..b32277edecfc293e5d53c4f9f0eccf7ee0f3ffc6 100644 --- a/dune/tectonic/spatial-solving/preconditioners/CMakeLists.txt +++ b/dune/tectonic/spatial-solving/preconditioners/CMakeLists.txt @@ -3,8 +3,6 @@ add_custom_target(tectonic_dune_spatial-solving_preconditioners SOURCES levelpatchpreconditioner.hh patchproblem.hh multilevelpatchpreconditioner.hh - nbodycontacttransfer.hh - nbodycontacttransfer.cc supportpatchfactory.hh ) @@ -14,6 +12,5 @@ install(FILES levelpatchpreconditioner.hh patchproblem.hh multilevelpatchpreconditioner.hh - nbodycontacttransfer.hh supportpatchfactory.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/tectonic) diff --git a/dune/tectonic/spatial-solving/tnnmg/functional.hh b/dune/tectonic/spatial-solving/tnnmg/functional.hh index eff8e22d34a8572122b67b503ed573463117d2d5..3e9eab1a55bce71384bd547761cc2501cd30a74c 100755 --- a/dune/tectonic/spatial-solving/tnnmg/functional.hh +++ b/dune/tectonic/spatial-solving/tnnmg/functional.hh @@ -422,10 +422,28 @@ class Functional : public Dune::TNNMG::BoxConstrainedQuadraticFunctional<M, V, L maxEigenvalues_(this->linearPart().size()) { for (size_t i=0; i<this->quadraticPart().N(); ++i) { + const auto& Aii = this->quadraticPart()[i][i]; + + // due to numerical imprecision, Aii might not be symmetric leading to complex eigenvalues + // consider Toeplitz decomposition of Aii and take only symmetric part + + auto symBlock = Aii; + + /*for (size_t j=0; j<symBlock.N(); j++) { + for (size_t k=0; k<symBlock.M(); k++) { + if (symBlock[j][k]/symBlock[j][j] < 1e-14) + symBlock[j][k] = 0; + } + }*/ + + try { typename Vector::block_type eigenvalues; - Dune::FMatrixHelp::eigenValues(this->quadraticPart()[i][i], eigenvalues); + Dune::FMatrixHelp::eigenValues(symBlock, eigenvalues); maxEigenvalues_[i] = *std::max_element(std::begin(eigenvalues), std::end(eigenvalues)); + } catch (Dune::Exception &e) { + std::cout << "complex eig in dof: " << i << std::endl; + } } } diff --git a/src/foam/foam-2D.cfg b/src/foam/foam-2D.cfg index 6efcb38b3aa3637d41b65307d5c97f2cb2136428..c22b8a7224393ab58613ba0f4a075e142f412bfc 100644 --- a/src/foam/foam-2D.cfg +++ b/src/foam/foam-2D.cfg @@ -1,6 +1,9 @@ # -*- mode:conf -*- -[boundary.friction] -smallestDiameter = 0.005 # 2e-3 [m] +[body0] +smallestDiameter = 0.01 # 2e-3 [m] + +[body1] +smallestDiameter = 0.01 # 2e-3 [m] [timeSteps] refinementTolerance = 1e-5 # 1e-5 diff --git a/src/foam/foam.cc b/src/foam/foam.cc index fe95424f3676d3797d2a0b3549a57870d8216edc..ce0f8d3a9ab4f1c188ea71afbb1b4dc9853d3d48 100644 --- a/src/foam/foam.cc +++ b/src/foam/foam.cc @@ -330,18 +330,18 @@ int main(int argc, char *argv[]) { BoundaryNodes dirichletNodes; contactNetwork.boundaryNodes("dirichlet", dirichletNodes); - /*for (size_t i=0; i<dirichletNodes.size(); i++) { + for (size_t i=0; i<dirichletNodes.size(); i++) { for (size_t j=0; j<dirichletNodes[i].size(); j++) { print(*dirichletNodes[i][j], "dirichletNodes_body_" + std::to_string(i) + "_boundary_" + std::to_string(j)); } - }*/ + } std::vector<const Dune::BitSetVector<1>*> frictionNodes; contactNetwork.frictionNodes(frictionNodes); - /*for (size_t i=0; i<frictionNodes.size(); i++) { + for (size_t i=0; i<frictionNodes.size(); i++) { print(*frictionNodes[i], "frictionNodes_body_" + std::to_string(i)); - }*/ + } Updaters current( initRateUpdater( diff --git a/src/foam/foam.cfg b/src/foam/foam.cfg index 7c8016105e83a5a03566458f4be8128cd5a12a4a..ee78e2fc57ca31b4e0f2eca7f668fb5098664e29 100644 --- a/src/foam/foam.cfg +++ b/src/foam/foam.cfg @@ -2,34 +2,34 @@ gravity = 9.81 # [m/s^2] [body0] -length = 0.4 # [m] +length = 0.04 # [m] height = 0.04 # [m] depth = 0.04 # [m] -bulkModulus = 2190 # [Pa] -poissonRatio = 0.11 # [1] +bulkModulus = 0.5e5 # [Pa] #2190 +poissonRatio = 0.30 # [1] #0.11 [body0.elastic] -density = 750 # [kg/m^3] -shearViscosity = 0 # [Pas] -bulkViscosity = 0 # [Pas] +density = 900 # [kg/m^3] #750 +shearViscosity = 1e3 # [Pas] +bulkViscosity = 1e3 # [Pas] [body0.viscoelastic] -density = 750 # [kg/m^3] -shearViscosity = 0 # [Pas] -bulkViscosity = 0 # [Pas] +density = 1000 # [kg/m^3] +shearViscosity = 1e4 # [Pas] +bulkViscosity = 1e4 # [Pas] [body1] length = 0.04 # [m] height = 0.04 # [m] depth = 0.04 # [m] -bulkModulus = 2190 # [Pa] -poissonRatio = 0.11 # [1] +bulkModulus = 0.5e5 # [Pa] +poissonRatio = 0.30 # [1] [body1.elastic] -density = 750 # [kg/m^3] -shearViscosity = 0 # [Pas] -bulkViscosity = 0 # [Pas] +density = 900 # [kg/m^3] +shearViscosity = 1e3 # [Pas] +bulkViscosity = 1e3 # [Pas] [body1.viscoelastic] -density = 750 # [kg/m^3] -shearViscosity = 0 # [Pas] -bulkViscosity = 0 # [Pas] +density = 1000 # [kg/m^3] +shearViscosity = 1e4 # [Pas] +bulkViscosity = 1e4 # [Pas] [boundary.friction] C = 10 # [Pa] @@ -40,10 +40,10 @@ initialAlpha = 0 # [ ] stateModel = AgeingLaw frictionModel = Truncated #Regularised [boundary.friction.weakening] -a = 0.025 # [ ] -b = 0.005 # [ ] +a = 0.002 # [ ] +b = 0.017 # [ ] [boundary.friction.strengthening] -a = 0.025 # [ ] +a = 0.020 # [ ] b = 0.005 # [ ] [boundary.neumann]