#include #include #include #include #include #include "parallel/ParallelDomainBase.h" #include "parallel/ParallelDomainDbg.h" #include "parallel/StdMpi.h" #include "ParMetisPartitioner.h" #include "Mesh.h" #include "Traverse.h" #include "ElInfo.h" #include "Element.h" #include "MacroElement.h" #include "PartitionElementData.h" #include "DOFMatrix.h" #include "DOFVector.h" #include "SystemVector.h" #include "VtkWriter.h" #include "ElementDofIterator.h" #include "ProblemStatBase.h" #include "StandardProblemIteration.h" #include "ElementFileWriter.h" #include "VertexVector.h" #include "MeshStructure.h" #include "ProblemVec.h" #include "ProblemInstat.h" #include "Debug.h" namespace AMDiS { using boost::lexical_cast; using namespace boost::filesystem; inline bool cmpDofsByValue(const DegreeOfFreedom* dof1, const DegreeOfFreedom* dof2) { return (*dof1 < *dof2); } MeshDistributor::MeshDistributor(std::string str) : probStat(0), name(str), feSpace(NULL), mesh(NULL), refineManager(NULL), info(10), partitioner(NULL), initialPartitionMesh(true), nRankDofs(0), nOverallDofs(0), rstart(0), deserialized(false), writeSerializationFile(false), lastMeshChangeIndex(0), macroElementStructureConsisten(false) { FUNCNAME("MeshDistributor::ParalleDomainBase()"); mpiRank = MPI::COMM_WORLD.Get_rank(); mpiSize = MPI::COMM_WORLD.Get_size(); mpiComm = MPI::COMM_WORLD; } void MeshDistributor::initParallelization(AdaptInfo *adaptInfo) { FUNCNAME("MeshDistributor::initParallelization()"); TEST_EXIT(mpiSize > 1) ("Parallelization does not work with only one process!\n"); TEST_EXIT(mesh)("No mesh has been defined for mesh distribution!\n"); // If the problem has been already read from a file, we need only to remove // periodic boundary conditions (if there are some). if (deserialized) { removePeriodicBoundaryConditions(); 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(); // create an initial partitioning of the mesh partitioner->createPartitionData(); // set the element weights, which are 1 at the very first begin setElemWeights(adaptInfo); // and now partition the mesh partitionMesh(adaptInfo); #if (DEBUG != 0) debug::ElementIdxToDofs elMap; debug::createSortedDofs(mesh, elMap); if (mpiRank == 0) { int writePartMesh = 1; GET_PARAMETER(0, "dbg->write part mesh", "%d", &writePartMesh); if (writePartMesh > 0) writePartitioningMesh("part.vtu"); else MSG("Skip write part mesh!\n"); } ParallelDomainDbg::testAllElements(*this); #endif // === Create interior boundary information. === createInteriorBoundaryInfo(); #if (DEBUG != 0) ParallelDomainDbg::printBoundaryInfo(*this); #endif // === Create new global and local DOF numbering. === createLocalGlobalNumbering(); // === Remove all macro elements that are not part of the rank partition. === removeMacroElements(); macroElementStructureConsisten = true; // === Reset all DOFAdmins of the mesh. === updateDofAdmins(); // === If in debug mode, make some tests. === #if (DEBUG != 0) MSG("AMDiS runs in debug mode, so make some test ...\n"); debug::testSortedDofs(mesh, elMap); ParallelDomainDbg::testInteriorBoundary(*this); ParallelDomainDbg::testCommonDofs(*this, true); MSG("Debug mode tests finished!\n"); debug::writeMesh(feSpace, -1, "macro_mesh"); #endif // === Create periodic dof mapping, if there are periodic boundaries. === createPeriodicMap(); // === Global refinements. === int globalRefinement = 0; GET_PARAMETER(0, mesh->getName() + "->global refinements", "%d", &globalRefinement); if (globalRefinement > 0) { refineManager->globalRefine(mesh, globalRefinement); #if (DEBUG != 0) debug::writeMesh(feSpace, -1, "gr_mesh"); #endif updateLocalGlobalNumbering(); // === Update periodic mapping, if there are periodic boundaries. === createPeriodicMap(); } /// === Set DOF rank information to all matrices and vectors. === for (unsigned int i = 0; i < probStat.size(); i++) { int nComponents = probStat[i]->getNumComponents(); for (int j = 0; j < nComponents; j++) { for (int k = 0; k < nComponents; k++) if (probStat[i]->getSystemMatrix(j, k)) probStat[i]->getSystemMatrix(j, k)->setRankDofs(isRankDof); TEST_EXIT_DBG(probStat[i]->getRhs()->getDOFVector(j))("No RHS vector!\n"); TEST_EXIT_DBG(probStat[i]->getSolution()->getDOFVector(j))("No solution vector!\n"); probStat[i]->getRhs()->getDOFVector(j)->setRankDofs(isRankDof); probStat[i]->getSolution()->getDOFVector(j)->setRankDofs(isRankDof); } } // === Remove periodic boundary conditions in sequential problem definition. === removePeriodicBoundaryConditions(); } void MeshDistributor::addProblemStat(ProblemVec *probVec) { FUNCNAME("MeshDistributor::addProblemVec()"); if (feSpace != NULL) { std::vector feSpaces = probVec->getFeSpaces(); for (unsigned int i = 0; i < feSpaces.size(); i++) { TEST_EXIT(feSpace == feSpaces[i]) ("Parallelizaton is not supported for multiple FE spaces!\n"); } } else { feSpace = probVec->getFeSpace(0); mesh = feSpace->getMesh(); info = probVec->getInfo(); TEST_EXIT(mesh->getNumberOfDOFAdmin() == 1) ("Only meshes with one DOFAdmin are supported!\n"); TEST_EXIT(mesh->getDOFAdmin(0).getNumberOfPreDOFs(0) == 0) ("Wrong pre dof number for DOFAdmin!\n"); 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 = new ParMetisPartitioner(mesh, &mpiComm); } // Create parallel serialization file writer, if needed. int writeSerialization = 0; GET_PARAMETER(0, probVec->getName() + "->output->write serialization", "%d", &writeSerialization); if (writeSerialization && !writeSerializationFile) { std::string f = ""; GET_PARAMETER(0, probVec->getName() + "->output->serialization filename", &f); path myPath(f); std::string meshFilename = myPath.parent_path().directory_string() + "/meshDistributor.ser"; int tsModulo = -1; GET_PARAMETER(0, probVec->getName() + "->output->write every i-th timestep", "%d", &tsModulo); probVec->getFileWriterList().push_back(new Serializer(this, meshFilename, tsModulo)); writeSerializationFile = true; } int readSerialization = 0; GET_PARAMETER(0, probVec->getName() + "->input->read serialization", "%d", &readSerialization); if (readSerialization) { std::string filename = ""; GET_PARAMETER(0, probVec->getName() + "->input->serialization filename", &filename); filename += ".p" + lexical_cast(mpiRank); MSG("Start deserialization with %s\n", filename.c_str()); std::ifstream in(filename.c_str()); probVec->deserialize(in); in.close(); MSG("Deserialization from file: %s\n", filename.c_str()); if (!deserialized) { GET_PARAMETER(0, probVec->getName() + "->input->serialization filename", &filename); path myPath(filename); std::string meshFilename = myPath.parent_path().directory_string() + "/meshDistributor.ser.p" + lexical_cast(mpiRank); std::ifstream in(meshFilename.c_str()); deserialize(in); in.close(); MSG("Deserializtion of mesh distributor from file: %s\n", meshFilename.c_str()); deserialized = true; } } probStat.push_back(probVec); } void MeshDistributor::exitParallelization(AdaptInfo *adaptInfo) {} void MeshDistributor::updateDofAdmins() { FUNCNAME("MeshDistributor::updateDofAdmins()"); for (int i = 0; i < mesh->getNumberOfDOFAdmin(); i++) { DOFAdmin& admin = const_cast(mesh->getDOFAdmin(i)); // There must be always more allocated DOFs than used DOFs in DOFAdmin. Otherwise, // it is not possible to define a first DOF hole. if (static_cast(admin.getSize()) == mapLocalGlobalDofs.size()) admin.enlargeDOFLists(); for (int j = 0; j < admin.getSize(); j++) admin.setDOFFree(j, true); for (unsigned int j = 0; j < mapLocalGlobalDofs.size(); j++) admin.setDOFFree(j, false); admin.setUsedSize(mapLocalGlobalDofs.size()); admin.setUsedCount(mapLocalGlobalDofs.size()); admin.setFirstHole(mapLocalGlobalDofs.size()); } } 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"); nMacroElements++; elInfo = stack.traverseNext(elInfo); } TEST_EXIT(nMacroElements >= mpiSize) ("The mesh has less macro elements than number of mpi processes!\n"); } void MeshDistributor::synchVector(DOFVector &vec) { StdMpi > stdMpi(mpiComm); for (RankToDofContainer::iterator sendIt = sendDofs.begin(); sendIt != sendDofs.end(); ++sendIt) { std::vector dofs; int nSendDofs = sendIt->second.size(); dofs.reserve(nSendDofs); for (int i = 0; i < nSendDofs; i++) dofs.push_back(vec[*((sendIt->second)[i])]); stdMpi.send(sendIt->first, dofs); } for (RankToDofContainer::iterator recvIt = recvDofs.begin(); recvIt != recvDofs.end(); ++recvIt) stdMpi.recv(recvIt->first, recvIt->second.size()); stdMpi.startCommunication(MPI_DOUBLE); for (RankToDofContainer::iterator recvIt = recvDofs.begin(); recvIt != recvDofs.end(); ++recvIt) for (unsigned int i = 0; i < recvIt->second.size(); i++) vec[*(recvIt->second)[i]] = stdMpi.getRecvData(recvIt->first)[i]; } void MeshDistributor::synchVector(SystemVector &vec) { int nComponents = vec.getSize(); StdMpi > stdMpi(mpiComm); for (RankToDofContainer::iterator sendIt = sendDofs.begin(); sendIt != sendDofs.end(); ++sendIt) { std::vector dofs; int nSendDofs = sendIt->second.size(); dofs.reserve(nComponents * nSendDofs); for (int i = 0; i < nComponents; i++) { DOFVector *dofvec = vec.getDOFVector(i); for (int j = 0; j < nSendDofs; j++) dofs.push_back((*dofvec)[*((sendIt->second)[j])]); } stdMpi.send(sendIt->first, dofs); } for (RankToDofContainer::iterator recvIt = recvDofs.begin(); recvIt != recvDofs.end(); ++recvIt) stdMpi.recv(recvIt->first, recvIt->second.size() * nComponents); stdMpi.startCommunication(MPI_DOUBLE); for (RankToDofContainer::iterator recvIt = recvDofs.begin(); recvIt != recvDofs.end(); ++recvIt) { int nRecvDofs = recvIt->second.size(); int counter = 0; for (int i = 0; i < nComponents; i++) { DOFVector *dofvec = vec.getDOFVector(i); for (int j = 0; j < nRecvDofs; j++) (*dofvec)[*(recvIt->second)[j]] = stdMpi.getRecvData(recvIt->first)[counter++]; } } } void MeshDistributor::removePeriodicBoundaryConditions() { // Remove periodic boundaries in boundary manager on matrices and vectors. for (unsigned int i = 0; i < probStat.size(); i++) { int nComponents = probStat[i]->getNumComponents(); for (int j = 0; j < nComponents; j++) { for (int k = 0; k < nComponents; k++) { DOFMatrix* mat = probStat[i]->getSystemMatrix(j, k); if (mat && mat->getBoundaryManager()) removePeriodicBoundaryConditions(const_cast(mat->getBoundaryManager()->getBoundaryConditionMap())); } if (probStat[i]->getSolution()->getDOFVector(j)->getBoundaryManager()) removePeriodicBoundaryConditions(const_cast(probStat[i]->getSolution()->getDOFVector(j)->getBoundaryManager()->getBoundaryConditionMap())); if (probStat[i]->getRhs()->getDOFVector(j)->getBoundaryManager()) removePeriodicBoundaryConditions(const_cast(probStat[i]->getRhs()->getDOFVector(j)->getBoundaryManager()->getBoundaryConditionMap())); } } // 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); } } void MeshDistributor::removePeriodicBoundaryConditions(BoundaryIndexMap& boundaryMap) { BoundaryIndexMap::iterator it = boundaryMap.begin(); while (it != boundaryMap.end()) { if (it->second->isPeriodic()) boundaryMap.erase(it++); else ++it; } } void MeshDistributor::checkMeshChange() { FUNCNAME("MeshDistributor::checkMeshChange()"); #if (DEBUG != 0) debug::writeMesh(feSpace, -1, "before_check_mesh"); #endif // === 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. === clock_t first = clock(); do { // 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(myIntBoundary); !it.end(); ++it) if (it->rankObj.subObj == EDGE || it->rankObj.subObj == FACE) allBound[it.getRank()].push_back(*it); for (InteriorBoundary::iterator it(otherIntBoundary); !it.end(); ++it) if (it->rankObj.subObj == EDGE || it->rankObj.subObj == FACE) allBound[it.getRank()].push_back(*it); for (InteriorBoundary::iterator it(periodicBoundary); !it.end(); ++it) if (it->rankObj.subObj == EDGE || it->rankObj.subObj == FACE) allBound[it.getRank()].push_back(*it); // === Check the boundaries and adapt mesh if necessary. === #if (DEBUG != 0) MSG("Run checkAndAdaptBoundary ...\n"); #endif bool 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); #if (DEBUG != 0) MSG("Mesh changed on %d ranks!\n", recvAllValues); #endif } while (recvAllValues != 0); #if (DEBUG != 0) debug::writeMesh(feSpace, -1, "mesh"); #endif INFO(info, 8)("Parallel mesh adaption needed %.5f seconds\n", TIME_USED(first, clock())); // === Because the mesh has been changed, update the DOF numbering and mappings. === mesh->dofCompress(); updateLocalGlobalNumbering(); // === Update periodic mapping, if there are periodic boundaries. === createPeriodicMap(); } bool MeshDistributor::checkAndAdaptBoundary(RankToBoundMap &allBound) { FUNCNAME("MeshDistributor::checkAndAdaptBoundary()"); // === Create mesh structure codes for all ranks boundary elements. === std::map sendCodes; for (RankToBoundMap::iterator it = allBound.begin(); it != allBound.end(); ++it) { for (std::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); stdMpi.recv(allBound); stdMpi.startCommunication(MPI_UNSIGNED_LONG); // === Compare received mesh structure codes. === bool meshFitTogether = true; for (RankToBoundMap::iterator it = allBound.begin(); it != allBound.end(); ++it) { MeshCodeVec &recvCodes = stdMpi.getRecvData()[it->first]; int i = 0; for (std::vector::iterator boundIt = it->second.begin(); boundIt != it->second.end(); ++boundIt) { MeshStructure elCode; elCode.init(boundIt->rankObj); if (elCode.getCode() != recvCodes[i].getCode()) { TEST_EXIT_DBG(refineManager)("Refinement manager is not set correctly!\n"); bool b = fitElementToMeshCode(recvCodes[i], boundIt->rankObj.el, boundIt->rankObj.subObj, boundIt->rankObj.ithObj, boundIt->rankObj.elType, boundIt->rankObj.reverseMode); if (b) meshFitTogether = false; } i++; } } return meshFitTogether; } bool MeshDistributor::fitElementToMeshCode(MeshStructure &code, Element *el, GeoIndex subObj, int ithObj, int elType, bool reverseMode) { FUNCNAME("MeshDistributor::fitElementToMeshCode()"); TEST_EXIT_DBG(el)("No element given!\n"); if (code.empty()) return false; int s1 = el->getSubObjOfChild(0, subObj, ithObj, elType); int s2 = el->getSubObjOfChild(1, subObj, ithObj, elType); TEST_EXIT_DBG(s1 != -1 || s2 != -1)("This should not happen!\n"); bool meshChanged = false; Flag traverseFlag = Mesh::CALL_EVERY_EL_PREORDER | Mesh::FILL_NEIGH | Mesh::FILL_BOUND; if (reverseMode) { std::swap(s1, s2); traverseFlag |= Mesh::CALL_REVERSE_MODE; } if (s1 != -1 && s2 != -1) { TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(el->getMesh(), -1, traverseFlag); while (elInfo && elInfo->getElement() != el) elInfo = stack.traverseNext(elInfo); meshChanged = fitElementToMeshCode2(code, stack, subObj, ithObj, elType, reverseMode); return meshChanged; } if (el->isLeaf()) { if (code.getNumElements() == 1 && code.isLeafElement()) return false; meshChanged = true; TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(el->getMesh(), -1, traverseFlag); while (elInfo && elInfo->getElement() != el) { elInfo = stack.traverseNext(elInfo); } TEST_EXIT_DBG(elInfo)("This should not happen!\n"); el->setMark(1); refineManager->setMesh(el->getMesh()); refineManager->setStack(&stack); refineManager->refineFunction(elInfo); } Element *child0 = el->getFirstChild(); Element *child1 = el->getSecondChild(); if (reverseMode) std::swap(child0, child1); if (s1 != -1) { TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(el->getMesh(), -1, traverseFlag); while (elInfo && elInfo->getElement() != child0) { elInfo = stack.traverseNext(elInfo); } meshChanged |= fitElementToMeshCode2(code, stack, subObj, s1, el->getChildType(elType), reverseMode); } else { TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(el->getMesh(), -1, traverseFlag); while (elInfo && elInfo->getElement() != child1) { elInfo = stack.traverseNext(elInfo); } meshChanged |= fitElementToMeshCode2(code, stack, subObj, s2, el->getChildType(elType), reverseMode); } return meshChanged; } bool MeshDistributor::fitElementToMeshCode2(MeshStructure &code, TraverseStack &stack, GeoIndex subObj, int ithObj, int elType, bool reverseMode) { FUNCNAME("MeshDistributor::fitElementToMeshCode2()"); ElInfo *elInfo = stack.getElInfo(); bool value = false; if (!elInfo) return value; Element *el = elInfo->getElement(); if (code.isLeafElement()) { int level = elInfo->getLevel(); do { elInfo = stack.traverseNext(elInfo); } while (elInfo && elInfo->getLevel() > level); return value; } if (!elInfo) return value; if (el->isLeaf()) { el->setMark(1); refineManager->setMesh(el->getMesh()); refineManager->setStack(&stack); refineManager->refineFunction(elInfo); value = true; } int s1 = el->getSubObjOfChild(0, subObj, ithObj, elType); int s2 = el->getSubObjOfChild(1, subObj, ithObj, elType); Element *child0 = el->getFirstChild(); Element *child1 = el->getSecondChild(); if (reverseMode) { std::swap(s1, s2); std::swap(child0, child1); } if (s1 != -1) { stack.traverseNext(elInfo); code.nextElement(); value |= fitElementToMeshCode2(code, stack, subObj, s1, el->getChildType(elType), reverseMode); elInfo = stack.getElInfo(); } else { do { elInfo = stack.traverseNext(elInfo); } while (elInfo && elInfo->getElement() != child1); } TEST_EXIT_DBG(elInfo->getElement() == child1)("This should not happen!\n"); if (s2 != -1) { code.nextElement(); value |= fitElementToMeshCode2(code, stack, subObj, s2, el->getChildType(elType), reverseMode); } else { int level = elInfo->getLevel(); do { elInfo = stack.traverseNext(elInfo); } while (elInfo && elInfo->getLevel() > level); } return value; } void MeshDistributor::serialize(std::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::deserialize(std::istream &in, DofContainer &data, std::map &dofMap) { FUNCNAME("MeshDistributor::deserialize()"); int vecSize = 0; SerUtil::deserialize(in, vecSize); data.resize(vecSize); for (int i = 0; i < vecSize; i++) { int dofIndex = 0; SerUtil::deserialize(in, dofIndex); TEST_EXIT_DBG(dofMap.count(dofIndex) != 0) ("Dof index could not be deserialized correctly!\n"); data[i] = dofMap[dofIndex]; } } void MeshDistributor::serialize(std::ostream &out, RankToDofContainer &data) { int mapSize = data.size(); SerUtil::serialize(out, mapSize); for (RankToDofContainer::iterator it = data.begin(); it != data.end(); ++it) { int rank = it->first; SerUtil::serialize(out, rank); serialize(out, it->second); } } void MeshDistributor::deserialize(std::istream &in, RankToDofContainer &data, std::map &dofMap) { int mapSize = 0; SerUtil::deserialize(in, mapSize); for (int i = 0; i < mapSize; i++) { int rank = 0; SerUtil::deserialize(in, rank); deserialize(in, data[rank], dofMap); } } double MeshDistributor::setElemWeights(AdaptInfo *adaptInfo) { FUNCNAME("MeshDistributor::setElemWeights()"); double localWeightSum = 0.0; elemWeights.clear(); std::string filename = ""; GET_PARAMETER(0, mesh->getName() + "->macro weights", &filename); if (filename != "") { MSG("Read macro weights from %s\n", filename.c_str()); std::ifstream infile; infile.open(filename.c_str(), std::ifstream::in); while (!infile.eof()) { int elNum, elWeight; infile >> elNum; if (infile.eof()) break; infile >> elWeight; elemWeights[elNum] = elWeight; localWeightSum += elWeight; } infile.close(); } else { TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL); while (elInfo) { elemWeights[elInfo->getElement()->getIndex()] = 1.0; localWeightSum++; elInfo = stack.traverseNext(elInfo); } } return localWeightSum; } void MeshDistributor::partitionMesh(AdaptInfo *adaptInfo) { FUNCNAME("MeshDistributor::partitionMesh()"); if (initialPartitionMesh) { initialPartitionMesh = false; partitioner->fillCoarsePartitionVec(&oldPartitionVec); partitioner->partition(&elemWeights, INITIAL); } else { oldPartitionVec = partitionVec; partitioner->partition(&elemWeights, ADAPTIVE_REPART, 100.0 /*0.000001*/); } partitioner->fillCoarsePartitionVec(&partitionVec); } void MeshDistributor::createInteriorBoundaryInfo() { FUNCNAME("MeshDistributor::createInteriorBoundaryInfo()"); // === First, create the interior boundaries based on macro element's === // === neighbour informations. === createMacroElementInteriorBoundaryInfo(); // === Second, search the whole mesh for interior boundaries that consists of === // === substructures of the macro elements. === createSubstructureInteriorBoundaryInfo(); // === Once we have this information, we must care about the order of the atomic === // === bounds in the three boundary handling object. Eventually all the bound- === // === aries have to be in the same order on both ranks that share the bounday. === StdMpi > stdMpi(mpiComm); stdMpi.send(myIntBoundary.boundary); stdMpi.recv(otherIntBoundary.boundary); stdMpi.startCommunication(MPI_INT); // === The information about all neighbouring boundaries has been received. So === // === the rank tests if its own atomic boundaries are in the same order. If === // === not, the atomic boundaries are swaped to the correct order. === for (RankToBoundMap::iterator rankIt = otherIntBoundary.boundary.begin(); rankIt != otherIntBoundary.boundary.end(); ++rankIt) { // === We have received from rank "rankIt->first" the ordered list of element === // === indices. Now, we have to sort the corresponding list in this rank to === // === get the same order. === for (int j = 0; j < static_cast(rankIt->second.size()); j++) { // If the expected object is not at place, search for it. BoundaryObject &recvedBound = stdMpi.getRecvData()[rankIt->first][j].rankObj; if ((rankIt->second)[j].neighObj != recvedBound) { int k = j + 1; for (; k < static_cast(rankIt->second.size()); k++) if ((rankIt->second)[k].neighObj == recvedBound) break; // The element must always be found, because the list is just in another order. TEST_EXIT_DBG(k < static_cast(rankIt->second.size())) ("Should never happen!\n"); // Swap the current with the found element. AtomicBoundary tmpBound = (rankIt->second)[k]; (rankIt->second)[k] = (rankIt->second)[j]; (rankIt->second)[j] = tmpBound; } } } // === Do the same for the periodic boundaries. === if (periodicBoundary.boundary.size() > 0) { stdMpi.clear(); InteriorBoundary sendBounds, recvBounds; for (RankToBoundMap::iterator rankIt = periodicBoundary.boundary.begin(); rankIt != periodicBoundary.boundary.end(); ++rankIt) { TEST_EXIT_DBG(rankIt->first != mpiRank) ("It is no possible to have an interior boundary within a rank partition!\n"); if (rankIt->first < mpiRank) sendBounds.boundary[rankIt->first] = rankIt->second; else recvBounds.boundary[rankIt->first] = rankIt->second; } stdMpi.send(sendBounds.boundary); stdMpi.recv(recvBounds.boundary); stdMpi.startCommunication(MPI_INT); for (RankToBoundMap::iterator rankIt = periodicBoundary.boundary.begin(); rankIt != periodicBoundary.boundary.end(); ++rankIt) { if (rankIt->first <= mpiRank) continue; for (int j = 0; j < static_cast(rankIt->second.size()); j++) { BoundaryObject &recvedBound = stdMpi.getRecvData()[rankIt->first][j].rankObj; if (periodicBoundary.boundary[rankIt->first][j].neighObj != recvedBound) { int k = j + 1; for (; k < static_cast(rankIt->second.size()); k++) if (periodicBoundary.boundary[rankIt->first][k].neighObj == recvedBound) break; // The element must always be found, because the list is just in // another order. TEST_EXIT_DBG(k < static_cast(rankIt->second.size())) ("Should never happen!\n"); // Swap the current with the found element. AtomicBoundary tmpBound = (rankIt->second)[k]; (rankIt->second)[k] = (rankIt->second)[j]; (rankIt->second)[j] = tmpBound; } } } } // periodicBoundary.boundary.size() > 0 } void MeshDistributor::createMacroElementInteriorBoundaryInfo() { FUNCNAME("MeshDistributor::createMacroElementInteriorBoundaryInfo()"); int nNeighbours = mesh->getGeo(NEIGH); int dim = mesh->getDim(); GeoIndex subObj = INDEX_OF_DIM(dim - 1, dim); // === First, traverse the mesh and search for all elements that have an === // === boundary with an element within another partition. === TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL | Mesh::FILL_NEIGH | Mesh::FILL_BOUND); while (elInfo) { Element *element = elInfo->getElement(); PartitionElementData *partitionData = dynamic_cast(element->getElementData(PARTITION_ED)); // Check, if the element is within rank's partition. if (partitionData->getPartitionStatus() == IN) { for (int i = 0; i < nNeighbours; i++) { if (!elInfo->getNeighbour(i)) continue; PartitionElementData *neighbourPartitionData = dynamic_cast(elInfo->getNeighbour(i)-> getElementData(PARTITION_ED)); if (neighbourPartitionData->getPartitionStatus() == OUT) { // We have found an element that is in rank's partition, but has a // neighbour outside of the rank's partition. // === Create information about the boundary between the two elements. === AtomicBoundary bound; bound.rankObj.el = element; bound.rankObj.elIndex = element->getIndex(); bound.rankObj.elType = elInfo->getType(); bound.rankObj.subObj = subObj; bound.rankObj.ithObj = i; bound.neighObj.el = elInfo->getNeighbour(i); bound.neighObj.elIndex = elInfo->getNeighbour(i)->getIndex(); bound.neighObj.elType = -1; bound.neighObj.subObj = subObj; bound.neighObj.ithObj = elInfo->getSideOfNeighbour(i); // Get rank number of the neighbouring element. int otherElementRank = partitionVec[elInfo->getNeighbour(i)->getIndex()]; // === Add the boundary information object to the corresponding overall === // === boundary. There are three possibilities: if the boundary is a === // === periodic boundary, just add it to \ref periodicBounadry. Here it === // === does not matter which rank is responsible for this boundary. === // === Otherwise, the boundary is added either to \ref myIntBoundary or === // === to \ref otherIntBoundary. It dependes on which rank is respon- === // === sible for this boundary. === if (BoundaryManager::isBoundaryPeriodic(elInfo->getBoundary(subObj, i))) { // We have found an element that is at an interior, periodic boundary. AtomicBoundary& b = periodicBoundary.getNewAtomic(otherElementRank); b = bound; if (mpiRank > otherElementRank) b.rankObj.setReverseMode(b.neighObj, feSpace); else b.neighObj.setReverseMode(b.rankObj, feSpace); } else { // We have found an element that is at an interior, non-periodic boundary. if (mpiRank > otherElementRank) { AtomicBoundary& b = myIntBoundary.getNewAtomic(otherElementRank); b = bound; b.rankObj.setReverseMode(b.neighObj, feSpace); } else { AtomicBoundary& b = otherIntBoundary.getNewAtomic(otherElementRank); b = bound; b.neighObj.setReverseMode(b.rankObj, feSpace); } } } } } elInfo = stack.traverseNext(elInfo); } } void MeshDistributor::createSubstructureInteriorBoundaryInfo() { FUNCNAME("MeshDistributor::createSubstructureInteriorBoundaryInfo()"); // === Seach for all vertices/edges, which are part of an interior boundary, === // === but are not part of the interior boundaries that are created based on === // === the information of macro elements. === int dim = mesh->getDim(); const BasisFunction *basFcts = feSpace->getBasisFcts(); std::vector localIndices(basFcts->getNumber()); // Maps each DOF in the whole domain to the rank that ownes this DOF. std::map dofOwner; // Maps each DOF in ranks partition to the element object that contains this DOF. std::map rankDofs; // Maps each edge in the whole domain to the rank that ownes this edge. std::map edgeOwner; // Maps each edge in ranks partition to the element object that contains this edge. std::map rankEdges; // Stores to every element index a pointer to this element. std::map elIndexMap; // === Traverse whole mesh and fill the maps defined above. === TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL); while (elInfo) { Element *el = elInfo->getElement(); elIndexMap[el->getIndex()] = el; basFcts->getLocalIndices(el, feSpace->getAdmin(), localIndices); PartitionElementData *partitionData = dynamic_cast (el->getElementData(PARTITION_ED)); // In 2d and 3d, traverse all vertices of current element. for (int i = 0; i < mesh->getGeo(VERTEX); i++) { DegreeOfFreedom dof0 = localIndices[i]; // Update the owner of the current vertex DOF. dofOwner[dof0] = max(dofOwner[dof0], partitionVec[el->getIndex()]); // If the DOF is part of an element that is within ranks partition, add it // to the set of all ranks vertex DOFs. if (partitionData && partitionData->getPartitionStatus() == IN) rankDofs[dof0] = BoundaryObject(el, elInfo->getType(), VERTEX, i); } // In 3d, traverse all edges of current element. if (dim == 3) { for (int i = 0; i < 6; i++) { DegreeOfFreedom dof0 = localIndices[el->getVertexOfEdge(i, 0)]; DegreeOfFreedom dof1 = localIndices[el->getVertexOfEdge(i, 1)]; GlobalEdge edge = std::make_pair(min(dof0, dof1), max(dof0, dof1)); // Update the owner of the current edge. edgeOwner[edge] = max(edgeOwner[edge], partitionVec[el->getIndex()]); // If the edge is part of an element that is within ranks partition, add it // to the set of all ranks edges. if (partitionData && partitionData->getPartitionStatus() == IN) rankEdges[edge] = BoundaryObject(el, elInfo->getType(), EDGE, i); } } elInfo = stack.traverseNext(elInfo); } // === Create a set of all edges and vertices at ranks interior boundaries. === // Stores all edges at rank's interior boundaries. std::set rankBoundaryEdges; // Stores all vertices at rank's interior boundaries. std::set rankBoundaryDofs; // First, traverse the rank owned elements at the interior boundaries. for (InteriorBoundary::iterator it(myIntBoundary); !it.end(); ++it) { Element *el = it->rankObj.el; basFcts->getLocalIndices(el, feSpace->getAdmin(), localIndices); for (int i = 0; i < mesh->getGeo(VERTEX); i++) { int dofNo = el->getVertexOfPosition(it->rankObj.subObj, it->rankObj.ithObj, i); DegreeOfFreedom dof = localIndices[dofNo]; if (dofOwner[dof] > mpiRank) it->rankObj.excludedSubstructures.push_back(std::make_pair(VERTEX, dofNo)); else rankBoundaryDofs.insert(dof); } if (dim == 3) { for (int i = 0; i < 3; i++) { int edgeNo = el->getEdgeOfFace(it->rankObj.ithObj, i); DegreeOfFreedom dof0 = localIndices[el->getVertexOfEdge(edgeNo, 0)]; DegreeOfFreedom dof1 = localIndices[el->getVertexOfEdge(edgeNo, 1)]; GlobalEdge edge = std::make_pair(min(dof0, dof1), max(dof0, dof1)); // If the edge at rank's interior boundarie has a higher owner rank, than // we have to remove this edge from the corresponding boundary element. // Otherwise, it is part of the interior boundary and we add it to the set // rankBoundaryEdges. if (edgeOwner[edge] > mpiRank) it->rankObj.excludedSubstructures.push_back(std::make_pair(EDGE, edgeNo)); else rankBoundaryEdges.insert(edge); } } } // Now, do the same with all other elements at the interior boundaries. for (InteriorBoundary::iterator it(otherIntBoundary); !it.end(); ++it) { Element *el = it->rankObj.el; basFcts->getLocalIndices(el, feSpace->getAdmin(), localIndices); for (int i = 0; i < mesh->getGeo(VERTEX); i++) { int dofNo = el->getVertexOfPosition(it->rankObj.subObj, it->rankObj.ithObj, i); DegreeOfFreedom dof = localIndices[dofNo]; if (dofOwner[dof] > it.getRank()) it->rankObj.excludedSubstructures.push_back(std::make_pair(VERTEX, dofNo)); else rankBoundaryDofs.insert(dof); } if (dim == 3) { for (int i = 0; i < 3; i++) { int edgeNo = el->getEdgeOfFace(it->rankObj.ithObj, i); DegreeOfFreedom dof0 = localIndices[el->getVertexOfEdge(edgeNo, 0)]; DegreeOfFreedom dof1 = localIndices[el->getVertexOfEdge(edgeNo, 1)]; GlobalEdge edge = std::make_pair(min(dof0, dof1), max(dof0, dof1)); if (edgeOwner[edge] > it.getRank()) it->rankObj.excludedSubstructures.push_back(std::make_pair(EDGE, edgeNo)); else rankBoundaryEdges.insert(edge); } } } // === Create the new interior boundaries consisting only of edges. These === // === boundaries are created on that ranks, which do not own the boundary === // === but are on the other side of the edge. Then, these ranks inform the === // === owner of the edge. === // Maps that stores to each rank number the global edges this rank has an // interior boundary with. std::map > recvEdges; // Stores to the map above the corresponding element objects that include the edge. std::map > recvObjs; for (int i = 0; i < mpiSize; i++) { recvEdges[i].clear(); recvObjs[i].clear(); } // Traverse all edges in ranks domain. for (std::map::iterator it = rankEdges.begin(); it != rankEdges.end(); ++it) { // If we have found an edge in ranks domain that has an owner with a higher // rank number, i.e., it must be part of an interior domain, but have not found // it before to be part of an interior domain, we have found an edge interior // boundary. if (edgeOwner[it->first] > mpiRank && rankBoundaryEdges.count(it->first) == 0) { std::vector &ownerEdges = recvEdges[edgeOwner[it->first]]; // Check, we have not add this edge before. if (find(ownerEdges.begin(), ownerEdges.end(), it->first) == ownerEdges.end()) { ownerEdges.push_back(it->first); recvObjs[edgeOwner[it->first]].push_back(it->second); rankBoundaryDofs.insert(it->first.first); rankBoundaryDofs.insert(it->first.second); } } } // === Send all edge interior boundary infos to the owner of the new edge === // === interior boundaries. === StdMpi > stdMpiEdge(mpiComm, true); stdMpiEdge.send(recvEdges); stdMpiEdge.recvFromAll(); stdMpiEdge.startCommunication(MPI_INT); StdMpi > stdMpiObjs(mpiComm, true); stdMpiObjs.send(recvObjs); stdMpiObjs.recvFromAll(); stdMpiObjs.startCommunication(MPI_INT); // The owner of an edge interior boundary must send back information about the // mesh element that stores the edge at the owners domain. These information are // stored in this map. std::map > sendObjects; for (int i = 0; i < mpiSize; i++) sendObjects[i].clear(); // === All owner of a new edge interior boundary will create this now from the === // === received data. === for (std::map >::iterator it = stdMpiEdge.getRecvData().begin(); it != stdMpiEdge.getRecvData().end(); ++it) { for (unsigned int i = 0; i < it->second.size(); i++) { TEST_EXIT_DBG(stdMpiObjs.getRecvData(it->first).size() > i) ("This should not happen!\n"); AtomicBoundary& b = myIntBoundary.getNewAtomic(it->first); b.rankObj = rankEdges[it->second[i]]; b.neighObj = stdMpiObjs.getRecvData(it->first)[i]; b.neighObj.el = elIndexMap[b.neighObj.elIndex]; b.rankObj.setReverseMode(b.neighObj, feSpace); sendObjects[it->first].push_back(b.rankObj); rankBoundaryDofs.insert(it->second[i].first); rankBoundaryDofs.insert(it->second[i].second); } } // === Send the information about the elements on the owners side of the new === // === edge interior boundaries. === stdMpiObjs.clear(); stdMpiObjs.send(sendObjects); stdMpiObjs.recvFromAll(); stdMpiObjs.startCommunication(MPI_INT); // === To the last, all non-owners of a edge interior boundary will now create === // === the necessary boundary objects. === for (std::map >::iterator it = recvObjs.begin(); it != recvObjs.end(); ++it) { if (it->second.size() > 0) { TEST_EXIT_DBG(it->second.size() == stdMpiObjs.getRecvData(it->first).size()) ("This should not happen!\n"); for (unsigned int i = 0; i < it->second.size(); i++) { AtomicBoundary& b = otherIntBoundary.getNewAtomic(it->first); b.rankObj = it->second[i]; b.neighObj = stdMpiObjs.getRecvData(it->first)[i]; b.neighObj.el = elIndexMap[b.neighObj.elIndex]; b.neighObj.setReverseMode(b.rankObj, feSpace); } } } // === Create the new interior boundaries consisting only of vertices. As done === // === above for edge interior boundaries, also the vertex interior boundaries === // === are created on that ranks, which do not own this part of the boundary. === // Maps that stores to each rank number the DOF this rank has an interior // boundary with. std::map > recvGlobalDofs; for (int i = 0; i < mpiSize; i++) { recvGlobalDofs[i].clear(); recvObjs[i].clear(); } for (std::map::iterator it = rankDofs.begin(); it != rankDofs.end(); ++it) { if (dofOwner[it->first] > mpiRank && rankBoundaryDofs.count(it->first) == 0) { std::vector &ownerDofs = recvGlobalDofs[dofOwner[it->first]]; if (find(ownerDofs.begin(), ownerDofs.end(), it->first) == ownerDofs.end()) { ownerDofs.push_back(it->first); recvObjs[dofOwner[it->first]].push_back(it->second); } } } StdMpi > stdMpiDofs(mpiComm, true); stdMpiDofs.send(recvGlobalDofs); stdMpiDofs.recvFromAll(); stdMpiDofs.startCommunication(MPI_INT); stdMpiObjs.clear(); stdMpiObjs.send(recvObjs); stdMpiObjs.recvFromAll(); stdMpiObjs.startCommunication(MPI_INT); for (int i = 0; i < mpiSize; i++) sendObjects[i].clear(); for (std::map >::iterator it = stdMpiDofs.getRecvData().begin(); it != stdMpiDofs.getRecvData().end(); ++it) { for (unsigned int i = 0; i < it->second.size(); i++) { TEST_EXIT_DBG(stdMpiObjs.getRecvData(it->first).size() > i) ("Should not happen!\n"); AtomicBoundary& b = myIntBoundary.getNewAtomic(it->first); b.rankObj = rankDofs[it->second[i]]; b.neighObj = stdMpiObjs.getRecvData(it->first)[i]; if (mpiRank == 3 && it->first == 0) { MSG("ADDED VERTEX BOUNDARY WITH RANK 0!: %d %d %d\n", it->second[i], b.rankObj.el->getIndex(), b.rankObj.ithObj); } sendObjects[it->first].push_back(b.rankObj); } } stdMpiObjs.clear(); stdMpiObjs.send(sendObjects); stdMpiObjs.recvFromAll(); stdMpiObjs.startCommunication(MPI_INT); for (std::map >::iterator it = recvObjs.begin(); it != recvObjs.end(); ++it) { if (it->second.size() > 0) { TEST_EXIT_DBG(it->second.size() == stdMpiObjs.getRecvData(it->first).size()) ("Should not happen!\n"); for (unsigned int i = 0; i < it->second.size(); i++) { AtomicBoundary& b = otherIntBoundary.getNewAtomic(it->first); b.rankObj = it->second[i]; b.neighObj = stdMpiObjs.getRecvData(it->first)[i]; } } } } void MeshDistributor::removeMacroElements() { FUNCNAME("MeshDistributor::removeMacroElements()"); std::set macrosToRemove; for (std::deque::iterator it = mesh->firstMacroElement(); it != mesh->endOfMacroElements(); ++it) { PartitionElementData *partitionData = dynamic_cast ((*it)->getElement()->getElementData(PARTITION_ED)); if (partitionData->getPartitionStatus() != IN) macrosToRemove.insert(*it); } mesh->removeMacroElements(macrosToRemove, feSpace); } void MeshDistributor::createLocalGlobalNumbering() { FUNCNAME("MeshDistributor::createLocalGlobalNumbering()"); // === Get rank information about DOFs. === // Stores to each DOF pointer the set of ranks the DOF is part of. DofToPartitions partitionDofs; DofContainer rankDofs, rankAllDofs; DofToRank boundaryDofs; createDofMemberInfo(partitionDofs, rankDofs, rankAllDofs, boundaryDofs, vertexDof); nRankDofs = rankDofs.size(); nOverallDofs = partitionDofs.size(); // === Get starting position for global rank dof ordering. ==== rstart = 0; mpiComm.Scan(&nRankDofs, &rstart, 1, MPI_INT, MPI_SUM); rstart -= nRankDofs; // === Create for all dofs in rank new indices. The new index must start at === // === index 0, must be continues and have the same order as the indices === // === had before. === // Do not change the indices now, but create a new indexing and store it here. DofIndexMap rankDofsNewLocalIndex; isRankDof.clear(); int i = 0; for (DofContainer::iterator dofIt = rankAllDofs.begin(); dofIt != rankAllDofs.end(); ++dofIt) { rankDofsNewLocalIndex[*dofIt] = i; // First, we set all dofs in ranks partition to be owend by the rank. Later, // the dofs in ranks partition that are owned by other rank are set to false. isRankDof[i] = true; i++; } // === Create for all rank owned dofs a new global indexing. === // Stores for dofs in rank a new global index. DofIndexMap rankDofsNewGlobalIndex; // Stores for all rank owned dofs a continues local index. DofIndexMap rankOwnedDofsNewLocalIndex; i = 0; for (DofContainer::iterator dofIt = rankDofs.begin(); dofIt != rankDofs.end(); ++dofIt) { rankDofsNewGlobalIndex[*dofIt] = i + rstart; rankOwnedDofsNewLocalIndex[*dofIt] = i; i++; } // === Create information which dof indices must be send and which must === // === be received to/from other ranks. === // Stores to each rank a map from DOF pointers (which are all owned by the rank // and lie on an interior boundary) to new global DOF indices. std::map sendNewDofs; // Maps to each rank the number of new DOF indices this rank will receive from. // All these DOFs are at an interior boundary on this rank, but are owned by // another rank. std::map recvNewDofs; for (DofToRank::iterator it = boundaryDofs.begin(); it != boundaryDofs.end(); ++it) { if (it->second == mpiRank) { // If the boundary dof is a rank dof, it must be send to other ranks. // Search for all ranks that have this dof too. for (std::set::iterator itRanks = partitionDofs[it->first].begin(); itRanks != partitionDofs[it->first].end(); ++itRanks) { if (*itRanks != mpiRank) { TEST_EXIT_DBG(rankDofsNewGlobalIndex.count(it->first) == 1) ("DOF Key not found!\n"); sendNewDofs[*itRanks][it->first] = rankDofsNewGlobalIndex[it->first]; } } } else { // If the boundary dof is not a rank dof, its new dof index (and later // also the dof values) must be received from another rank. if (recvNewDofs.find(it->second) == recvNewDofs.end()) recvNewDofs[it->second] = 1; else recvNewDofs[it->second]++; } } // === Send and receive the dof indices at boundary. === sendDofs.clear(); for (std::map::iterator sendIt = sendNewDofs.begin(); sendIt != sendNewDofs.end(); ++sendIt) for (DofIndexMap::iterator dofIt = sendIt->second.begin(); dofIt != sendIt->second.end(); ++dofIt) sendDofs[sendIt->first].push_back(dofIt->first); typedef std::vector > DofMapVec; StdMpi stdMpi(mpiComm); stdMpi.send(sendNewDofs); for (std::map::iterator recvIt = recvNewDofs.begin(); recvIt != recvNewDofs.end(); ++recvIt, i++) stdMpi.recv(recvIt->first, recvIt->second * 2); stdMpi.startCommunication(MPI_INT); std::map &dofMap = stdMpi.getRecvData(); // === Change global dof indices at boundary from other ranks. === // Within this small data structure we track which dof index was already changed. // This is used to avoid the following situation: Assume, there are two dof indices // a and b in boundaryDofs. Then we have to change index a to b and b to c. When // the second rule applies, we have to avoid that not the first b, resulted from // changing a to b, is set to c, but the second one. Therefore, after the first // rule was applied, the dof pointer is set to false in this data structure and // is not allowed to be changed anymore. std::map dofChanged; recvDofs.clear(); for (DofToRank::iterator dofIt = boundaryDofs.begin(); dofIt != boundaryDofs.end(); ++dofIt) dofChanged[dofIt->first] = false; for (std::map::iterator rankIt = dofMap.begin(); rankIt != dofMap.end(); ++rankIt) { for (DofMapVec::iterator recvDofIt = rankIt->second.begin(); recvDofIt != rankIt->second.end(); ++recvDofIt) { DegreeOfFreedom oldDof = recvDofIt->first; DegreeOfFreedom newGlobalDof = recvDofIt->second; bool found = false; // Iterate over all boundary dofs to find the dof which index we have to change. for (DofToRank::iterator dofIt = boundaryDofs.begin(); dofIt != boundaryDofs.end(); ++dofIt) { if (*(dofIt->first) == oldDof && !dofChanged[dofIt->first]) { dofChanged[dofIt->first] = true; recvDofs[rankIt->first].push_back(dofIt->first); rankDofsNewGlobalIndex[dofIt->first] = newGlobalDof; isRankDof[rankDofsNewLocalIndex[dofIt->first]] = false; found = true; break; } } TEST_EXIT_DBG(found)("Should not happen!\n"); } } // === Create now the local to global index and local to dof index mappings. === createLocalMappings(rankDofsNewLocalIndex, rankOwnedDofsNewLocalIndex, rankDofsNewGlobalIndex); lastMeshChangeIndex = mesh->getChangeIndex(); } void MeshDistributor::updateLocalGlobalNumbering() { FUNCNAME("MeshDistributor::updateLocalGlobalNumbering()"); #if (DEBUG != 0) debug::ElementIdxToDofs elMap; debug::createSortedDofs(mesh, elMap); #endif typedef std::set DofSet; // === Get all DOFs in ranks partition. === ElementDofIterator elDofIt(feSpace); DofSet rankDofSet; // The vertexDof list must be recreated from the scratch. Otherwise, it is possible // that it maps dofs, that were removed (this is also possible, if the mesh was // refined, e.g., center dofs of an element are not dofs of the children). DofToBool oldVertexDof = vertexDof; vertexDof.clear(); TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL); while (elInfo) { elDofIt.reset(elInfo->getElement()); do { rankDofSet.insert(elDofIt.getDofPtr()); vertexDof[elDofIt.getDofPtr()] = false; } while(elDofIt.next()); elInfo = stack.traverseNext(elInfo); } elInfo = stack.traverseFirst(mesh, 0, Mesh::CALL_EL_LEVEL); while (elInfo) { Element *element = elInfo->getElement(); elDofIt.reset(element); elDofIt.reset(elInfo->getElement()); do { if (elDofIt.getCurrentPos() == 0) vertexDof[elDofIt.getDofPtr()] = true; } while(elDofIt.next()); elInfo = stack.traverseNext(elInfo); } DofContainer rankDofs; for (DofSet::iterator it = rankDofSet.begin(); it != rankDofSet.end(); ++it) rankDofs.push_back(*it); sort(rankDofs.begin(), rankDofs.end(), cmpDofsByValue); int nRankAllDofs = rankDofs.size(); // === Traverse on interior boundaries and move all not ranked owned DOFs from === // === rankDofs to boundaryDofs. === RankToDofContainer oldSendDofs = sendDofs; RankToDofContainer oldRecvDofs = recvDofs; sendDofs.clear(); recvDofs.clear(); for (RankToDofContainer::iterator it = oldSendDofs.begin(); it != oldSendDofs.end(); ++it) for (DofContainer::iterator dofIt = it->second.begin(); dofIt != it->second.end(); ++dofIt) if (oldVertexDof[*dofIt] && vertexDof[*dofIt]) sendDofs[it->first].push_back(*dofIt); for (RankToDofContainer::iterator it = oldRecvDofs.begin(); it != oldRecvDofs.end(); ++it) { for (DofContainer::iterator dofIt = it->second.begin(); dofIt != it->second.end(); ++dofIt) { if (oldVertexDof[*dofIt] && vertexDof[*dofIt]) { recvDofs[it->first].push_back(*dofIt); DofContainer::iterator eraseIt = find(rankDofs.begin(), rankDofs.end(), *dofIt); if (eraseIt != rankDofs.end()) rankDofs.erase(eraseIt); } } } for (InteriorBoundary::iterator it(myIntBoundary); !it.end(); ++it) { DofContainer dofs; it->rankObj.el->getVertexDofs(feSpace, it->rankObj, dofs); it->rankObj.el->getNonVertexDofs(feSpace, it->rankObj, dofs); for (int i = 0; i < static_cast(dofs.size()); i++) sendDofs[it.getRank()].push_back(dofs[i]); } for (InteriorBoundary::iterator it(otherIntBoundary); !it.end(); ++it) { DofContainer dofs; it->rankObj.el->getVertexDofs(feSpace, it->rankObj, dofs); it->rankObj.el->getNonVertexDofs(feSpace, it->rankObj, dofs); for (int i = 0; i < static_cast(dofs.size()); i++) { DofContainer::iterator eraseIt = find(rankDofs.begin(), rankDofs.end(), dofs[i]); if (eraseIt != rankDofs.end()) rankDofs.erase(eraseIt); recvDofs[it.getRank()].push_back(dofs[i]); } } nRankDofs = rankDofs.size(); // === Get starting position for global rank dof ordering. ==== mpiComm.Scan(&nRankDofs, &rstart, 1, MPI_INT, MPI_SUM); rstart -= nRankDofs; // === Calculate number of overall DOFs of all partitions. === nOverallDofs = 0; mpiComm.Allreduce(&nRankDofs, &nOverallDofs, 1, MPI_INT, MPI_SUM); // First, we set all dofs in ranks partition to be owend by the rank. Later, // the dofs in ranks partition that are owned by other rank are set to false. isRankDof.clear(); for (int i = 0; i < nRankAllDofs; i++) isRankDof[i] = true; // Stores for all rank owned dofs a new global index. DofIndexMap rankDofsNewGlobalIndex; for (int i = 0; i < nRankDofs; i++) rankDofsNewGlobalIndex[rankDofs[i]] = i + rstart; // === Send new DOF indices. === #if (DEBUG != 0) ParallelDomainDbg::testDofContainerCommunication(*this, sendDofs, recvDofs); #endif int i = 0; StdMpi > stdMpi(mpiComm, false); for (RankToDofContainer::iterator sendIt = sendDofs.begin(); sendIt != sendDofs.end(); ++sendIt, i++) { stdMpi.getSendData(sendIt->first).resize(0); stdMpi.getSendData(sendIt->first).reserve(sendIt->second.size()); for (DofContainer::iterator dofIt = sendIt->second.begin(); dofIt != sendIt->second.end(); ++dofIt) stdMpi.getSendData(sendIt->first).push_back(rankDofsNewGlobalIndex[*dofIt]); } stdMpi.updateSendDataSize(); stdMpi.recv(recvDofs); stdMpi.startCommunication(MPI_INT); for (RankToDofContainer::iterator recvIt = recvDofs.begin(); recvIt != recvDofs.end(); ++recvIt) { int j = 0; for (DofContainer::iterator dofIt = recvIt->second.begin(); dofIt != recvIt->second.end(); ++dofIt) { rankDofsNewGlobalIndex[*dofIt] = stdMpi.getRecvData(recvIt->first)[j++]; isRankDof[**dofIt] = false; } } // === Create now the local to global index and local to dof index mappings. === mapLocalGlobalDofs.clear(); mapLocalDofIndex.clear(); for (DofIndexMap::iterator dofIt = rankDofsNewGlobalIndex.begin(); dofIt != rankDofsNewGlobalIndex.end(); ++dofIt) mapLocalGlobalDofs[*(dofIt->first)] = dofIt->second; for (int i = 0; i < nRankDofs; i++) mapLocalDofIndex[i] = *(rankDofs[i]); // === Update dof admins due to new number of dofs. === updateDofAdmins(); lastMeshChangeIndex = mesh->getChangeIndex(); #if (DEBUG != 0) debug::testSortedDofs(mesh, elMap); ParallelDomainDbg::testCommonDofs(*this, true); #if 0 MSG("------------- Debug information -------------\n"); MSG("nRankDofs = %d\n", nRankDofs); MSG("nOverallDofs = %d\n", nOverallDofs); MSG("rstart %d\n", rstart); for (RankToDofContainer::iterator it = sendDofs.begin(); it != sendDofs.end(); ++it) { MSG("send %d DOFs to rank %d\n", it->second.size(), it->first); for (unsigned int i = 0; i < it->second.size(); i++) MSG("%d send DOF: %d\n", i, *(it->second[i])); } for (RankToDofContainer::iterator it = recvDofs.begin(); it != recvDofs.end(); ++it) { MSG("recv %d DOFs from rank %d\n", it->second.size(), it->first); for (unsigned int i = 0; i < it->second.size(); i++) MSG("%d recv DOF: %d\n", i, *(it->second[i])); } std::set testDofs; debug::getAllDofs(feSpace, testDofs); for (std::set::iterator it = testDofs.begin(); it != testDofs.end(); ++it) { MSG("DOF %d: mapLocalGlobalDofs = %d vertexDof = %d isRankDof = %d\n", **it, mapLocalGlobalDofs[**it], vertexDof[*it], isRankDof[**it]); } #endif #endif } void MeshDistributor::createLocalMappings(DofIndexMap &rankDofsNewLocalIndex, DofIndexMap &rankOwnedDofsNewLocalIndex, DofIndexMap &rankDofsNewGlobalIndex) { FUNCNAME("MeshDistributor::createLocalMappings()"); #if (DEBUG != 0) if (macroElementStructureConsisten) { std::set allDofs; debug::getAllDofs(feSpace, allDofs); for (std::set::iterator it = allDofs.begin(); it != allDofs.end(); ++it) { TEST_EXIT(rankDofsNewLocalIndex.count(*it) == 1) ("Missing DOF %d in rankDofsNewLocalIndex!\n", **it); TEST_EXIT(rankDofsNewGlobalIndex.count(*it) == 1) ("Missing DOF %d in rankDofsNewGlobalIndex!\n", **it); if (isRankDof[**it]) { TEST_EXIT(rankOwnedDofsNewLocalIndex.count(*it) == 1) ("Missing DOF %d in rankOwnedDofsNewLocalIndex!\n", **it); } } } #endif mapLocalGlobalDofs.clear(); mapLocalDofIndex.clear(); // Iterate over all DOFs in ranks partition. for (DofIndexMap::iterator dofIt = rankDofsNewLocalIndex.begin(); dofIt != rankDofsNewLocalIndex.end(); ++dofIt) { DegreeOfFreedom newLocalIndex = dofIt->second; DegreeOfFreedom newGlobalIndex = rankDofsNewGlobalIndex[dofIt->first]; *const_cast(dofIt->first) = newLocalIndex; mapLocalGlobalDofs[newLocalIndex] = newGlobalIndex; } for (DofIndexMap::iterator dofIt = rankOwnedDofsNewLocalIndex.begin(); dofIt != rankOwnedDofsNewLocalIndex.end(); ++dofIt) mapLocalDofIndex[dofIt->second] = *(dofIt->first); } void MeshDistributor::createDofMemberInfo(DofToPartitions& partitionDofs, DofContainer& rankOwnedDofs, DofContainer& rankAllDofs, DofToRank& boundaryDofs, DofToBool& isVertexDof) { partitionDofs.clear(); rankOwnedDofs.clear(); rankAllDofs.clear(); boundaryDofs.clear(); isVertexDof.clear(); // === Determine to each dof the set of partitions the dof belongs to. === ElementDofIterator elDofIt(feSpace); TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL); while (elInfo) { Element *element = elInfo->getElement(); elDofIt.reset(element); do { // Determine to each dof the partition(s) it corresponds to. partitionDofs[elDofIt.getDofPtr()].insert(partitionVec[element->getIndex()]); if (elDofIt.getCurrentPos() == 0) isVertexDof[elDofIt.getDofPtr()] = true; else isVertexDof[elDofIt.getDofPtr()] = false; } while (elDofIt.next()); elInfo = stack.traverseNext(elInfo); } // === Determine the set of ranks dofs and the dofs ownership at the boundary. === // iterate over all DOFs for (DofToPartitions::iterator it = partitionDofs.begin(); it != partitionDofs.end(); ++it) { bool isInRank = false; // iterate over all partition the current DOF is part of. for (std::set::iterator itpart1 = it->second.begin(); itpart1 != it->second.end(); ++itpart1) { if (*itpart1 == mpiRank) { rankAllDofs.push_back(it->first); if (it->second.size() == 1) { rankOwnedDofs.push_back(it->first); } else { // This dof is at the ranks boundary. It is owned by the rank only if // the rank number is the highest of all ranks containing this dof. bool insert = true; int highestRank = mpiRank; for (std::set::iterator itpart2 = it->second.begin(); itpart2 != it->second.end(); ++itpart2) { if (*itpart2 > mpiRank) insert = false; if (*itpart2 > highestRank) highestRank = *itpart2; } if (insert) rankOwnedDofs.push_back(it->first); boundaryDofs[it->first] = highestRank; } isInRank = true; break; } } if (!isInRank) isVertexDof.erase(it->first); } sort(rankAllDofs.begin(), rankAllDofs.end(), cmpDofsByValue); sort(rankOwnedDofs.begin(), rankOwnedDofs.end(), cmpDofsByValue); } void MeshDistributor::createPeriodicMap() { FUNCNAME("MeshDistributor::createPeriodicMap()"); if (periodicBoundary.boundary.size() == 0) return; // Clear all periodic dof mappings calculated before. We do it from scratch. periodicDof.clear(); 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. === for (RankToBoundMap::iterator it = periodicBoundary.boundary.begin(); it != periodicBoundary.boundary.end(); ++it) { TEST_EXIT_DBG(it->first != mpiRank) ("Periodic interior boundary within the rank itself is not possible!\n"); // Create dof indices on the boundary. DofContainer dofs; for (std::vector::iterator boundIt = it->second.begin(); boundIt != it->second.end(); ++boundIt) { boundIt->rankObj.el->getVertexDofs(feSpace, boundIt->rankObj, dofs); boundIt->rankObj.el->getNonVertexDofs(feSpace, boundIt->rankObj, dofs); } // 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(mapLocalGlobalDofs[*(dofs[i])]); // Receive from this rank the same number of dofs. stdMpi.recv(it->first, dofs.size()); } stdMpi.updateSendDataSize(); stdMpi.startCommunication(MPI_INT); // === 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. === std::map > dofFromRank; for (RankToBoundMap::iterator it = periodicBoundary.boundary.begin(); it != periodicBoundary.boundary.end(); ++it) { DofContainer dofs; // Create the dofs on the boundary in inverse order. for (std::vector::iterator boundIt = it->second.begin(); boundIt != it->second.end(); ++boundIt) { DofContainer tmpdofs; boundIt->rankObj.el->getNonVertexDofs(feSpace, boundIt->rankObj, tmpdofs); boundIt->rankObj.el->getVertexDofs(feSpace, boundIt->rankObj, tmpdofs); for (unsigned int i = 0; i < tmpdofs.size(); i++) dofs.push_back(tmpdofs[i]); } // Added the received dofs to the mapping. for (int i = 0; i < static_cast(dofs.size()); i++) { int globalDofIndex = mapLocalGlobalDofs[*(dofs[i])]; periodicDof[globalDofIndex].insert(stdMpi.getRecvData(it->first)[i]); dofFromRank[globalDofIndex].insert(it->first); } } if (dofFromRank.size() > 0) TEST_EXIT_DBG(mesh->getDim() == 2) ("Periodic boundary corner problem must be generalized to 3d!\n"); MPI::Request request[min(static_cast(periodicBoundary.boundary.size() * 2), 4)]; int requestCounter = 0; std::vector sendBuffers, recvBuffers; for (std::map >::iterator it = dofFromRank.begin(); it != dofFromRank.end(); ++it) { if (it->second.size() == 2) { TEST_EXIT_DBG(periodicDof[it->first].size() == 2)("Missing periodic dof!\n"); int *sendbuf = new int[2]; sendbuf[0] = *(periodicDof[it->first].begin()); sendbuf[1] = *(++(periodicDof[it->first].begin())); request[requestCounter++] = mpiComm.Isend(sendbuf, 2, MPI_INT, *(it->second.begin()), 0); request[requestCounter++] = mpiComm.Isend(sendbuf, 2, MPI_INT, *(++(it->second.begin())), 0); sendBuffers.push_back(sendbuf); int *recvbuf1 = new int[2]; int *recvbuf2 = new int[2]; request[requestCounter++] = mpiComm.Irecv(recvbuf1, 2, MPI_INT, *(it->second.begin()), 0); request[requestCounter++] = mpiComm.Irecv(recvbuf2, 2, MPI_INT, *(++(it->second.begin())), 0); recvBuffers.push_back(recvbuf1); recvBuffers.push_back(recvbuf2); } } MPI::Request::Waitall(requestCounter, request); int i = 0; for (std::map >::iterator it = dofFromRank.begin(); it != dofFromRank.end(); ++it) { if (it->second.size() == 2) { for (int k = 0; k < 2; k++) for (int j = 0; j < 2; j++) if (recvBuffers[i + k][j] != it->first) periodicDof[it->first].insert(recvBuffers[i + k][j]); i++; } } } void MeshDistributor::serialize(std::ostream &out) { SerUtil::serialize(out, elemWeights); SerUtil::serialize(out, initialPartitionMesh); SerUtil::serialize(out, partitionVec); SerUtil::serialize(out, oldPartitionVec); SerUtil::serialize(out, nRankDofs); SerUtil::serialize(out, nOverallDofs); myIntBoundary.serialize(out); otherIntBoundary.serialize(out); periodicBoundary.serialize(out); serialize(out, sendDofs); serialize(out, recvDofs); SerUtil::serialize(out, mapLocalGlobalDofs); SerUtil::serialize(out, mapLocalDofIndex); SerUtil::serialize(out, isRankDof); serialize(out, vertexDof); serialize(out, periodicDof); SerUtil::serialize(out, rstart); SerUtil::serialize(out, macroElementStructureConsisten); } void MeshDistributor::deserialize(std::istream &in) { SerUtil::deserialize(in, elemWeights); SerUtil::deserialize(in, initialPartitionMesh); SerUtil::deserialize(in, partitionVec); SerUtil::deserialize(in, oldPartitionVec); SerUtil::deserialize(in, nRankDofs); SerUtil::deserialize(in, nOverallDofs); // Create two maps: one from from element indices to the corresponding element // pointers, and one map from Dof indices to the corresponding dof pointers. std::map elIndexMap; std::map dofMap; ElementDofIterator elDofIter(feSpace); TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_EVERY_EL_PREORDER); while (elInfo) { Element *el = elInfo->getElement(); elIndexMap[el->getIndex()] = el; if (el->isLeaf()) { elDofIter.reset(el); do { dofMap[elDofIter.getDof()] = elDofIter.getDofPtr(); } while (elDofIter.next()); } elInfo = stack.traverseNext(elInfo); } myIntBoundary.deserialize(in, elIndexMap); otherIntBoundary.deserialize(in, elIndexMap); periodicBoundary.deserialize(in, elIndexMap); deserialize(in, sendDofs, dofMap); deserialize(in, recvDofs, dofMap); SerUtil::deserialize(in, mapLocalGlobalDofs); SerUtil::deserialize(in, mapLocalDofIndex); SerUtil::deserialize(in, isRankDof); deserialize(in, vertexDof, dofMap); deserialize(in, periodicDof); SerUtil::deserialize(in, rstart); SerUtil::deserialize(in, macroElementStructureConsisten); deserialized = true; } void MeshDistributor::serialize(std::ostream &out, PeriodicDofMap &data) { int mapSize = data.size(); SerUtil::serialize(out, mapSize); for (PeriodicDofMap::iterator it = data.begin(); it != data.end(); ++it) { DegreeOfFreedom dof = it->first; std::set dofSet = it->second; SerUtil::serialize(out, dof); SerUtil::serialize(out, dofSet); } } void MeshDistributor::deserialize(std::istream &in, PeriodicDofMap &data) { data.clear(); int mapSize = 0; SerUtil::deserialize(in, mapSize); for (int i = 0; i < mapSize; i++) { DegreeOfFreedom dof = 0; std::set dofSet; SerUtil::deserialize(in, dof); SerUtil::deserialize(in, dofSet); data[dof] = dofSet; } } void MeshDistributor::writePartitioningMesh(std::string filename) { FUNCNAME("MeshDistributor::writePartitioningMesh()"); std::map vec; TraverseStack stack; ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL | Mesh::FILL_COORDS); while (elInfo) { int index = elInfo->getElement()->getIndex(); vec[index] = partitionVec[index]; elInfo = stack.traverseNext(elInfo); } ElementFileWriter::writeFile(vec, mesh, filename); } }