/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | Website: https://openfoam.org \\ / A nd | Copyright (C) 2011-2021 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 "pointFields.H" #include "syncTools.H" #include "CompactListList.H" #include "fvMeshTools.H" #include "ListOps.H" #include "globalIndex.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(fvMeshDistribute, 0); //- Less function class that can be used for sorting processor patches class lessProcPatches { const labelList& nbrProc_; const labelList& referPatchID_; const labelList& referNbrPatchID_; public: lessProcPatches ( const labelList& nbrProc, const labelList& referPatchID, const labelList& referNbrPatchID ) : nbrProc_(nbrProc), referPatchID_(referPatchID), referNbrPatchID_(referNbrPatchID) {} bool operator()(const label a, const label b) { // Lower processor ID-s go first if (nbrProc_[a] < nbrProc_[b]) { return true; } else if (nbrProc_[a] > nbrProc_[b]) { return false; } // Non-cyclics go next else if (referPatchID_[a] == -1) { return true; } else if (referPatchID_[b] == -1) { return false; } // Cyclics should be ordered by refer patch ID if this is the owner // (lower processor ID), and by the neighbour refer patch ID if this is // the neighbour else if (Pstream::myProcNo() < nbrProc_[a]) { return referPatchID_[a] < referPatchID_[b]; } else { return referNbrPatchID_[a] < referNbrPatchID_[b]; } } }; } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::fvMeshDistribute::inplaceRenumberWithFlip ( const labelUList& oldToNew, const bool oldToNewHasFlip, const bool lstHasFlip, labelUList& lst ) { if (!lstHasFlip && !oldToNewHasFlip) { Foam::inplaceRenumber(oldToNew, lst); } else { // Either input data or map encodes sign so result encodes sign forAll(lst, elemI) { // Extract old value and sign label val = lst[elemI]; label sign = 1; if (lstHasFlip) { if (val > 0) { val = val-1; } else if (val < 0) { val = -val-1; sign = -1; } else { FatalErrorInFunction << "Problem : zero value " << val << " at index " << elemI << " out of " << lst.size() << " list with flip bit" << exit(FatalError); } } // Lookup new value and possibly change sign label newVal = oldToNew[val]; if (oldToNewHasFlip) { if (newVal > 0) { newVal = newVal-1; } else if (newVal < 0) { newVal = -newVal-1; sign = -sign; } else { FatalErrorInFunction << "Problem : zero value " << newVal << " at index " << elemI << " out of " << oldToNew.size() << " list with flip bit" << exit(FatalError); } } // Encode new value and sign lst[elemI] = sign*(newVal+1); } } } 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::findInternalPatch() const { const polyBoundaryMesh& patches = mesh_.boundaryMesh(); label internalPatchi = -1; forAll(patches, patchi) { const polyPatch& pp = patches[patchi]; if (isA(pp)) { internalPatchi = patchi; break; } } if (internalPatchi != -1) { return internalPatchi; } 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<< "findInternalPatch : 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) { inplaceRenumberWithFlip ( map().reverseFaceMap(), false, true, 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 ( const labelList& pointToGlobalMaster, labelListList& constructPointMap ) { // Find out which sets of points get merged and create a map from // mesh point to unique point. label nShared = 0; forAll(pointToGlobalMaster, pointi) { if (pointToGlobalMaster[pointi] != -1) { nShared++; } } Map