/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "fvMeshDistribute.H" #include "PstreamCombineReduceOps.H" #include "fvMeshAdder.H" #include "faceCoupleInfo.H" #include "processorFvPatchField.H" #include "processorFvsPatchField.H" #include "processorCyclicPolyPatch.H" #include "processorCyclicFvPatchField.H" #include "polyTopoChange.H" #include "removeCells.H" #include "polyModifyFace.H" #include "polyRemovePoint.H" #include "mapDistributePolyMesh.H" #include "surfaceFields.H" #include "syncTools.H" #include "CompactListList.H" #include "fvMeshTools.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(fvMeshDistribute, 0); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // Foam::labelList Foam::fvMeshDistribute::select ( const bool selectEqual, const labelList& values, const label value ) { label n = 0; forAll(values, i) { if (selectEqual == (values[i] == value)) { n++; } } labelList indices(n); n = 0; forAll(values, i) { if (selectEqual == (values[i] == value)) { indices[n++] = i; } } return indices; } // Check all procs have same names and in exactly same order. void Foam::fvMeshDistribute::checkEqualWordList ( const string& msg, const wordList& lst ) { List allNames(Pstream::nProcs()); allNames[Pstream::myProcNo()] = lst; Pstream::gatherList(allNames); Pstream::scatterList(allNames); for (label procI = 1; procI < Pstream::nProcs(); procI++) { if (allNames[procI] != allNames[0]) { FatalErrorInFunction << "When checking for equal " << msg.c_str() << " :" << endl << "processor0 has:" << allNames[0] << endl << "processor" << procI << " has:" << allNames[procI] << endl << msg.c_str() << " need to be synchronised on all processors." << exit(FatalError); } } } Foam::wordList Foam::fvMeshDistribute::mergeWordList(const wordList& procNames) { List allNames(Pstream::nProcs()); allNames[Pstream::myProcNo()] = procNames; Pstream::gatherList(allNames); Pstream::scatterList(allNames); HashSet mergedNames; forAll(allNames, procI) { forAll(allNames[procI], i) { mergedNames.insert(allNames[procI][i]); } } return mergedNames.toc(); } // Print some info on mesh. void Foam::fvMeshDistribute::printMeshInfo(const fvMesh& mesh) { Pout<< "Primitives:" << nl << " points :" << mesh.nPoints() << nl << " bb :" << boundBox(mesh.points(), false) << nl << " internalFaces:" << mesh.nInternalFaces() << nl << " faces :" << mesh.nFaces() << nl << " cells :" << mesh.nCells() << nl; const fvBoundaryMesh& patches = mesh.boundary(); Pout<< "Patches:" << endl; forAll(patches, patchI) { const polyPatch& pp = patches[patchI].patch(); Pout<< " " << patchI << " name:" << pp.name() << " size:" << pp.size() << " start:" << pp.start() << " type:" << pp.type() << endl; } if (mesh.pointZones().size()) { Pout<< "PointZones:" << endl; forAll(mesh.pointZones(), zoneI) { const pointZone& pz = mesh.pointZones()[zoneI]; Pout<< " " << zoneI << " name:" << pz.name() << " size:" << pz.size() << endl; } } if (mesh.faceZones().size()) { Pout<< "FaceZones:" << endl; forAll(mesh.faceZones(), zoneI) { const faceZone& fz = mesh.faceZones()[zoneI]; Pout<< " " << zoneI << " name:" << fz.name() << " size:" << fz.size() << endl; } } if (mesh.cellZones().size()) { Pout<< "CellZones:" << endl; forAll(mesh.cellZones(), zoneI) { const cellZone& cz = mesh.cellZones()[zoneI]; Pout<< " " << zoneI << " name:" << cz.name() << " size:" << cz.size() << endl; } } } void Foam::fvMeshDistribute::printCoupleInfo ( const primitiveMesh& mesh, const labelList& sourceFace, const labelList& sourceProc, const labelList& sourcePatch, const labelList& sourceNewNbrProc ) { Pout<< nl << "Current coupling info:" << endl; forAll(sourceFace, bFaceI) { label meshFaceI = mesh.nInternalFaces() + bFaceI; Pout<< " meshFace:" << meshFaceI << " fc:" << mesh.faceCentres()[meshFaceI] << " connects to proc:" << sourceProc[bFaceI] << "/face:" << sourceFace[bFaceI] << " which will move to proc:" << sourceNewNbrProc[bFaceI] << endl; } } // Finds (non-empty) patch that exposed internal and proc faces can be put into. Foam::label Foam::fvMeshDistribute::findNonEmptyPatch() const { const polyBoundaryMesh& patches = mesh_.boundaryMesh(); label nonEmptyPatchI = -1; forAllReverse(patches, patchI) { const polyPatch& pp = patches[patchI]; if (!isA(pp) && !pp.coupled()) { nonEmptyPatchI = patchI; break; } } if (nonEmptyPatchI == -1) { FatalErrorInFunction << "Cannot find a patch which is neither of type empty nor" << " coupled in patches " << patches.names() << endl << "There has to be at least one such patch for" << " distribution to work" << abort(FatalError); } if (debug) { Pout<< "findNonEmptyPatch : using patch " << nonEmptyPatchI << " name:" << patches[nonEmptyPatchI].name() << " type:" << patches[nonEmptyPatchI].type() << " to put exposed faces into." << endl; } // Do additional test for processor patches intermingled with non-proc // patches. label procPatchI = -1; forAll(patches, patchI) { if (isA(patches[patchI])) { procPatchI = patchI; } else if (procPatchI != -1) { FatalErrorInFunction << "Processor patches should be at end of patch list." << endl << "Have processor patch " << procPatchI << " followed by non-processor patch " << patchI << " in patches " << patches.names() << abort(FatalError); } } return nonEmptyPatchI; } // Delete all processor patches. Move any processor faces into the last // non-processor patch. Foam::autoPtr Foam::fvMeshDistribute::deleteProcPatches ( const label destinationPatch ) { // New patchID per boundary faces to be repatched. Is -1 (no change) // or new patchID labelList newPatchID(mesh_.nFaces() - mesh_.nInternalFaces(), -1); label nProcPatches = 0; forAll(mesh_.boundaryMesh(), patchI) { const polyPatch& pp = mesh_.boundaryMesh()[patchI]; if (isA(pp)) { if (debug) { Pout<< "Moving all faces of patch " << pp.name() << " into patch " << destinationPatch << endl; } label offset = pp.start() - mesh_.nInternalFaces(); forAll(pp, i) { newPatchID[offset+i] = destinationPatch; } nProcPatches++; } } // Note: order of boundary faces been kept the same since the // destinationPatch is at the end and we have visited the patches in // incremental order. labelListList dummyFaceMaps; autoPtr map = repatch(newPatchID, dummyFaceMaps); // Delete (now empty) processor patches. { labelList oldToNew(identity(mesh_.boundaryMesh().size())); label newI = 0; // Non processor patches first forAll(mesh_.boundaryMesh(), patchI) { if (!isA(mesh_.boundaryMesh()[patchI])) { oldToNew[patchI] = newI++; } } label nNonProcPatches = newI; // Processor patches as last forAll(mesh_.boundaryMesh(), patchI) { if (isA(mesh_.boundaryMesh()[patchI])) { oldToNew[patchI] = newI++; } } fvMeshTools::reorderPatches(mesh_, oldToNew, nNonProcPatches, false); } return map; } // Repatch the mesh. Foam::autoPtr Foam::fvMeshDistribute::repatch ( const labelList& newPatchID, // per boundary face -1 or new patchID labelListList& constructFaceMap ) { polyTopoChange meshMod(mesh_); forAll(newPatchID, bFaceI) { if (newPatchID[bFaceI] != -1) { label faceI = mesh_.nInternalFaces() + bFaceI; label zoneID = mesh_.faceZones().whichZone(faceI); bool zoneFlip = false; if (zoneID >= 0) { const faceZone& fZone = mesh_.faceZones()[zoneID]; zoneFlip = fZone.flipMap()[fZone.whichFace(faceI)]; } meshMod.setAction ( polyModifyFace ( mesh_.faces()[faceI], // modified face faceI, // label of face mesh_.faceOwner()[faceI], // owner -1, // neighbour false, // face flip newPatchID[bFaceI], // patch for face false, // remove from zone zoneID, // zone for face zoneFlip // face flip in zone ) ); } } // Do mapping of fields from one patchField to the other ourselves since // is currently not supported by updateMesh. // Store boundary fields (we only do this for surfaceFields) PtrList> sFlds; saveBoundaryFields(sFlds); PtrList> vFlds; saveBoundaryFields(vFlds); PtrList> sptFlds; saveBoundaryFields(sptFlds); PtrList> sytFlds; saveBoundaryFields(sytFlds); PtrList> tFlds; saveBoundaryFields(tFlds); // Change the mesh (no inflation). Note: parallel comms allowed. // // NOTE: there is one very particular problem with this ordering. // We first create the processor patches and use these to merge out // shared points (see mergeSharedPoints below). So temporarily points // and edges do not match! autoPtr map = meshMod.changeMesh(mesh_, false, true); // Update fields. No inflation, parallel sync. mesh_.updateMesh(map); // Map patch fields using stored boundary fields. Note: assumes order // of fields has not changed in object registry! mapBoundaryFields(map, sFlds); mapBoundaryFields(map, vFlds); mapBoundaryFields(map, sptFlds); mapBoundaryFields(map, sytFlds); mapBoundaryFields(map, tFlds); // Move mesh (since morphing does not do this) if (map().hasMotionPoints()) { mesh_.movePoints(map().preMotionPoints()); } // Adapt constructMaps. if (debug) { label index = findIndex(map().reverseFaceMap(), -1); if (index != -1) { FatalErrorInFunction << "reverseFaceMap contains -1 at index:" << index << endl << "This means that the repatch operation was not just" << " a shuffle?" << abort(FatalError); } } forAll(constructFaceMap, procI) { inplaceRenumber(map().reverseFaceMap(), constructFaceMap[procI]); } return map; } // Detect shared points. Need processor patches to be present. // Background: when adding bits of mesh one can get points which // share the same position but are only detectable to be topologically // the same point when doing parallel analysis. This routine will // merge those points. Foam::autoPtr Foam::fvMeshDistribute::mergeSharedPoints ( labelListList& constructPointMap ) { // Find out which sets of points get merged and create a map from // mesh point to unique point. Map