// // Software License for AMDiS // // Copyright (c) 2010 Dresden University of Technology // All rights reserved. // Authors: Simon Vey, Thomas Witkowski et al. // // This file is part of AMDiS // // See also license.opensource.txt in the distribution. #include #include #include #include #include #include #include #include "parallel/MeshDistributor.h" #include "parallel/MeshManipulation.h" #include "parallel/ParallelDebug.h" #include "parallel/StdMpi.h" #include "parallel/MeshPartitioner.h" #include "parallel/ParMetisPartitioner.h" #include "parallel/ZoltanPartitioner.h" #include "parallel/SimplePartitioner.h" #include "parallel/CheckerPartitioner.h" #include "parallel/MpiHelper.h" #include "parallel/DofComm.h" #include "io/ElementFileWriter.h" #include "io/MacroInfo.h" #include "io/MacroWriter.h" #include "io/VtkWriter.h" #include "Mesh.h" #include "Traverse.h" #include "ElInfo.h" #include "Element.h" #include "MacroElement.h" #include "DOFMatrix.h" #include "DOFVector.h" #include "SystemVector.h" #include "ElementDofIterator.h" #include "ProblemStatBase.h" #include "StandardProblemIteration.h" #include "VertexVector.h" #include "MeshStructure.h" #include "ProblemStat.h" #include "ProblemInstat.h" #include "RefinementManager3d.h" #include "Debug.h" namespace AMDiS { using boost::lexical_cast; using namespace boost::filesystem; using namespace std; MeshDistributor* MeshDistributor::globalMeshDistributor = NULL; const Flag MeshDistributor::BOUNDARY_SUBOBJ_SORTED = 0X01L; const Flag MeshDistributor::BOUNDARY_FILL_INFO_SEND_DOFS = 0X02L; const Flag MeshDistributor::BOUNDARY_FILL_INFO_RECV_DOFS = 0X04L; inline bool cmpDofsByValue(const DegreeOfFreedom* dof1, const DegreeOfFreedom* dof2) { return (*dof1 < *dof2); } MeshDistributor::MeshDistributor() : problemStat(0), initialized(false), name("parallel"), mesh(NULL), refineManager(NULL), partitioner(NULL), deserialized(false), writeSerializationFile(false), repartitioningAllowed(false), repartitionIthChange(20), nMeshChangesAfterLastRepartitioning(0), repartitioningCounter(0), repartitioningFailed(0), debugOutputDir(""), lastMeshChangeIndex(0), createBoundaryDofFlag(0), boundaryDofInfo(1), meshAdaptivity(true), hasPeriodicBoundary(false), printTimings(false) { FUNCNAME("MeshDistributor::ParalleDomainBase()"); mpiComm = MPI::COMM_WORLD; mpiRank = mpiComm.Get_rank(); mpiSize = mpiComm.Get_size(); Parameters::get(name + "->repartitioning", repartitioningAllowed); Parameters::get(name + "->debug output dir", debugOutputDir); Parameters::get(name + "->repartition ith change", repartitionIthChange); Parameters::get(name + "->mesh adaptivity", meshAdaptivity); string partStr = "parmetis"; Parameters::get(name + "->partitioner", partStr); if (partStr == "parmetis") partitioner = new ParMetisPartitioner(&mpiComm); if (partStr == "zoltan") { #ifdef HAVE_ZOLTAN partitioner = new ZoltanPartitioner(&mpiComm); #else ERROR_EXIT("AMDiS was compiled without Zoltan support. Therefore you cannot make use of it!\n"); #endif } if (partStr == "checker") partitioner = new CheckerPartitioner(&mpiComm); if (partStr == "simple") partitioner = new SimplePartitioner(&mpiComm); int tmp = 0; Parameters::get(name + "->box partitioning", tmp); partitioner->setBoxPartitioning(static_cast(tmp)); Parameters::get(name + "->print timings", printTimings); TEST_EXIT(partitioner)("Could not create partitioner \"%s\"!\n", partStr.c_str()); } MeshDistributor::~MeshDistributor() { if (partitioner) delete partitioner; } void MeshDistributor::initParallelization() { FUNCNAME("MeshDistributor::initParallelization()"); if (initialized) return; TEST_EXIT(mpiSize > 1) ("Parallelization does not work with only one process!\n"); TEST_EXIT(feSpaces.size() > 0) ("No FE space has been defined for the mesh distributor!\n"); TEST_EXIT(mesh)("No mesh has been defined for the mesh distributor!\n"); // === Sort FE spaces with respect to the degree of the basis === // === functions. Use stuiped bubble sort for this. === bool doNext = false; do { doNext = false; for (unsigned int i = 0; i < feSpaces.size() - 1; i++) { if (feSpaces[i]->getBasisFcts()->getDegree() > feSpaces[i + 1]->getBasisFcts()->getDegree()) { const FiniteElemSpace *tmp = feSpaces[i + 1]; feSpaces[i + 1] = feSpaces[i]; feSpaces[i] = tmp; doNext = true; } } } while (doNext); elObjDb.setFeSpace(feSpaces[0]); // If required, create hierarchical mesh level structure. createMeshLevelStructure(); // If the problem has been already read from a file, we need only to set // isRankDofs to all matrices and rhs vector and to remove periodic // boundary conditions (if there are some). if (deserialized) { createMeshLevelStructure(); updateMacroElementInfo(); removePeriodicBoundaryConditions(); elObjDb.createMacroElementInfo(allMacroElements); updateLocalGlobalNumbering(); elObjDb.setData(partitionMap, levelData); #if (DEBUG != 0) TEST_EXIT_DBG(dofMaps.size())("No DOF mapping defined!\n"); ParallelDebug::writeDebugFile(feSpaces[feSpaces.size() - 1], *(dofMaps[0]), debugOutputDir + "mpi-dbg", "dat"); #endif initialized = true; return; } // Test, if the mesh is the macro mesh only! Paritioning of the mesh is // supported only for macro meshes, so it will not work yet if the mesh is // already refined in some way. testForMacroMesh(); // For later mesh repartitioning, we need to store some information about // the macro mesh. createMacroElementInfo(); // create an initial partitioning of the mesh partitioner->createInitialPartitioning(); // set the element weights, which are 1 at the very first begin setInitialElementWeights(); // and now partition the mesh bool partitioningSucceed = partitioner->partition(elemWeights, INITIAL); TEST_EXIT(partitioningSucceed)("Initial partitioning does not work!\n"); partitioner->createPartitionMap(partitionMap); #if (DEBUG != 0) debug::ElementIdxToDofs elMap; debug::createSortedDofs(mesh, elMap); #endif if (mpiRank == 0) { #if (DEBUG != 0) int writePartMesh = 1; #else int writePartMesh = 0; #endif Parameters::get("dbg->write part mesh", writePartMesh); if (writePartMesh > 0) { debug::writeElementIndexMesh(mesh, debugOutputDir + "elementIndex"); ParallelDebug::writePartitioning(*this, debugOutputDir + "part.vtu"); } } // Create interior boundary information. createInteriorBoundary(true); // === Remove neighbourhood relations due to periodic bounday conditions. === for (deque::iterator it = mesh->firstMacroElement(); it != mesh->endOfMacroElements(); ++it) { for (int i = 0; i < mesh->getGeo(NEIGH); i++) { if ((*it)->getNeighbour(i) && mesh->isPeriodicAssociation((*it)->getBoundary(i))) { int neighIndex = (*it)->getNeighbour(i)->getIndex(); (*it)->getNeighbour(i)->setNeighbour((*it)->getOppVertex(i), NULL); (*it)->setNeighbour(i, NULL); (*it)->setBoundary(i, 0); macroElementNeighbours[(*it)->getIndex()][i] = -1; macroElementNeighbours[neighIndex][(*it)->getOppVertex(i)] = -1; } } } for (vector::iterator it = allMacroElements.begin(); it != allMacroElements.end(); ++it) { for (int i = 0; i < mesh->getGeo(NEIGH); i++) { if ((*it)->getNeighbour(i) && mesh->isPeriodicAssociation((*it)->getBoundary(i))) { int neighIndex = (*it)->getNeighbour(i)->getIndex(); (*it)->getNeighbour(i)->setNeighbour((*it)->getOppVertex(i), NULL); (*it)->setNeighbour(i, NULL); (*it)->setBoundary(i, 0); macroElementNeighbours[(*it)->getIndex()][i] = -1; macroElementNeighbours[neighIndex][(*it)->getOppVertex(i)] = -1; } } } // === Remove all macro elements that are not part of the rank partition. === removeMacroElements(); // === Create new global and local DOF numbering. === // We have to remove the VertexVectors, which contain periodic assoiciations, // because they are not valid anymore after some macro elements have been // removed and the corresponding DOFs were deleted. for (map::iterator it = mesh->getPeriodicAssociations().begin(); it != mesh->getPeriodicAssociations().end(); ++it) const_cast(mesh->getDofAdmin(0)).removeDOFContainer(dynamic_cast(it->second)); updateLocalGlobalNumbering(); // === In 3D we have to make some test, if the resulting mesh is valid. === // === If it is not valid, there is no possiblity yet to fix this === // === problem, just exit with an error message. === check3dValidMesh(); // === If in debug mode, make some tests. === #if (DEBUG != 0) MSG("AMDiS runs in debug mode, so make some test ...\n"); ParallelDebug::testAllElements(*this); debug::testSortedDofs(mesh, elMap); ParallelDebug::testInteriorBoundary(*this); ParallelDebug::followBoundary(*this); debug::writeMesh(feSpaces[0], -1, debugOutputDir + "macro_mesh"); MSG("Debug mode tests finished!\n"); #endif // Remove periodic boundary conditions in sequential problem definition. removePeriodicBoundaryConditions(); #if (DEBUG != 0) ParallelDebug::testPeriodicBoundary(*this); #endif // === Global refinements. === int globalRefinement = 0; Parameters::get(mesh->getName() + "->global refinements", globalRefinement); if (globalRefinement > 0) { refineManager->globalRefine(mesh, globalRefinement); updateLocalGlobalNumbering(); #if (DEBUG != 0) ParallelDebug::testPeriodicBoundary(*this); #endif } // And delete some data, we there is no mesh adaptivty. if (!meshAdaptivity) elObjDb.clear(); initialized = true; } void MeshDistributor::addProblemStat(ProblemStatSeq *probStat) { FUNCNAME("MeshDistributor::addProblemStat()"); TEST_EXIT_DBG(probStat->getFeSpaces().size()) ("No FE spaces in stationary problem!\n"); // === Add all FE spaces from stationary problem. === vector newFeSpaces = probStat->getFeSpaces(); for (int i = 0; i < static_cast(newFeSpaces.size()); i++) if (find(feSpaces.begin(), feSpaces.end(), newFeSpaces[i]) == feSpaces.end()) feSpaces.push_back(newFeSpaces[i]); // === Add mesh of stationary problem and create a corresponding === // === refinement manager object. === if (mesh != NULL) { TEST_EXIT(mesh == probStat->getMesh()) ("Does not yet support for different meshes!\n"); } else { mesh = probStat->getMesh(); } switch (mesh->getDim()) { case 2: refineManager = new RefinementManager2d(); break; case 3: refineManager = new RefinementManager3d(); break; default: ERROR_EXIT("This should not happen for dim = %d!\n", mesh->getDim()); } partitioner->setMesh(mesh); // === Check whether the stationary problem should be serialized. === // Create parallel serialization file writer, if needed. int writeSerialization = 0; Parameters::get(probStat->getName() + "->output->write serialization", writeSerialization); if (writeSerialization && !writeSerializationFile) { string filename = ""; Parameters::get(name + "->output->serialization filename", filename); TEST_EXIT(filename != "") ("No filename defined for parallel serialization file!\n"); int tsModulo = -1; Parameters::get(probStat->getName() + "->output->write every i-th timestep", tsModulo); probStat->getFileWriterList().push_back(new Serializer(this, filename, tsModulo)); writeSerializationFile = true; } // === Check whether the stationary problem should be deserialized. === int readSerialization = 0; Parameters::get(probStat->getName() + "->input->read serialization", readSerialization); if (readSerialization) { string filename = ""; Parameters::get(probStat->getName() + "->input->serialization filename", filename); filename += ".p" + lexical_cast(mpiRank); MSG("Start deserialization with %s\n", filename.c_str()); ifstream in(filename.c_str()); TEST_EXIT(!in.fail())("Could not open deserialization file: %s\n", filename.c_str()); // Read the revision number of the AMDiS version which was used to create // the serialization file. int revNumber = -1; SerUtil::deserialize(in, revNumber); probStat->deserialize(in); in.close(); MSG("Deserialization from file: %s\n", filename.c_str()); filename = ""; Parameters::get(name + "->input->serialization filename", filename); TEST_EXIT(filename != "") ("No filename defined for parallel deserialization file!\n"); string rankFilename = filename + ".p" + lexical_cast(mpiRank); in.open(rankFilename.c_str()); TEST_EXIT(!in.fail())("Could not open parallel deserialization file: %s\n", filename.c_str()); // Read the revision number of the AMDiS version which was used to create // the serialization file. revNumber = -1; SerUtil::deserialize(in, revNumber); deserialize(in); in.close(); MSG("Deserializtion of mesh distributor from file: %s\n", rankFilename.c_str()); deserialized = true; } problemStat.push_back(probStat); // === If the mesh distributor is already initialized, don't forget to === // === remove the periodic boundary conditions on these objects. === if (initialized) removePeriodicBoundaryConditions(probStat); } void MeshDistributor::addProblemStatGlobal(ProblemStatSeq *probStat) { FUNCNAME("MeshDistributor::addProblemStatGlobal()"); if (globalMeshDistributor == NULL) globalMeshDistributor = new MeshDistributor(); globalMeshDistributor->addProblemStat(probStat); } void MeshDistributor::exitParallelization() {} void MeshDistributor::registerDofMap(ParallelDofMapping &dofMap) { FUNCNAME("MeshDistributor::registerDofMap()"); TEST_EXIT(find(dofMaps.begin(), dofMaps.end(), &dofMap) == dofMaps.end()) ("Parallel DOF mapping already registerd in mesh distributor object!\n"); dofMaps.push_back(&dofMap); } void MeshDistributor::testForMacroMesh() { FUNCNAME("MeshDistributor::testForMacroMesh()"); int nMacroElements = 0; TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL); while (elInfo) { TEST_EXIT(elInfo->getLevel() == 0) ("Mesh is already refined! This does not work with parallelization!\n"); TEST_EXIT(elInfo->getType() == 0) ("Only macro elements with level 0 are supported!\n"); nMacroElements++; elInfo = stack.traverseNext(elInfo); } TEST_EXIT(nMacroElements >= mpiSize) ("The mesh has less macro elements than number of mpi processes!\n"); } void MeshDistributor::synchVector(SystemVector &vec, int level) { FUNCNAME("MeshDistributor::synchVector()"); TEST_EXIT_DBG(level >= 0 && level <= 1)("Wrong level number!\n"); MPI::Intracomm &levelComm = levelData.getMpiComm(level); DofComm &dc = (level == 0 ? dofComm : dofCommSd); StdMpi > stdMpi(levelComm); for (DofComm::Iterator it(dc.getSendDofs()); !it.end(); it.nextRank()) { vector dofs; for (int i = 0; i < vec.getSize(); i++) { DOFVector &dofVec = *(vec.getDOFVector(i)); for (it.beginDofIter(vec.getFeSpace(i)); !it.endDofIter(); it.nextDof()) dofs.push_back(dofVec[it.getDofIndex()]); } int rank = it.getRank(); if (level > 0) rank = levelData.mapRank(rank, 0, level); stdMpi.send(rank, dofs); } for (DofComm::Iterator it(dc.getRecvDofs()); !it.end(); it.nextRank()) { int rank = it.getRank(); if (level > 0) rank = levelData.mapRank(rank, 0, level); stdMpi.recv(rank); } stdMpi.startCommunication(); for (DofComm::Iterator it(dc.getRecvDofs()); !it.end(); it.nextRank()) { int rank = it.getRank(); if (level > 0) rank = levelData.mapRank(rank, 0, level); int counter = 0; vector &recvData = stdMpi.getRecvData(rank); for (int i = 0; i < vec.getSize(); i++) { DOFVector &dofVec = *(vec.getDOFVector(i)); for (it.beginDofIter(vec.getFeSpace(i)); !it.endDofIter(); it.nextDof()) { TEST_EXIT_DBG(counter < recvData.size()) ("Recv data from rank %d has only %d entries!\n", rank, recvData.size()); dofVec[it.getDofIndex()] = recvData[counter++]; } } } } void MeshDistributor::check3dValidMesh() { FUNCNAME("MeshDistributor::check3dValidMesh()"); if (mesh->getDim() != 3) return; // === Create set of all edges and all macro element indices in rank's === // === subdomain. === std::set allEdges; std::set rankMacroEls; TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, 0, Mesh::CALL_EL_LEVEL); while (elInfo) { for (int i = 0; i < 6; i++) { ElementObjectData elData(elInfo->getElement()->getIndex(), i); DofEdge e = elObjDb.getEdgeLocalMap(elData); allEdges.insert(e); } rankMacroEls.insert(elInfo->getElement()->getIndex()); elInfo = stack.traverseNext(elInfo); } // === Reset fixed refinement edges and assume the mesh is valid. Search === // === for the opposite. === FixRefinementPatch::connectedEdges.clear(); bool valid3dMesh = true; // Traverse all edges for (std::set::iterator it = allEdges.begin(); it != allEdges.end(); ++it) { // For each edge get all macro elements that contain this edge. vector& edgeEls = elObjDb.getElements(*it); TEST_EXIT_DBG(edgeEls.size() > 0) ("No edge %d/%d in elObjDB!\n", it->first, it->second); // We have now to check, if the current edge connects two macro elements, // which are otherwise not connected. The basic idea to check this is very // simple: We take the first macro element in rank's subdomain that contain // this edge and add it to the set variable "el0". All other macro elements // which share this edge are added to "el1". std::set el0, el1; map edgeNoInEl; for (unsigned int i = 0; i < edgeEls.size(); i++) { if (rankMacroEls.count(edgeEls[i].elIndex)) { if (el0.empty()) el0.insert(edgeEls[i].elIndex); else el1.insert(edgeEls[i].elIndex); edgeNoInEl[edgeEls[i].elIndex] = edgeEls[i].ithObject; } } TEST_EXIT_DBG(!el0.empty())("Should not happen!\n"); // If el1 is empty, there is only on macro element in the mesh which // contains this edge. Hence, we can continue with checking another edge. if (el1.empty()) continue; bool found = false; do { found = false; for (std::set::iterator elIt = el0.begin(); elIt != el0.end(); ++elIt) { for (int i = 0; i < 4; i++) { int neighEl = macroElementNeighbours[*elIt][i]; if (neighEl != -1 && el1.count(neighEl)) { el0.insert(neighEl); el1.erase(neighEl); found = true; } } if (found) break; } } while (found); if (!el1.empty()) { valid3dMesh = false; MSG_DBG("Unvalid 3D mesh with %d (%d - %d) elements on this edge!\n", edgeEls.size(), el0.size(), el1.size()); for (std::set::iterator elIt0 = el0.begin(); elIt0 != el0.end(); ++elIt0) { for (std::set::iterator elIt1 = el1.begin(); elIt1 != el1.end(); ++elIt1) { pair edge0 = make_pair(elObjDb.getElementPtr(*elIt0), edgeNoInEl[*elIt0]); pair edge1 = make_pair(elObjDb.getElementPtr(*elIt1), edgeNoInEl[*elIt1]); #if (DEBUG != 0) DofEdge dofEdge0 = edge0.first->getEdge(edge0.second); DofEdge dofEdge1 = edge1.first->getEdge(edge1.second); WorldVector c0, c1; mesh->getDofIndexCoords(dofEdge0.first, feSpaces[0], c0); mesh->getDofIndexCoords(dofEdge0.second, feSpaces[0], c1); MSG("FOUND EDGE %d/%d <-> %d/%d\n", edge0.first->getIndex(), edge0.second, edge1.first->getIndex(), edge1.second); MSG("FOUND EDGE: %d %d with coords %f %f %f %f %f %f\n", dofEdge0.first, dofEdge0.second, c0[0], c0[1], c0[2], c1[0], c1[1], c1[2]); #endif TEST_EXIT_DBG(dofEdge0 == dofEdge1)("Should noth happen!\n"); FixRefinementPatch::connectedEdges.push_back(make_pair(edge0, edge1)); FixRefinementPatch::connectedEdges.push_back(make_pair(edge1, edge0)); } } } } MSG_DBG("Mesh is 3d valid mesh: %d\n", valid3dMesh); } void MeshDistributor::getAllBoundaryDofs(const FiniteElemSpace *feSpace, int level, DofContainer& dofs) { FUNCNAME("MeshDistributor::getAllBoundaryDofs()"); DofContainerSet dofSet; for (DofComm::Iterator it(dofComm.getSendDofs(), level, feSpace); !it.end(); it.nextRank()) dofSet.insert(it.getDofs().begin(), it.getDofs().end()); for (DofComm::Iterator it(dofComm.getRecvDofs(), level, feSpace); !it.end(); it.nextRank()) dofSet.insert(it.getDofs().begin(), it.getDofs().end()); dofs.clear(); dofs.insert(dofs.begin(), dofSet.begin(), dofSet.end()); } void MeshDistributor::removePeriodicBoundaryConditions() { FUNCNAME("MeshDistributor::removePeriodicBoundaryConditions()"); // Remove periodic boundaries in boundary manager on matrices and vectors. for (unsigned int i = 0; i < problemStat.size(); i++) removePeriodicBoundaryConditions(problemStat[i]); // Remove periodic boundaries on elements in mesh. TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_EVERY_EL_PREORDER); while (elInfo) { elInfo->getElement()->deleteElementData(PERIODIC); elInfo = stack.traverseNext(elInfo); } // Remove periodic vertex associations mesh->getPeriodicAssociations().clear(); } void MeshDistributor::removePeriodicBoundaryConditions(ProblemStatSeq *probStat) { FUNCNAME("MeshDistributor::removePeriodicBoundaryConditions()"); int nComponents = probStat->getNumComponents(); for (int j = 0; j < nComponents; j++) { for (int k = 0; k < nComponents; k++) { DOFMatrix* mat = probStat->getSystemMatrix(j, k); if (mat && mat->getBoundaryManager()) removePeriodicBoundaryConditions(const_cast(mat->getBoundaryManager()->getBoundaryConditionMap())); } if (probStat->getSolution()->getDOFVector(j)->getBoundaryManager()) removePeriodicBoundaryConditions(const_cast(probStat->getSolution()->getDOFVector(j)->getBoundaryManager()->getBoundaryConditionMap())); if (probStat->getRhs()->getDOFVector(j)->getBoundaryManager()) removePeriodicBoundaryConditions(const_cast(probStat->getRhs()->getDOFVector(j)->getBoundaryManager()->getBoundaryConditionMap())); } } void MeshDistributor::removePeriodicBoundaryConditions(BoundaryIndexMap& boundaryMap) { FUNCNAME("MeshDistributor::removePeriodicBoundaryConditions()"); BoundaryIndexMap::iterator it = boundaryMap.begin(); while (it != boundaryMap.end()) { if (it->second->isPeriodic()) boundaryMap.erase(it++); else ++it; } } void MeshDistributor::createMeshLevelStructure() { FUNCNAME("MeshDistributor::createMeshLevelStructure()"); if (mpiSize != 16) return; std::set neighbours; switch (mpiRank) { case 0: neighbours.insert(1); neighbours.insert(2); neighbours.insert(3); break; case 1: neighbours.insert(0); neighbours.insert(4); neighbours.insert(2); neighbours.insert(3); neighbours.insert(6); break; case 2: neighbours.insert(0); neighbours.insert(1); neighbours.insert(3); neighbours.insert(8); neighbours.insert(9); break; case 3: neighbours.insert(0); neighbours.insert(1); neighbours.insert(4); neighbours.insert(2); neighbours.insert(6); neighbours.insert(8); neighbours.insert(9); neighbours.insert(12); break; case 4: neighbours.insert(1); neighbours.insert(3); neighbours.insert(6); neighbours.insert(7); neighbours.insert(5); break; case 5: neighbours.insert(4); neighbours.insert(6); neighbours.insert(7); break; case 6: neighbours.insert(1); neighbours.insert(4); neighbours.insert(5); neighbours.insert(3); neighbours.insert(7); neighbours.insert(9); neighbours.insert(12); neighbours.insert(13); break; case 7: neighbours.insert(4); neighbours.insert(5); neighbours.insert(6); neighbours.insert(12); neighbours.insert(13); break; case 8: neighbours.insert(2); neighbours.insert(3); neighbours.insert(9); neighbours.insert(10); neighbours.insert(11); break; case 9: neighbours.insert(2); neighbours.insert(3); neighbours.insert(6); neighbours.insert(8); neighbours.insert(12); neighbours.insert(10); neighbours.insert(11); neighbours.insert(14); break; case 10: neighbours.insert(8); neighbours.insert(9); neighbours.insert(11); break; case 11: neighbours.insert(8); neighbours.insert(9); neighbours.insert(12); neighbours.insert(10); neighbours.insert(14); break; case 12: neighbours.insert(3); neighbours.insert(6); neighbours.insert(7); neighbours.insert(9); neighbours.insert(13); neighbours.insert(11); neighbours.insert(14); neighbours.insert(15); break; case 13: neighbours.insert(6); neighbours.insert(7); neighbours.insert(12); neighbours.insert(14); neighbours.insert(15); break; case 14: neighbours.insert(9); neighbours.insert(12); neighbours.insert(13); neighbours.insert(11); neighbours.insert(15); break; case 15: neighbours.insert(12); neighbours.insert(13); neighbours.insert(14); break; } TEST_EXIT(neighbours.size() > 0)("Should not happen!\n"); levelData.init(neighbours); bool multiLevelTest = false; Parameters::get("parallel->multi level test", multiLevelTest); if (multiLevelTest) { switch (mpiRank) { case 0: case 1: case 2: case 3: { std::set m; m.insert(0); m.insert(1); m.insert(2); m.insert(3); levelData.addLevel(m, 0); } break; case 4: case 5: case 6: case 7: { std::set m; m.insert(4); m.insert(5); m.insert(6); m.insert(7); levelData.addLevel(m, 1); } break; case 8: case 9: case 10: case 11: { std::set m; m.insert(8); m.insert(9); m.insert(10); m.insert(11); levelData.addLevel(m, 2); } break; case 12: case 13: case 14: case 15: { std::set m; m.insert(12); m.insert(13); m.insert(14); m.insert(15); levelData.addLevel(m, 3); } break; } } } void MeshDistributor::checkMeshChange(bool tryRepartition) { FUNCNAME("MeshDistributor::checkMeshChange()"); MPI::COMM_WORLD.Barrier(); double first = MPI::Wtime(); int skip = 0; Parameters::get("parallel->debug->skip check mesh change", skip); // === If mesh has not been changed on all ranks, return. === int recvAllValues = 0; int sendValue = static_cast(mesh->getChangeIndex() != lastMeshChangeIndex); mpiComm.Allreduce(&sendValue, &recvAllValues, 1, MPI_INT, MPI_SUM); if (recvAllValues == 0) return; // === At least one rank mesh has been changed, so the boundaries must be === // === adapted to the new mesh structure. === if (skip == 0) { int iterationCounter = 0; // To check the interior boundaries, the ownership of the boundaries is not // important. Therefore, we add all boundaries to one boundary container. RankToBoundMap allBound; for (InteriorBoundary::iterator it(intBoundary.getOwn()); !it.end(); ++it) if ((mesh->getDim() == 2 && it->rankObj.subObj == EDGE) || (mesh->getDim() == 3 && it->rankObj.subObj == FACE)) allBound[it.getRank()].push_back(*it); for (InteriorBoundary::iterator it(intBoundary.getOther()); !it.end(); ++it) if ((mesh->getDim() == 2 && it->rankObj.subObj == EDGE) || (mesh->getDim() == 3 && it->rankObj.subObj == FACE)) allBound[it.getRank()].push_back(*it); for (InteriorBoundary::iterator it(intBoundary.getPeriodic()); !it.end(); ++it) if (it.getRank() != mpiRank) if ((mesh->getDim() == 2 && it->rankObj.subObj == EDGE) || (mesh->getDim() == 3 && it->rankObj.subObj == FACE)) allBound[it.getRank()].push_back(*it); do { bool meshChanged = false; // === Check the boundaries and adapt mesh if necessary. === MSG_DBG("Run checkAndAdaptBoundary ...\n"); // Check for periodic boundaries within rank's subdomain. for (InteriorBoundary::iterator it(intBoundary.getPeriodic()); !it.end(); ++it) { if (it.getRank() == mpiRank) { if ((mesh->getDim() == 2 && it->rankObj.subObj == EDGE) || (mesh->getDim() == 3 && it->rankObj.subObj == FACE)) { MeshStructure elCode; elCode.init(it->rankObj); MeshManipulation mm(mesh); meshChanged |= mm.fitElementToMeshCode(elCode, it->neighObj); } } } meshChanged |= checkAndAdaptBoundary(allBound); // === Check on all ranks if at least one rank's mesh has changed. === int sendValue = static_cast(meshChanged); recvAllValues = 0; mpiComm.Allreduce(&sendValue, &recvAllValues, 1, MPI_INT, MPI_SUM); MSG("Mesh changed on %d ranks!\n", recvAllValues); iterationCounter++; } while (recvAllValues != 0); MSG("Number of iteration to adapt mesh: %d\n", iterationCounter); } MPI::COMM_WORLD.Barrier(); MSG("Parallel mesh adaption needed %.5f seconds\n", MPI::Wtime() - first); // === Update the DOF numbering and mappings. === updateLocalGlobalNumbering(); #if (DEBUG != 0) debug::writeMesh(feSpaces[0], -1, debugOutputDir + "mesh"); ParallelDebug::testPeriodicBoundary(*this); #endif // === The mesh has changed, so check if it is required to repartition === // === the mesh. === nMeshChangesAfterLastRepartitioning++; if (repartitioningFailed > 0) { MSG_DBG("Repartitioning not tried because it has failed in the past!\n"); repartitioningFailed--; } else if (tryRepartition && repartitioningAllowed && nMeshChangesAfterLastRepartitioning >= repartitionIthChange) { repartitionMesh(); nMeshChangesAfterLastRepartitioning = 0; } else { MSG("Repartitioning not tried because tryRepartitioning = %d repartitioningAllowed = %d nMeshChange = %d repartitionIthChange = %d\n", tryRepartition, repartitioningAllowed, nMeshChangesAfterLastRepartitioning, repartitionIthChange); } // === Print imbalance factor. === printImbalanceFactor(); } void MeshDistributor::getImbalanceFactor(double &imbalance, int &minDofs, int &maxDofs, int &sumDofs) { FUNCNAME("MeshDistributor::getImbalanceFactor()"); vector nDofsInRank(mpiSize); int nDofs = mesh->getDofAdmin(0).getUsedDofs(); mpiComm.Gather(&nDofs, 1, MPI_INT, &(nDofsInRank[0]), 1, MPI_INT, 0); if (mpiRank == 0) { sumDofs = 0; minDofs = numeric_limits::max(); maxDofs = numeric_limits::min(); for (int i = 0; i < mpiSize; i++) { sumDofs += nDofsInRank[i]; minDofs = std::min(minDofs, nDofsInRank[i]); maxDofs = std::max(maxDofs, nDofsInRank[i]); } int avrgDofs = sumDofs / mpiSize; imbalance = ((static_cast(maxDofs) / avrgDofs) - 1.0); } } double MeshDistributor::getImbalanceFactor() { double factor; int a = 0; int b = 0; int c = 0; getImbalanceFactor(factor, a, b, c); return factor; } void MeshDistributor::printImbalanceFactor() { FUNCNAME("MeshDistributor::printImbalanceFactor()"); double imbalanceFactor = 0.0; int minDofs = 0; int maxDofs = 0; int sumDofs = 0; getImbalanceFactor(imbalanceFactor, minDofs, maxDofs, sumDofs); if (mpiRank == 0) MSG("Imbalancing factor: %.2f [ minDofs = %d, maxDofs = %d, sumDofs = %d ]\n", imbalanceFactor * 100.0, minDofs, maxDofs, sumDofs); } bool MeshDistributor::checkAndAdaptBoundary(RankToBoundMap &allBound) { FUNCNAME("MeshDistributor::checkAndAdaptBoundary()"); // === Create mesh structure codes for all ranks boundary elements. === map sendCodes; for (RankToBoundMap::iterator it = allBound.begin(); it != allBound.end(); ++it) { for (vector::iterator boundIt = it->second.begin(); boundIt != it->second.end(); ++boundIt) { MeshStructure elCode; elCode.init(boundIt->rankObj); sendCodes[it->first].push_back(elCode); } } StdMpi stdMpi(mpiComm, true); stdMpi.send(sendCodes); for (RankToBoundMap::iterator it = allBound.begin(); it != allBound.end(); ++it) stdMpi.recv(it->first); stdMpi.startCommunication(); // === Compare received mesh structure codes. === bool meshChanged = false; int cCounter = 0; for (RankToBoundMap::iterator it = allBound.begin(); it != allBound.end(); ++it) { MeshCodeVec &recvCodes = stdMpi.getRecvData()[it->first]; int i = 0; for (vector::iterator boundIt = it->second.begin(); boundIt != it->second.end(); ++boundIt, i++) { MeshStructure elCode; elCode.init(boundIt->rankObj); #if (DEBUG != 0) ParallelDebug::followBoundary(mesh, *boundIt, elCode); #endif if (elCode.getCode() != recvCodes[i].getCode()) { // MSG("MACRO EL %d %d %d DOES NOT FIT WITH MACRO EL %d %d %d ON RANK %d\n", // boundIt->rankObj.elIndex, boundIt->rankObj.subObj, boundIt->rankObj.ithObj, // boundIt->neighObj.elIndex, boundIt->neighObj.subObj, boundIt->neighObj.ithObj, // it->first); TEST_EXIT_DBG(refineManager)("Refinement manager is not set correctly!\n"); MeshManipulation mm(mesh); meshChanged |= mm.fitElementToMeshCode(recvCodes[i], boundIt->rankObj); } } } return meshChanged; } void MeshDistributor::serialize(ostream &out, DofContainer &data) { int vecSize = data.size(); SerUtil::serialize(out, vecSize); for (int i = 0; i < vecSize; i++) { int dofIndex = *(data[i]); SerUtil::serialize(out, dofIndex); } } void MeshDistributor::serialize(ostream &out, map > &data) { int mapSize = data.size(); SerUtil::serialize(out, mapSize); for (map >::iterator it = data.begin(); it != data.end(); ++it) { int rank = it->first; SerUtil::serialize(out, rank); for (map::iterator dcIt = it->second.begin(); dcIt != it->second.end(); ++dcIt) serialize(out, dcIt->second); } } void MeshDistributor::deserialize(istream &in, DofContainer &data, map &dofIndexMap) { FUNCNAME("MeshDistributor::deserialize()"); int vecSize = 0; SerUtil::deserialize(in, vecSize); data.clear(); data.resize(vecSize); for (int i = 0; i < vecSize; i++) { int dofIndex = 0; SerUtil::deserialize(in, dofIndex); TEST_EXIT_DBG(dofIndexMap.count(dofIndex) != 0) ("Dof index could not be deserialized correctly!\n"); data[i] = dofIndexMap[dofIndex]; } } void MeshDistributor::deserialize(istream &in, map > &data, map > &dofIndexMap) { FUNCNAME("MeshDistributor::deserialize()"); ERROR_EXIT("Must be reimplemented!\n"); #if 0 data.clear(); int mapSize = 0; SerUtil::deserialize(in, mapSize); for (int i = 0; i < mapSize; i++) { int rank = 0; SerUtil::deserialize(in, rank); for (unsigned int j = 0; j < componentSpaces.size(); j++) deserialize(in, data[rank][componentSpaces[j]], dofIndexMap[componentSpaces[j]]); } #endif } void MeshDistributor::setInitialElementWeights() { FUNCNAME("MeshDistributor::setInitialElementWeights()"); elemWeights.clear(); string filename = ""; Parameters::get(mesh->getName() + "->macro weights", filename); if (filename != "") { MSG("Read macro weights from %s\n", filename.c_str()); ifstream infile; infile.open(filename.c_str(), ifstream::in); while (!infile.eof()) { int elNum, elWeight; infile >> elNum; if (infile.eof()) break; infile >> elWeight; elemWeights[elNum] = elWeight; } infile.close(); } else { TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL); while (elInfo) { elemWeights[elInfo->getElement()->getIndex()] = 1.0; elInfo = stack.traverseNext(elInfo); } } } void MeshDistributor::repartitionMesh() { FUNCNAME("MeshDistributor::repartitionMesh()"); // === First, check if the load is unbalanced on the ranks. === int repartitioning = 0; double imbalanceFactor = getImbalanceFactor(); if (mpiRank == 0) { double imbalanceRepartitionBound = 0.2; Parameters::get("parallel->repartitioning->imbalance", imbalanceRepartitionBound); if (imbalanceFactor > imbalanceRepartitionBound) repartitioning = 1; mpiComm.Bcast(&repartitioning, 1, MPI_INT, 0); } else { mpiComm.Bcast(&repartitioning, 1, MPI_INT, 0); } if (repartitioning == 0) return; MPI::COMM_WORLD.Barrier(); double timePoint = MPI::Wtime(); #if (DEBUG != 0) ParallelDebug::testDoubleDofs(mesh); int writePartMesh = 1; #else int writePartMesh = 0; #endif Parameters::get("dbg->write part mesh", writePartMesh); if (writePartMesh > 0 && repartitioningCounter == 0) ParallelDebug::writePartitioningFile(debugOutputDir + "partitioning", repartitioningCounter, feSpaces[0]); repartitioningCounter++; // === Create new element weights. === elemWeights.clear(); { TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL); while (elInfo) { elemWeights[elInfo->getMacroElement()->getIndex()]++; elInfo = stack.traverseNext(elInfo); } } double maxWeight = -1.0; double sumWeight = 0.0; for (map::iterator it = elemWeights.begin(); it != elemWeights.end(); ++it) { maxWeight = std::max(maxWeight, it->second); sumWeight += it->second; } mpi::globalMax(maxWeight); mpi::globalAdd(sumWeight); MSG("Partition weight: sum = %e max = %e\n", sumWeight, maxWeight); // === Run mesh partitioner to calculate a new mesh partitioning. === TEST_EXIT(dofMaps.size())("No DOF mapping defined!\n"); ParallelDofMapping &dofMap = *(dofMaps[0]); partitioner->setLocalGlobalDofMap(&(dofMap[feSpaces[0]].getMap())); bool partitioningSucceed = partitioner->partition(elemWeights, ADAPTIVE_REPART); if (!partitioningSucceed) { repartitioningFailed = 20; MSG("Mesh partitioner created empty partition!\n"); return; } // In the case the partitioner does not create a new mesh partition, return // without and changes. if (!partitioner->meshChanged()) { repartitioningFailed = 20; MSG("Mesh partition does not create a new partition!\n"); return; } TEST_EXIT_DBG(!(partitioner->getSendElements().size() == mesh->getMacroElements().size() && partitioner->getRecvElements().size() == 0)) ("Partition is empty, should not happen!\n"); // === Create map that maps macro element indices to pointers to the === // === macro elements. === map elIndexMap; for (vector::iterator it = allMacroElements.begin(); it != allMacroElements.end(); ++it) elIndexMap[(*it)->getIndex()] = *it; // === Create set of all new macro elements this rank will receive from === // === other ranks. === std::set newMacroEl; for (map >::iterator it = partitioner->getRecvElements().begin(); it != partitioner->getRecvElements().end(); ++it) { for (vector::iterator elIt = it->second.begin(); elIt != it->second.end(); ++elIt) { TEST_EXIT_DBG(elIndexMap.count(*elIt) == 1) ("Could not find macro element %d\n", *elIt); newMacroEl.insert(elIndexMap[*elIt]); } } std::set allMacroEl; for (std::set::iterator it = newMacroEl.begin(); it != newMacroEl.end(); ++it) allMacroEl.insert(*it); for (deque::iterator it = mesh->firstMacroElement(); it != mesh->endOfMacroElements(); ++it) allMacroEl.insert(*it); // === Add new macro elements to mesh. === for (std::set::iterator it = newMacroEl.begin(); it != newMacroEl.end(); ++it) { MacroElement *mel = *it; // First, reset all neighbour relations. The correct neighbours will be // set later. for (int i = 0; i < mesh->getGeo(NEIGH); i++) mel->setNeighbour(i, NULL); // Create new DOFs for the macro element. mel->getElement()->createNewDofPtrs(true); // Push the macro element to all macro elements in mesh. mesh->getMacroElements().push_back(mel); } // === Send and receive mesh structure codes. === map sendCodes; map > > sendValues; for (map >::iterator it = partitioner->getSendElements().begin(); it != partitioner->getSendElements().end(); ++it) { for (vector::iterator elIt = it->second.begin(); elIt != it->second.end(); ++elIt) { MeshStructure elCode; elCode.init(mesh, *elIt); sendCodes[it->first].push_back(elCode); for (unsigned int i = 0; i < interchangeVectors.size(); i++) { vector valVec; elCode.getMeshStructureValues(*elIt, interchangeVectors[i], valVec); sendValues[it->first].push_back(valVec); } } } StdMpi stdMpi(mpiComm, true); stdMpi.send(sendCodes); for (map >::iterator it = partitioner->getRecvElements().begin(); it != partitioner->getRecvElements().end(); ++it) stdMpi.recv(it->first); stdMpi.startCommunication(); TEST_EXIT(interchangeVectors.size() > 0) ("There are no interchange vectors defined!\n"); StdMpi > > stdMpi2(mpiComm, true); stdMpi2.send(sendValues); for (map >::iterator it = partitioner->getRecvElements().begin(); it != partitioner->getRecvElements().end(); ++it) stdMpi2.recv(it->first); stdMpi2.startCommunication(); // === Adapte the new macro elements due to the received mesh === // === structure codes. === for (map >::iterator it = partitioner->getRecvElements().begin(); it != partitioner->getRecvElements().end(); ++it) { MeshCodeVec &recvCodes = stdMpi.getRecvData()[it->first]; int i = 0; TEST_EXIT_DBG(recvCodes.size() == it->second.size()) ("Should not happen!\n"); for (vector::iterator elIt = it->second.begin(); elIt != it->second.end(); ++elIt) recvCodes[i++].fitMeshToStructure(mesh, refineManager, false, *elIt); } // === Set correct neighbour information on macro elements. === for (std::set::iterator it = allMacroEl.begin(); it != allMacroEl.end(); ++it) { int elIndex = (*it)->getIndex(); for (int i = 0; i < mesh->getGeo(NEIGH); i++) { TEST_EXIT_DBG(macroElementNeighbours.count(elIndex) == 1) ("Should not happen!\n"); TEST_EXIT_DBG(static_cast(macroElementNeighbours[elIndex].size()) == mesh->getGeo(NEIGH)) ("Should not happen!\n"); int neighIndex = macroElementNeighbours[elIndex][i]; if (neighIndex == -1 || partitioner->getElementInRank()[neighIndex] == false) { (*it)->setNeighbour(i, NULL); } else { TEST_EXIT_DBG(elIndexMap.count(neighIndex) == 1) ("Should not happen!\n"); (*it)->setNeighbour(i, elIndexMap[neighIndex]); } } } // === Delete macros from mesh. === std::set deleteMacroElements; for (map >::iterator it = partitioner->getSendElements().begin(); it != partitioner->getSendElements().end(); ++it) { for (vector::iterator elIt = it->second.begin(); elIt != it->second.end(); ++elIt) { TEST_EXIT_DBG(elIndexMap.count(*elIt) == 1) ("Could not find macro element %d\n", *elIt); deleteMacroElements.insert(elIndexMap[*elIt]); } } // Note that also if there are no macros to be deleted, this function will // update the number of elements, vertices, etc. of the mesh. mesh->removeMacroElements(deleteMacroElements, feSpaces); // === Remove double DOFs. === MeshManipulation meshManipulation(mesh); meshManipulation.deleteDoubleDofs(feSpaces, newMacroEl, elObjDb); mesh->dofCompress(); partitioner->createPartitionMap(partitionMap); createInteriorBoundary(false); updateLocalGlobalNumbering(); // === After mesh adaption, set values of interchange vectors. === for (map >::iterator it = partitioner->getRecvElements().begin(); it != partitioner->getRecvElements().end(); ++it) { MeshCodeVec &recvCodes = stdMpi.getRecvData()[it->first]; vector > &recvValues = stdMpi2.getRecvData()[it->first]; int i = 0, j = 0; for (vector::iterator elIt = it->second.begin(); elIt != it->second.end(); ++elIt) { for (unsigned int k = 0; k < interchangeVectors.size(); k++) recvCodes[i].setMeshStructureValues(*elIt, interchangeVectors[k], recvValues[j++]); i++; } } #if (DEBUG != 0) MSG("AMDiS runs in debug mode, so make some test ...\n"); ParallelDebug::writePartitioningFile(debugOutputDir + "partitioning", repartitioningCounter, feSpaces[0]); ParallelDebug::testAllElements(*this); ParallelDebug::testDoubleDofs(mesh); ParallelDebug::testInteriorBoundary(*this); ParallelDebug::testPeriodicBoundary(*this); MSG("Debug mode tests finished!\n"); #endif // In 3D we have to make some test, if the resulting mesh is valid. check3dValidMesh(); MPI::COMM_WORLD.Barrier(); MSG("Mesh repartitioning needed %.5f seconds\n", MPI::Wtime() - timePoint); } void MeshDistributor::createInteriorBoundary(bool firstCall) { FUNCNAME("MeshDistributor::createInteriorBoundary()"); if (firstCall) elObjDb.create(partitionMap, levelData); elObjDb.updateRankData(); // unsigned long memsize = elObjDb.calculateMemoryUsage(); // MSG("Memory usage of element object database = %5.f KByte\n", // static_cast(memsize / 1024)); intBoundary.create(levelData, 0, elObjDb); ParallelDebug::printBoundaryInfo(intBoundary); if (levelData.getLevelNumber() > 1) { intBoundarySd.create(levelData, 1, elObjDb); ParallelDebug::printBoundaryInfo(intBoundarySd, 0, true); } if (firstCall) { int tmpSend = static_cast(intBoundary.hasPeriodic()); int tmpRecv = 0; mpiComm.Allreduce(&tmpSend, &tmpRecv, 1, MPI_INT, MPI_MAX); hasPeriodicBoundary = static_cast(tmpRecv); } } void MeshDistributor::createBoundaryDofs() { FUNCNAME("MeshDistributor::createBoundaryDofs()"); if (printTimings) MPI::COMM_WORLD.Barrier(); double first = MPI::Wtime(); // === Create DOF communicator. === dofComm.init(0, levelData, feSpaces); dofComm.create(intBoundary); if (levelData.getLevelNumber() > 1) { dofCommSd.init(0, levelData, feSpaces); dofCommSd.create(intBoundarySd); } // === If requested, create more information on communication DOFs. === if (createBoundaryDofFlag.isSet(BOUNDARY_SUBOBJ_SORTED)) { int nLevels = levelData.getLevelNumber(); boundaryDofInfo.resize(nLevels); for (unsigned int i = 0; i < feSpaces.size(); i++) { const FiniteElemSpace *feSpace = feSpaces[i]; for (int level = 0; level < nLevels; level++) { // === Clear data. === for (int geo = FACE; geo >= VERTEX; geo--) boundaryDofInfo[level][feSpace].geoDofs[static_cast(geo)].clear(); // === Create send DOFs. === for (int geo = FACE; geo >= VERTEX; geo--) { for (InteriorBoundary::iterator it(intBoundary.getOwn(), level); !it.end(); ++it) { if (it->rankObj.subObj == geo) { DofContainer dofs; it->rankObj.el->getAllDofs(feSpace, it->rankObj, dofs); if (createBoundaryDofFlag.isSet(BOUNDARY_FILL_INFO_SEND_DOFS)) boundaryDofInfo[level][feSpace]. geoDofs[static_cast(geo)].insert(dofs.begin(), dofs.end()); } } } // === Create recv DOFs. === for (int geo = FACE; geo >= VERTEX; geo--) { for (InteriorBoundary::iterator it(intBoundary.getOther(), level); !it.end(); ++it) { if (it->rankObj.subObj == geo) { DofContainer dofs; it->rankObj.el->getAllDofs(feSpace, it->rankObj, dofs); if (createBoundaryDofFlag.isSet(BOUNDARY_FILL_INFO_RECV_DOFS)) boundaryDofInfo[level][feSpace]. geoDofs[static_cast(geo)].insert(dofs.begin(), dofs.end()); } } } } } } if (printTimings) { MPI::COMM_WORLD.Barrier(); MSG("MESHDIST 01 (createBoundaryDofs) needed %.5f seconds\n", MPI::Wtime() - first); } } void MeshDistributor::removeMacroElements() { FUNCNAME("MeshDistributor::removeMacroElements()"); std::set macrosToRemove; for (deque::iterator it = mesh->firstMacroElement(); it != mesh->endOfMacroElements(); ++it) if (partitioner->getElementInRank()[(*it)->getIndex()] == false) macrosToRemove.insert(*it); mesh->removeMacroElements(macrosToRemove, feSpaces); } void MeshDistributor::updateLocalGlobalNumbering() { FUNCNAME("MeshDistributor::updateLocalGlobalNumbering()"); MPI::COMM_WORLD.Barrier(); double first = MPI::Wtime(); mesh->dofCompress(); #if (DEBUG != 0) debug::ElementIdxToDofs elMap; debug::createSortedDofs(mesh, elMap); #endif // === Update DOF communicator objects. === createBoundaryDofs(); // === Update all registered DOF mapping objects. === updateParallelDofMappings(); // === Create periodic DOF maps, if there are periodic boundaries. === if (hasPeriodicBoundary) { createPeriodicMap(); for (int i = 0; i < static_cast(dofMaps.size()); i++) dofMaps[i]->updateMatIndex(); } // === Update DOF admins due to new number of DOFs. === lastMeshChangeIndex = mesh->getChangeIndex(); #if (DEBUG != 0) ParallelDebug::testDofContainerCommunication(*this); MSG("------------- Debug information -------------\n"); MSG("| number of levels: %d\n", levelData.getLevelNumber()); MSG("| number of FE spaces: %d\n", feSpaces.size()); for (int i = 0; i < static_cast(dofMaps.size()); i++) { vector& dofMapSpaces = dofMaps[i]->getFeSpaces(); for (int j = 0; j < static_cast(dofMapSpaces.size()); j++) { const FiniteElemSpace *feSpace = dofMapSpaces[j]; MSG("| FE space = %d (pointer adr %p):\n", j, feSpace); MSG("| nRankDofs = %d\n", (*(dofMaps[i]))[feSpace].nRankDofs); MSG("| nOverallDofs = %d\n", (*(dofMaps[i]))[feSpace].nOverallDofs); MSG("| rStartDofs = %d\n", (*(dofMaps[i]))[feSpace].rStartDofs); } } // debug::writeElementIndexMesh(mesh, debugOutputDir + "elementIndex-" + // lexical_cast(mpiRank) + ".vtu"); ParallelDebug::writeDebugFile(feSpaces[feSpaces.size() - 1], *(dofMaps[0]), debugOutputDir + "mpi-dbg", "dat"); debug::testSortedDofs(mesh, elMap); int test = 0; Parameters::get("parallel->remove periodic boundary", test); if (test == 0) { ParallelDebug::testCommonDofs(*this, true); ParallelDebug::testGlobalIndexByCoords(*this); } #else for (int i = 0; i < static_cast(dofMaps.size()); i++) { vector& dofMapSpaces = dofMaps[i]->getFeSpaces(); for (int j = 0; j < static_cast(dofMapSpaces.size()); j++) MSG("FE space %d: nRankDofs = %d nOverallDofs = %d\n", j, (*(dofMaps[i]))[feSpaces[j]].nRankDofs, (*(dofMaps[i]))[feSpaces[j]].nOverallDofs); } int tmp = 0; Parameters::get(name + "->write parallel debug file", tmp); if (tmp) ParallelDebug::writeDebugFile(feSpaces[feSpaces.size() - 1], *(dofMaps[0]), debugOutputDir + "mpi-dbg", "dat"); #endif MPI::COMM_WORLD.Barrier(); MSG("Rebuild of parallel data structures needed %.5f seconds\n", MPI::Wtime() - first); } void MeshDistributor::updateParallelDofMappings() { FUNCNAME("MeshDistributor::updateParallelDofMappings()"); if (printTimings) MPI::COMM_WORLD.Barrier(); double first = MPI::Wtime(); TEST_EXIT(dofMaps.size())("No DOF mapping defined!\n"); for (int i = 0; i < static_cast(dofMaps.size()); i++) { vector& dofMapSpaces = dofMaps[i]->getFeSpaces(); dofMaps[i]->clear(); for (int j = 0; j < static_cast(dofMapSpaces.size()); j++) updateLocalGlobalNumbering(*(dofMaps[i]), dofMapSpaces[j]); dofMaps[i]->update(); #if (DEBUG != 0) // dofMaps[i]->printInfo(); #endif } if (printTimings) { MPI::COMM_WORLD.Barrier(); MSG("MESHDIST 02 (update DOF mappings) needed %.5f seconds\n", MPI::Wtime() - first); } } void MeshDistributor::updateLocalGlobalNumbering(ParallelDofMapping &dofMap, const FiniteElemSpace *feSpace) { FUNCNAME("MeshDistributor::updateLocalGlobalNumbering()"); DofComm &dcom = dofMap.getDofComm(); // === Get all DOFs in ranks partition. === std::set rankDofSet; mesh->getAllDofs(feSpace, rankDofSet); DofContainer rankDofs(rankDofSet.begin(), rankDofSet.end()); sort(rankDofs.begin(), rankDofs.end(), cmpDofsByValue); // === Traverse interior boundaries and get all DOFs on them. === DofContainerSet nonRankDofs; for (DofComm::Iterator it(dcom.getRecvDofs(), 0, feSpace); !it.end(); it.nextRank()) for (; !it.endDofIter(); it.nextDof()) nonRankDofs.insert(it.getDof()); for (DofContainer::iterator it = rankDofs.begin(); it != rankDofs.end(); ++it) { if (nonRankDofs.count(*it)) dofMap[feSpace].insertNonRankDof(**it); else dofMap[feSpace].insertRankDof(**it); } } void MeshDistributor::createPeriodicMap() { FUNCNAME("MeshDistributor::createPeriodicMap()"); // Clear all periodic DOF mappings calculated before. We do it from scratch. periodicMap.clear(); // If there are no periodic boundaries, return. if (!intBoundary.hasPeriodic()) return; TEST_EXIT(levelData.getLevelNumber() == 1) ("Periodic DOF map does not support multi level domain decomposition!\n"); // MPI::COMM_WORLD.Barrier(); [TODO: CHANGE BECAUSE NOT ALL RANKS HAVE PERIODIC MAP!!!] double first = MPI::Wtime(); for (unsigned int i = 0; i < feSpaces.size(); i++) createPeriodicMap(feSpaces[i]); // MPI::COMM_WORLD.Barrier(); MSG("Creation of periodic mapping needed %.5f seconds\n", MPI::Wtime() - first); } void MeshDistributor::createPeriodicMap(const FiniteElemSpace *feSpace) { FUNCNAME("MeshDistributor::createPeriodicMap()"); TEST_EXIT(dofMaps.size())("No DOF mapping defined!\n"); DofComm::LevelDataType &periodicDofs = dofComm.getPeriodicDofs(); ComponentDofMap &dofMap = (*(dofMaps[0]))[feSpace]; StdMpi > stdMpi(mpiComm, false); // === Each rank traverse its periodic boundaries and sends the DOF === // === indices to the rank "on the other side" of the periodic boundary. === map > rankToDofType; for (RankToBoundMap::iterator it = intBoundary.getPeriodic().begin(); it != intBoundary.getPeriodic().end(); ++it) { if (it->first == mpiRank) { // Here we have a periodic boundary within rank's subdomain. So we can // compute the periodic mappings without communication. TEST_EXIT_DBG(it->second.size() % 2 == 0)("Should not happen!\n"); for (unsigned int i = 0; i < it->second.size(); i++) { AtomicBoundary &bound = it->second[i]; TEST_EXIT_DBG(bound.rankObj.el)("No rank object!\n"); TEST_EXIT_DBG(bound.neighObj.el)("No neigh object!\n"); DofContainer dofs0, dofs1; bound.rankObj.el->getAllDofs(feSpace, bound.rankObj, dofs0); bound.neighObj.el->getAllDofs(feSpace, bound.neighObj, dofs1); TEST_EXIT_DBG(dofs0.size() == dofs1.size()) ("Number of DOFs does not fit together: %d %d\n", dofs0.size(), dofs1.size()); BoundaryType type = bound.type; for (unsigned int j = 0; j < dofs0.size(); j++) { DegreeOfFreedom globalDof0 = dofMap[*(dofs0[j])].global; DegreeOfFreedom globalDof1 = dofMap[*(dofs1[j])].global; if (!periodicMap.isPeriodicOnBound(feSpace, type, globalDof0)) periodicMap.add(feSpace, type, globalDof0, globalDof1); } } } else { // Here we have a periodic boundary between two ranks. // Create DOF indices on the boundary. DofContainer& dofs = periodicDofs[0][it->first][feSpace]; for (vector::iterator boundIt = it->second.begin(); boundIt != it->second.end(); ++boundIt) { int nDofs = dofs.size(); boundIt->rankObj.el->getAllDofs(feSpace, boundIt->rankObj, dofs); for (unsigned int i = 0; i < (dofs.size() - nDofs); i++) rankToDofType[it->first].push_back(boundIt->type); } // Send the global indices to the rank on the other side. stdMpi.getSendData(it->first).reserve(dofs.size()); for (unsigned int i = 0; i < dofs.size(); i++) stdMpi.getSendData(it->first).push_back(dofMap[*(dofs[i])].global); // Receive from this rank the same number of dofs. stdMpi.recv(it->first, dofs.size()); } } stdMpi.updateSendDataSize(); stdMpi.startCommunication(); // === The rank has received the DOFs from the rank on the other side of === // === the boundary. Now it can use them to create the mapping between === // === the periodic DOFs in this rank and the corresponding periodic === // === DOFs from the other ranks. === for (RankToBoundMap::iterator it = intBoundary.getPeriodic().begin(); it != intBoundary.getPeriodic().end(); ++it) { DofContainer& dofs = periodicDofs[0][it->first][feSpace]; vector& types = rankToDofType[it->first]; TEST_EXIT_DBG(dofs.size() == types.size())("Should not happen!\n"); // Added the received DOFs to the mapping. for (unsigned int i = 0; i < dofs.size(); i++) { int globalDofIndex = dofMap[*(dofs[i])].global; int mapGlobalDofIndex = stdMpi.getRecvData(it->first)[i]; BoundaryType type = types[i]; // Check if this global DOF with the corresponding boundary type was // not added before by another periodic boundary from other rank. if (!periodicMap.isPeriodicOnBound(feSpace, type, globalDofIndex)) periodicMap.add(feSpace, type, globalDofIndex, mapGlobalDofIndex); } } StdMpi stdMpi2(mpiComm); for (RankToBoundMap::iterator it = intBoundary.getPeriodic().begin(); it != intBoundary.getPeriodic().end(); ++it) { if (it->first == mpiRank) continue; PeriodicDofMap perObjMap; for (vector::iterator boundIt = it->second.begin(); boundIt != it->second.end(); ++boundIt) { // If the boundary type is a normal periodic BC, continue. We just // operate on indirectly connected periodic boundaries. if (elObjDb.isValidPeriodicType(boundIt->type)) continue; DofContainer dofs; boundIt->rankObj.el->getAllDofs(feSpace, boundIt->rankObj, dofs); for (unsigned int i = 0; i < dofs.size(); i++) { DegreeOfFreedom globalDof = dofMap[*dofs[i]].global; std::set& assoc = periodicMap.getAssociations(feSpace, globalDof); TEST_EXIT_DBG(assoc.size() > 0)("Should not happen!\n"); for (std::set::iterator perAscIt = assoc.begin(); perAscIt != assoc.end(); ++perAscIt) if (elObjDb.isValidPeriodicType(*perAscIt)) perObjMap[*perAscIt][globalDof] = periodicMap.map(feSpace, *perAscIt, globalDof); } } stdMpi2.send(it->first, perObjMap); stdMpi2.recv(it->first); } stdMpi2.startCommunication(); // NOTE: Before changing the data structure of periodic boundary DOFS, // at this place no periodicDofAssociations are set for the received // DOFs. I'm note sure what is correct here. for (map::iterator it = stdMpi2.getRecvData().begin(); it != stdMpi2.getRecvData().end(); ++it) periodicMap.add(feSpace, it->second); } void MeshDistributor::createMacroElementInfo() { FUNCNAME("MeshDistributor::createMacroElementInfo()"); for (deque::iterator it = mesh->firstMacroElement(); it != mesh->endOfMacroElements(); ++it) { allMacroElements.push_back(*it); int elIndex = (*it)->getIndex(); macroElementNeighbours[elIndex].resize(mesh->getGeo(NEIGH)); for (int i = 0; i < mesh->getGeo(NEIGH); i++) macroElementNeighbours[elIndex][i] = (*it)->getNeighbour(i) ? (*it)->getNeighbour(i)->getIndex() : -1; } } void MeshDistributor::updateMacroElementInfo() { deque& meshMacros = mesh->getMacroElements(); for (unsigned int i = 0; i < allMacroElements.size(); i++) { for (deque::iterator it = meshMacros.begin(); it != meshMacros.end(); ++it) { if ((*it)->getIndex() == allMacroElements[i]->getIndex() && *it != allMacroElements[i]) { delete allMacroElements[i]; allMacroElements[i] = *it; } } } } void MeshDistributor::serialize(ostream &out) { FUNCNAME("MeshDistributor::serialize()"); checkMeshChange(false); partitioner->serialize(out); SerUtil::serialize(out, elemWeights); SerUtil::serialize(out, partitionMap); elObjDb.serialize(out); intBoundary.serialize(out); // === Serialieze FE space dependent data === SerUtil::serialize(out, macroElementNeighbours); int nSize = allMacroElements.size(); SerUtil::serialize(out, nSize); for (int i = 0; i < nSize; i++) allMacroElements[i]->serialize(out); SerUtil::serialize(out, nMeshChangesAfterLastRepartitioning); SerUtil::serialize(out, repartitioningCounter); SerUtil::serialize(out, hasPeriodicBoundary); } void MeshDistributor::deserialize(istream &in) { FUNCNAME("MeshDistributor::deserialize()"); partitioner->deserialize(in); SerUtil::deserialize(in, elemWeights); SerUtil::deserialize(in, partitionMap); // Create a map from DOF indices to the corresponding DOF pointers. map > dofIndexMap; for (unsigned int i = 0; i < feSpaces.size(); i++) { ElementDofIterator elDofIter(feSpaces[i]); TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL); while (elInfo) { elDofIter.reset(elInfo->getElement()); do { dofIndexMap[feSpaces[i]][elDofIter.getDof()] = elDofIter.getDofPtr(); } while (elDofIter.next()); elInfo = stack.traverseNext(elInfo); } } elObjDb.deserialize(in); intBoundary.deserialize(in, mesh); // === Deerialieze FE space dependent data === SerUtil::deserialize(in, macroElementNeighbours); int nSize = 0; SerUtil::deserialize(in, nSize); allMacroElements.resize(nSize); for (int i = 0; i < nSize; i++) { // We do not need the neighbour indices, but must still read them. vector indices; allMacroElements[i] = new MacroElement(mesh->getDim()); allMacroElements[i]->writeNeighboursTo(&indices); allMacroElements[i]->deserialize(in); allMacroElements[i]->getElement()->setMesh(mesh); } SerUtil::deserialize(in, nMeshChangesAfterLastRepartitioning); SerUtil::deserialize(in, repartitioningCounter); SerUtil::deserialize(in, hasPeriodicBoundary); deserialized = true; } }