/*---------------------------------------------------------------------------*\
========= |
\\ / 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