/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2018 OpenFOAM Foundation Copyright (C) 2015-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- 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" #include "labelPairHashes.H" #include "ListOps.H" #include "globalIndex.H" #include "cyclicACMIPolyPatch.H" #include "mappedPatchBase.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_; public: lessProcPatches(const labelList& nbrProc, const labelList& referPatchID) : nbrProc_(nbrProc), referPatchID_(referPatchID) {} bool operator()(const label a, const label b) { if (nbrProc_[a] < nbrProc_[b]) { return true; } else if (nbrProc_[a] > nbrProc_[b]) { return false; } else { // Equal neighbour processor return referPatchID_[a] < referPatchID_[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; } Foam::wordList Foam::fvMeshDistribute::mergeWordList(const wordList& procNames) { List allNames(Pstream::nProcs()); allNames[Pstream::myProcNo()] = procNames; Pstream::gatherList(allNames); Pstream::scatterList(allNames); wordHashSet mergedNames; forAll(allNames, proci) { mergedNames.insert(allNames[proci]); } return mergedNames.sortedToc(); } 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; } } Foam::label Foam::fvMeshDistribute::findNonEmptyPatch() const { // Finds (non-empty) patch that exposed internal and proc faces can be // put into. const polyBoundaryMesh& patches = mesh_.boundaryMesh(); // Mark 'special' patches : -coupled, -duplicate faces. These probably // should not be used to (temporarily) store processor faces ... bitSet isCoupledPatch(patches.size()); forAll(patches, patchi) { const polyPatch& pp = patches[patchi]; const auto* cpp = isA(pp); if (cpp) { isCoupledPatch.set(patchi); const label dupPatchID = cpp->nonOverlapPatchID(); if (dupPatchID != -1) { isCoupledPatch.set(dupPatchID); } } else if (pp.coupled()) { isCoupledPatch.set(patchi); } } label nonEmptyPatchi = -1; forAllReverse(patches, patchi) { const polyPatch& pp = patches[patchi]; if ( !isA(pp) && !isCoupledPatch(patchi) && !isA(pp) ) { 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; } Foam::tmp Foam::fvMeshDistribute::generateTestField ( const fvMesh& mesh ) { const vector testNormal = normalised(vector::one); tmp tfld ( new surfaceScalarField ( IOobject ( "myFlux", mesh.time().timeName(), mesh, IOobject::NO_READ, IOobject::NO_WRITE ), mesh, dimensionedScalar(dimless, Zero) ) ); surfaceScalarField& fld = tfld.ref(); const surfaceVectorField n(mesh.Sf()/mesh.magSf()); forAll(fld, facei) { fld[facei] = (n[facei] & testNormal); } surfaceScalarField::Boundary& fluxBf = fld.boundaryFieldRef(); const surfaceVectorField::Boundary& nBf = n.boundaryField(); forAll(fluxBf, patchi) { fvsPatchScalarField& fvp = fluxBf[patchi]; scalarField newPfld(fvp.size()); forAll(newPfld, i) { newPfld[i] = (nBf[patchi][i] & testNormal); } fvp == newPfld; } return tfld; } void Foam::fvMeshDistribute::testField(const surfaceScalarField& fld) { const fvMesh& mesh = fld.mesh(); const vector testNormal = normalised(vector::one); const surfaceVectorField n(mesh.Sf()/mesh.magSf()); forAll(fld, facei) { scalar cos = (n[facei] & testNormal); if (mag(cos - fld[facei]) > 1e-6) { //FatalErrorInFunction WarningInFunction << "On internal face " << facei << " at " << mesh.faceCentres()[facei] << " the field value is " << fld[facei] << " whereas cos angle of " << testNormal << " with mesh normal " << n[facei] << " is " << cos //<< exit(FatalError); << endl; } } forAll(fld.boundaryField(), patchi) { const fvsPatchScalarField& fvp = fld.boundaryField()[patchi]; const fvsPatchVectorField& np = n.boundaryField()[patchi]; forAll(fvp, i) { scalar cos = (np[i] & testNormal); if (mag(cos - fvp[i]) > 1e-6) { label facei = fvp.patch().start()+i; //FatalErrorInFunction WarningInFunction << "On face " << facei << " on patch " << fvp.patch().name() << " at " << mesh.faceCentres()[facei] << " the field value is " << fvp[i] << " whereas cos angle of " << testNormal << " with mesh normal " << np[i] << " is " << cos //<< exit(FatalError); << endl; } } } } Foam::autoPtr Foam::fvMeshDistribute::deleteProcPatches ( const label destinationPatch ) { // Delete all processor patches. Move any processor faces into the last // non-processor patch. // New patchID per boundary faces to be repatched. Is -1 (no change) // or new patchID labelList newPatchID(mesh_.nBoundaryFaces(), -1); for (const polyPatch& pp : mesh_.boundaryMesh()) { if (isA(pp)) { if (debug) { Pout<< "Moving all faces of patch " << pp.name() << " into patch " << destinationPatch << endl; } SubList