diff --git a/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/IOdistributionMap.C b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/IOdistributionMap.C
new file mode 100644
index 0000000000..9efc758d7c
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/IOdistributionMap.C
@@ -0,0 +1,158 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration | Website: https://openfoam.org
+ \\ / A nd | Copyright (C) 2014-2022 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 "IOdistributionMap.H"
+
+/* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */
+
+namespace Foam
+{
+ defineTypeNameAndDebug(IOdistributionMap, 0);
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::IOdistributionMap::IOdistributionMap(const IOobject& io)
+:
+ regIOobject(io)
+{
+ // Temporary warning
+ if (io.readOpt() == IOobject::MUST_READ_IF_MODIFIED)
+ {
+ WarningInFunction
+ << "IOdistributionMap " << name()
+ << " constructed with IOobject::MUST_READ_IF_MODIFIED"
+ " but IOdistributionMap does not support automatic rereading."
+ << endl;
+ }
+
+ if
+ (
+ (
+ io.readOpt() == IOobject::MUST_READ
+ || io.readOpt() == IOobject::MUST_READ_IF_MODIFIED
+ )
+ || (io.readOpt() == IOobject::READ_IF_PRESENT && headerOk())
+ )
+ {
+ readStream(typeName) >> *this;
+ close();
+ }
+}
+
+
+Foam::IOdistributionMap::IOdistributionMap
+(
+ const IOobject& io,
+ const distributionMap& map
+)
+:
+ regIOobject(io)
+{
+ // Temporary warning
+ if (io.readOpt() == IOobject::MUST_READ_IF_MODIFIED)
+ {
+ WarningInFunction
+ << "IOdistributionMap " << name()
+ << " constructed with IOobject::MUST_READ_IF_MODIFIED"
+ " but IOdistributionMap does not support automatic rereading."
+ << endl;
+ }
+
+ if
+ (
+ (
+ io.readOpt() == IOobject::MUST_READ
+ || io.readOpt() == IOobject::MUST_READ_IF_MODIFIED
+ )
+ || (io.readOpt() == IOobject::READ_IF_PRESENT && headerOk())
+ )
+ {
+ readStream(typeName) >> *this;
+ close();
+ }
+ else
+ {
+ distributionMap::operator=(map);
+ }
+}
+
+
+Foam::IOdistributionMap::IOdistributionMap
+(
+ const IOobject& io,
+ distributionMap&& map
+)
+:
+ regIOobject(io),
+ distributionMap(move(map))
+{
+ // Temporary warning
+ if (io.readOpt() == IOobject::MUST_READ_IF_MODIFIED)
+ {
+ WarningInFunction
+ << "IOdistributionMap " << name()
+ << " constructed with IOobject::MUST_READ_IF_MODIFIED"
+ " but IOdistributionMap does not support automatic rereading."
+ << endl;
+ }
+
+ if
+ (
+ (
+ io.readOpt() == IOobject::MUST_READ
+ || io.readOpt() == IOobject::MUST_READ_IF_MODIFIED
+ )
+ || (io.readOpt() == IOobject::READ_IF_PRESENT && headerOk())
+ )
+ {
+ readStream(typeName) >> *this;
+ close();
+ }
+}
+
+
+// * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * * //
+
+Foam::IOdistributionMap::~IOdistributionMap()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
+
+bool Foam::IOdistributionMap::readData(Istream& is)
+{
+ return (is >> *this).good();
+}
+
+
+bool Foam::IOdistributionMap::writeData(Ostream& os) const
+{
+ return (os << *this).good();
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/IOdistributionMap.H b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/IOdistributionMap.H
new file mode 100644
index 0000000000..e78a31307e
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/IOdistributionMap.H
@@ -0,0 +1,98 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration | Website: https://openfoam.org
+ \\ / A nd | Copyright (C) 2014-2022 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 .
+
+Class
+ Foam::IOdistributionMap
+
+Description
+ IOdistributionMap is derived from distributionMap and
+ IOobject to give the distributionMap
+ automatic IO functionality via the objectRegistry.
+
+SourceFiles
+ IOdistributionMap.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef IOdistributionMap_H
+#define IOdistributionMap_H
+
+#include "distributionMap.H"
+#include "regIOobject.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+ Class IOdistributionMap Declaration
+\*---------------------------------------------------------------------------*/
+
+class IOdistributionMap
+:
+ public regIOobject,
+ public distributionMap
+{
+
+public:
+
+ //- Runtime type information
+ TypeName("distributionMap");
+
+ // Constructors
+
+ //- Construct given an IOobject
+ IOdistributionMap(const IOobject&);
+
+ //- Construct given an IOobject and distributionMap
+ IOdistributionMap(const IOobject&, const distributionMap&);
+
+ //- Move constructor transferring the distributionMap contents
+ IOdistributionMap(const IOobject&, distributionMap&&);
+
+
+ //- Destructor
+ virtual ~IOdistributionMap();
+
+
+ // Member Functions
+
+ //- ReadData function required for regIOobject read operation
+ virtual bool readData(Istream&);
+
+ //- WriteData function required for regIOobject write operation
+ virtual bool writeData(Ostream&) const;
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMap.C b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMap.C
new file mode 100644
index 0000000000..21626cbaeb
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMap.C
@@ -0,0 +1,556 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration | Website: https://openfoam.org
+ \\ / A nd | Copyright (C) 2011-2022 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 "distributionMap.H"
+#include "globalIndexAndTransform.H"
+#include "transformField.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+ defineTypeNameAndDebug(distributionMap, 0);
+}
+
+// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
+
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const transformer&,
+ const bool,
+ List&
+) const
+{}
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ UList&
+) const
+{}
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ Map&
+) const
+{}
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ EdgeMap&
+) const
+{}
+
+
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const transformer&,
+ const bool,
+ List&
+) const
+{}
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ UList&
+) const
+{}
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ Map&
+) const
+{}
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ EdgeMap&
+) const
+{}
+
+
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const transformer&,
+ const bool,
+ List&
+) const
+{}
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ UList&
+) const
+{}
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ Map&
+) const
+{}
+template<>
+void Foam::distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ EdgeMap&
+) const
+{}
+
+
+void Foam::distributionMap::printLayout(Ostream& os) const
+{
+ distributionMapBase::printLayout(os);
+
+ forAll(transformElements_, trafoI)
+ {
+ if (transformElements_[trafoI].size() > 0)
+ {
+ os << "transform " << trafoI << ':' << endl
+ << " start : " << transformStart_[trafoI] << endl
+ << " size : " << transformElements_[trafoI].size() << endl;
+ }
+ }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::distributionMap::distributionMap()
+:
+ distributionMapBase()
+{}
+
+
+Foam::distributionMap::distributionMap
+(
+ const label constructSize,
+ labelListList&& subMap,
+ labelListList&& constructMap,
+ const bool subHasFlip,
+ const bool constructHasFlip
+)
+:
+ distributionMapBase
+ (
+ constructSize,
+ move(subMap),
+ move(constructMap),
+ subHasFlip,
+ constructHasFlip
+ )
+{}
+
+
+Foam::distributionMap::distributionMap
+(
+ const label constructSize,
+ labelListList&& subMap,
+ labelListList&& constructMap,
+ labelListList&& transformElements,
+ labelList&& transformStart,
+ const bool subHasFlip,
+ const bool constructHasFlip
+)
+:
+ distributionMapBase
+ (
+ constructSize,
+ move(subMap),
+ move(constructMap),
+ subHasFlip,
+ constructHasFlip
+ ),
+ transformElements_(move(transformElements)),
+ transformStart_(move(transformStart))
+{}
+
+
+Foam::distributionMap::distributionMap
+(
+ const labelList& sendProcs,
+ const labelList& recvProcs
+)
+:
+ distributionMapBase(sendProcs, recvProcs)
+{}
+
+
+Foam::distributionMap::distributionMap
+(
+ const globalIndex& globalNumbering,
+ labelList& elements,
+ List>& compactMap,
+ const int tag
+)
+:
+ distributionMapBase
+ (
+ globalNumbering,
+ elements,
+ compactMap,
+ tag
+ )
+{}
+
+
+Foam::distributionMap::distributionMap
+(
+ const globalIndex& globalNumbering,
+ labelListList& cellCells,
+ List>& compactMap,
+ const int tag
+)
+:
+ distributionMapBase
+ (
+ globalNumbering,
+ cellCells,
+ compactMap,
+ tag
+ )
+{}
+
+
+Foam::distributionMap::distributionMap
+(
+ const globalIndex& globalNumbering,
+ labelList& elements,
+ const globalIndexAndTransform& globalTransforms,
+ const labelPairList& transformedElements,
+ labelList& transformedIndices,
+ List>& compactMap,
+ const int tag
+)
+:
+ distributionMapBase()
+{
+ // Construct per processor compact addressing of the global elements
+ // needed. The ones from the local processor are not included since
+ // these are always all needed.
+ calcCompactAddressing
+ (
+ globalNumbering,
+ elements,
+ compactMap
+ );
+
+ // Add all (non-local) transformed elements needed.
+ forAll(transformedElements, i)
+ {
+ labelPair elem = transformedElements[i];
+ label proci = globalTransforms.processor(elem);
+ if (proci != Pstream::myProcNo())
+ {
+ label index = globalTransforms.index(elem);
+ label nCompact = compactMap[proci].size();
+ compactMap[proci].insert(index, nCompact);
+ }
+ }
+
+
+ // Exchange what I need with processor that supplies it. Renumber elements
+ // into compact numbering
+ labelList compactStart;
+ exchangeAddressing
+ (
+ tag,
+ globalNumbering,
+ elements,
+ compactMap,
+ compactStart
+ );
+
+
+ // Renumber the transformed elements
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Count per transformIndex
+ label nTrafo = globalTransforms.transformPermutations().size();
+ labelList nPerTransform(nTrafo, 0);
+ forAll(transformedElements, i)
+ {
+ labelPair elem = transformedElements[i];
+ label trafoI = globalTransforms.transformIndex(elem);
+ nPerTransform[trafoI]++;
+ }
+ // Offset per transformIndex
+ transformStart_.setSize(nTrafo);
+ transformElements_.setSize(nTrafo);
+ forAll(transformStart_, trafoI)
+ {
+ transformStart_[trafoI] = constructSize_;
+ constructSize_ += nPerTransform[trafoI];
+ transformElements_[trafoI].setSize(nPerTransform[trafoI]);
+ }
+
+ // Sort transformed elements into their new slot.
+ nPerTransform = 0;
+
+ transformedIndices.setSize(transformedElements.size());
+ forAll(transformedElements, i)
+ {
+ labelPair elem = transformedElements[i];
+ label proci = globalTransforms.processor(elem);
+ label index = globalTransforms.index(elem);
+ label trafoI = globalTransforms.transformIndex(elem);
+
+ // Get compact index for untransformed element
+ label rawElemI =
+ (
+ proci == Pstream::myProcNo()
+ ? index
+ : compactMap[proci][index]
+ );
+
+ label& n = nPerTransform[trafoI];
+ // index of element to transform
+ transformElements_[trafoI][n] = rawElemI;
+ // destination of transformed element
+ transformedIndices[i] = transformStart_[trafoI]+n;
+ n++;
+ }
+
+ if (debug)
+ {
+ printLayout(Pout);
+ }
+}
+
+
+Foam::distributionMap::distributionMap
+(
+ const globalIndex& globalNumbering,
+ labelListList& cellCells,
+ const globalIndexAndTransform& globalTransforms,
+ const List& transformedElements,
+ labelListList& transformedIndices,
+ List>& compactMap,
+ const int tag
+)
+:
+ distributionMapBase()
+{
+ // Construct per processor compact addressing of the global elements
+ // needed. The ones from the local processor are not included since
+ // these are always all needed.
+ calcCompactAddressing
+ (
+ globalNumbering,
+ cellCells,
+ compactMap
+ );
+
+ // Add all (non-local) transformed elements needed.
+ forAll(transformedElements, celli)
+ {
+ const labelPairList& elems = transformedElements[celli];
+
+ forAll(elems, i)
+ {
+ label proci = globalTransforms.processor(elems[i]);
+ if (proci != Pstream::myProcNo())
+ {
+ label index = globalTransforms.index(elems[i]);
+ label nCompact = compactMap[proci].size();
+ compactMap[proci].insert(index, nCompact);
+ }
+ }
+ }
+
+
+ // Exchange what I need with processor that supplies it. Renumber elements
+ // into compact numbering
+ labelList compactStart;
+ exchangeAddressing
+ (
+ tag,
+ globalNumbering,
+ cellCells,
+ compactMap,
+ compactStart
+ );
+
+
+ // Renumber the transformed elements
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Count per transformIndex
+ label nTrafo = globalTransforms.transformPermutations().size();
+ labelList nPerTransform(nTrafo, 0);
+ forAll(transformedElements, celli)
+ {
+ const labelPairList& elems = transformedElements[celli];
+
+ forAll(elems, i)
+ {
+ label trafoI = globalTransforms.transformIndex(elems[i]);
+ nPerTransform[trafoI]++;
+ }
+ }
+ // Offset per transformIndex
+ transformStart_.setSize(nTrafo);
+ transformElements_.setSize(nTrafo);
+ forAll(transformStart_, trafoI)
+ {
+ transformStart_[trafoI] = constructSize_;
+ constructSize_ += nPerTransform[trafoI];
+ transformElements_[trafoI].setSize(nPerTransform[trafoI]);
+ }
+
+ // Sort transformed elements into their new slot.
+ nPerTransform = 0;
+
+ transformedIndices.setSize(transformedElements.size());
+ forAll(transformedElements, celli)
+ {
+ const labelPairList& elems = transformedElements[celli];
+ transformedIndices[celli].setSize(elems.size());
+
+ forAll(elems, i)
+ {
+ label proci = globalTransforms.processor(elems[i]);
+ label index = globalTransforms.index(elems[i]);
+ label trafoI = globalTransforms.transformIndex(elems[i]);
+
+ // Get compact index for untransformed element
+ label rawElemI =
+ (
+ proci == Pstream::myProcNo()
+ ? index
+ : compactMap[proci][index]
+ );
+
+ label& n = nPerTransform[trafoI];
+ // index of element to transform
+ transformElements_[trafoI][n] = rawElemI;
+ // destination of transformed element
+ transformedIndices[celli][i] = transformStart_[trafoI]+n;
+ n++;
+ }
+ }
+
+ if (debug)
+ {
+ printLayout(Pout);
+ }
+}
+
+
+Foam::distributionMap::distributionMap(const distributionMap& map)
+:
+ distributionMapBase(map),
+ transformElements_(map.transformElements_),
+ transformStart_(map.transformStart_)
+{}
+
+
+Foam::distributionMap::distributionMap(distributionMap&& map)
+:
+ distributionMapBase(move(map)),
+ transformElements_(move(map.transformElements_)),
+ transformStart_(move(map.transformStart_))
+{}
+
+
+Foam::distributionMap::distributionMap(Istream& is)
+{
+ is >> *this;
+}
+
+
+Foam::autoPtr Foam::distributionMap::clone() const
+{
+ return autoPtr(new distributionMap(*this));
+}
+
+
+// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
+
+Foam::label Foam::distributionMap::whichTransform(const label index)
+const
+{
+ return findLower(transformStart_, index+1);
+}
+
+
+void Foam::distributionMap::transfer(distributionMap& rhs)
+{
+ distributionMapBase::transfer(rhs);
+ transformElements_.transfer(rhs.transformElements_);
+ transformStart_.transfer(rhs.transformStart_);
+}
+
+
+// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
+
+void Foam::distributionMap::operator=(const distributionMap& rhs)
+{
+ // Check for assignment to self
+ if (this == &rhs)
+ {
+ FatalErrorInFunction
+ << "Attempted assignment to self"
+ << abort(FatalError);
+ }
+ distributionMapBase::operator=(rhs);
+ transformElements_ = rhs.transformElements_;
+ transformStart_ = rhs.transformStart_;
+}
+
+
+// * * * * * * * * * * * * * * Istream Operator * * * * * * * * * * * * * * //
+
+Foam::Istream& Foam::operator>>(Istream& is, distributionMap& map)
+{
+ is.fatalCheck("operator>>(Istream&, distributionMap&)");
+
+ is >> static_cast(map)
+ >> map.transformElements_ >> map.transformStart_;
+
+ return is;
+}
+
+
+// * * * * * * * * * * * * * * Ostream Operator * * * * * * * * * * * * * * //
+
+Foam::Ostream& Foam::operator<<(Ostream& os, const distributionMap& map)
+{
+ os << static_cast(map) << token::NL
+ << map.transformElements_ << token::NL
+ << map.transformStart_;
+
+ return os;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMap.H b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMap.H
new file mode 100644
index 0000000000..95eab374f0
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMap.H
@@ -0,0 +1,672 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration | Website: https://openfoam.org
+ \\ / A nd | Copyright (C) 2011-2022 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 .
+
+Class
+ Foam::distributionMap
+
+Description
+ Class containing processor-to-processor mapping information.
+
+ We store mapping from the bits-to-send to the complete starting list
+ (subXXXMap) and from the received bits to their location in the new
+ list (constructXXXMap).
+
+Note:
+ Schedule is a list of processor pairs (one send, one receive. One of
+ them will be myself) which forms a scheduled (i.e. non-buffered) exchange.
+ See distribute on how to use it.
+ Note2: number of items sent on one processor have to equal the number
+ of items received on the other processor.
+
+ To aid constructing these maps there are the constructors from global
+ numbering, either with or without transforms.
+
+ - without transforms:
+ Constructors using compact numbering: layout is
+ - all my own elements first (whether used or not)
+ - followed by used-only remote elements sorted by remote processor.
+ So e.g 4 procs and on proc 1 the compact
+ table will first have all globalIndex.localSize() elements from proc1
+ followed by used-only elements of proc0, proc2, proc3.
+ The constructed distributionMap sends the local elements from and
+ receives the remote elements into their compact position.
+ compactMap[proci] is the position of elements from proci in the compact
+ map. compactMap[myProcNo()] is empty since trivial addressing.
+
+ It rewrites the input global indices into indices into the constructed
+ data.
+
+
+ - with transforms:
+ This requires the precalculated set of possible transforms
+ (globalIndexAndTransform). These are given as permutations (+, -, or none)
+ of up to 3 independent transforms.
+ The layout of the data is
+ - all my own elements first (whether used or not)
+ - followed by used-only remote elements sorted by remote processor.
+ - followed by - for each transformation index - the set of local or
+ remote elements with that transformation.
+ The inputs for the constructor are
+ - the set of untransformed local or remote indices in globalIndex
+ numbering. These get rewritten to be indices into the layout of the data.
+ - the set of transformed local or remote indices in globalIndexAndTransform
+ encoding. These are labelPairs.
+
+ Any distribute with transforms is now done as:
+ 1. exchange data with other processors and receive these into the
+ slots for that processor
+ 2. for all transformations transform a subset of the data according
+ to transformElements_[transformI] and store this starting from
+ transformStart_[transformI]
+
+ In the same way a reverse distribute will
+ 1. apply the inverse transform to the data starting at
+ transformStart_[transformI] and copy the result back into the
+ transformElements_[transformI]. These might be local or remote slots.
+ 2. the data in the remote slots will now be sent back to the correct
+ location in the originating processor.
+
+ E.g. a map to handle
+ - mesh points on a mesh with
+ - 1 cyclic so 3 permutations (+,-,none) will have layout
+ - on e.g. processor 1 out of 2:
+
+ +------+ <- transformStart[2]
+ | |
+ | | <- transform2 applied to data in local or remote slots
+ | |
+ +------+ <- transformStart[1]
+ | |
+ | | <- transform1 applied to data in local or remote slots
+ | |
+ +------+ <- transformStart[1]
+ | |
+ | | <- transform0 applied to data in local or remote slots
+ | |
+ +------+ <- transformStart[0]
+ | |
+ | | <- data from proc2
+ | |
+ +------+
+ | |
+ | | <- data from proc0
+ | |
+ +------+ <- mesh.nPoints()
+ | |
+ | |
+ | |
+ +------+ 0
+
+
+ When constructing from components optionally a 'flip' on
+ the maps can be specified. This will interpret the map
+ values as index+flip, similar to e.g. faceProcAddressing. The flip
+ will only be applied to fieldTypes (scalar, vector, .. triad)
+
+
+SourceFiles
+ distributionMap.C
+ distributionMapTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef distributionMap_H
+#define distributionMap_H
+
+#include "distributionMapBase.H"
+#include "transformer.H"
+#include "coupledPolyPatch.H"
+#include "EdgeMap.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+class globalIndexAndTransform;
+
+
+// Forward declaration of friend functions and operators
+
+class distributionMap;
+
+Istream& operator>>(Istream&, distributionMap&);
+Ostream& operator<<(Ostream&, const distributionMap&);
+
+
+/*---------------------------------------------------------------------------*\
+ Class distributionMap Declaration
+\*---------------------------------------------------------------------------*/
+
+class distributionMap
+:
+ public distributionMapBase
+{
+ // Private Data
+
+ //- For every globalIndexAndTransform::transformPermutations
+ // gives the elements that need to be transformed
+ labelListList transformElements_;
+
+ //- Destination in constructMap for transformed elements
+ labelList transformStart_;
+
+
+ // Private Member Functions
+
+ //- Helper function: copy transformElements without transformation
+ template
+ void applyDummyTransforms(List& field) const;
+
+ template
+ void applyTransforms
+ (
+ const globalIndexAndTransform& globalTransforms,
+ List& field,
+ const TransformOp& top
+ ) const;
+
+ //- Helper function: copy transformElements without transformation
+ template
+ void applyDummyInverseTransforms(List& field) const;
+
+ template
+ void applyInverseTransforms
+ (
+ const globalIndexAndTransform& globalTransforms,
+ List& field,
+ const TransformOp& top
+ ) const;
+
+
+public:
+
+ // Public classes
+
+ //- Default transformation behaviour
+ class transform
+ {
+ public:
+
+ template
+ void operator()
+ (
+ const transformer& vt,
+ const bool forward,
+ List& fld
+ ) const
+ {
+ if (forward)
+ {
+ vt.transformList(fld);
+ }
+ else
+ {
+ vt.invTransformList(fld);
+ }
+ }
+
+ template
+ void operator()
+ (
+ const transformer& vt,
+ const bool forward,
+ List>& flds
+ ) const
+ {
+ forAll(flds, i)
+ {
+ operator()(vt, forward, flds[i]);
+ }
+ }
+
+ //- Transform patch-based field
+ template
+ void operator()(const coupledPolyPatch& cpp, UList& fld) const
+ {
+ if (cpp.transform().transforms())
+ {
+ cpp.transform().transformList(fld);
+ }
+ }
+
+ //- Transform sparse field
+ template class Container>
+ void operator()(const coupledPolyPatch& cpp, Container& map)
+ const
+ {
+ if (cpp.transform().transforms())
+ {
+ cpp.transform().transformList(map);
+ }
+ }
+ };
+
+ //- Default transformation behaviour for position
+ class transformPosition
+ {
+ public:
+
+ void operator()
+ (
+ const transformer& vt,
+ const bool forward,
+ List& fld
+ ) const
+ {
+ pointField pfld(move(fld));
+ if (forward)
+ {
+ fld = vt.transformPosition(pfld);
+ }
+ else
+ {
+ fld = vt.invTransformPosition(pfld);
+ }
+ }
+
+ void operator()
+ (
+ const transformer& vt,
+ const bool forward,
+ List>& flds
+ ) const
+ {
+ forAll(flds, i)
+ {
+ operator()(vt, forward, flds[i]);
+ }
+ }
+
+ //- Transform patch-based field
+ void operator()(const coupledPolyPatch& cpp, pointField& fld) const
+ {
+ cpp.transform().transformPosition(fld, fld);
+ }
+
+ template class Container>
+ void operator()(const coupledPolyPatch& cpp, Container& map)
+ const
+ {
+ Field fld(map.size());
+
+ label i = 0;
+ forAllConstIter(typename Container, map, iter)
+ {
+ fld[i++] = iter();
+ }
+
+ cpp.transform().transformPosition(fld, fld);
+
+ i = 0;
+ forAllIter(typename Container, map, iter)
+ {
+ iter() = fld[i++];
+ }
+ }
+ };
+
+
+ // Declare name of the class and its debug switch
+ ClassName("distributionMap");
+
+
+ // Constructors
+
+ //- Construct null
+ distributionMap();
+
+ //- Construct from components
+ distributionMap
+ (
+ const label constructSize,
+ labelListList&& subMap,
+ labelListList&& constructMap,
+ const bool subHasFlip = false,
+ const bool constructHasFlip = false
+ );
+
+ //- Construct from components
+ distributionMap
+ (
+ const label constructSize,
+ labelListList&& subMap,
+ labelListList&& constructMap,
+ labelListList&& transformElements,
+ labelList&& transformStart,
+ const bool subHasFlip = false,
+ const bool constructHasFlip = false
+ );
+
+ //- Construct from reverse addressing: per data item the send
+ // processor and the receive processor. (note: data is not stored
+ // sorted per processor so cannot use printLayout).
+ distributionMap
+ (
+ const labelList& sendProcs,
+ const labelList& recvProcs
+ );
+
+ //- Construct from list of (possibly) remote elements in globalIndex
+ // numbering (or -1). Determines compact numbering (see above) and
+ // distribute map to get data into this ordering and renumbers the
+ // elements to be in compact numbering.
+ distributionMap
+ (
+ const globalIndex&,
+ labelList& elements,
+ List>& compactMap,
+ const int tag = Pstream::msgType()
+ );
+
+ //- Special variant that works with the info sorted into bins
+ // according to local indices. E.g. think cellCells where
+ // cellCells[localCelli] is a list of global cells
+ distributionMap
+ (
+ const globalIndex&,
+ labelListList& cellCells,
+ List>& compactMap,
+ const int tag = Pstream::msgType()
+ );
+
+ //- Construct from list of (possibly remote) untransformed elements
+ // in globalIndex numbering (or -1) and (possibly remote)
+ // transformded elements in globalIndexAndTransform numbering.
+ // Determines compact numbering (see above) and
+ // distribute map to get data into this ordering and renumbers the
+ // elements to be in compact numbering.
+ distributionMap
+ (
+ const globalIndex&,
+ labelList& untransformedElements,
+ const globalIndexAndTransform&,
+ const labelPairList& transformedElements,
+ labelList& transformedIndices,
+ List>& compactMap,
+ const int tag = Pstream::msgType()
+ );
+
+ //- As above but with ListLists.
+ distributionMap
+ (
+ const globalIndex&,
+ labelListList& cellCells,
+ const globalIndexAndTransform&,
+ const List& transformedElements,
+ labelListList& transformedIndices,
+ List>& compactMap,
+ const int tag = Pstream::msgType()
+ );
+
+ //- Copy constructor
+ distributionMap(const distributionMap&);
+
+ //- Move constructor
+ distributionMap(distributionMap&&);
+
+ //- Construct from Istream
+ distributionMap(Istream&);
+
+ //- Clone
+ autoPtr clone() const;
+
+
+ //- Destructor
+ virtual ~distributionMap()
+ {}
+
+
+ // Member Functions
+
+ // Access
+
+ //- For every globalIndexAndTransform::transformPermutations
+ // gives the elements that need to be transformed
+ const labelListList& transformElements() const
+ {
+ return transformElements_;
+ }
+
+ //- Destination in constructMap for transformed elements
+ const labelList& transformStart() const
+ {
+ return transformStart_;
+ }
+
+ //- Find transform from transformElements
+ label whichTransform(const label index) const;
+
+
+ // Other
+
+ //- Transfer the contents of the argument and annul the argument.
+ void transfer(distributionMap&);
+
+ //- Distribute data using default commsType.
+ template
+ void distribute
+ (
+ List& fld,
+ const bool dummyTransform = true,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Distribute data using default commsType.
+ template
+ void distribute
+ (
+ List& fld,
+ const negateOp& negOp,
+ const bool dummyTransform = true,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Distribute data using default commsType.
+ template
+ void distribute
+ (
+ DynamicList& fld,
+ const bool dummyTransform = true,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Reverse distribute data using default commsType.
+ template
+ void reverseDistribute
+ (
+ const label constructSize,
+ List&,
+ const bool dummyTransform = true,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Reverse distribute data using default commsType.
+ // Since constructSize might be larger than supplied size supply
+ // a nullValue
+ template
+ void reverseDistribute
+ (
+ const label constructSize,
+ const T& nullValue,
+ List& fld,
+ const bool dummyTransform = true,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Distribute with transforms
+ template
+ void distribute
+ (
+ const globalIndexAndTransform&,
+ List& fld,
+ const TransformOp& top,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Reverse distribute with transforms
+ template
+ void reverseDistribute
+ (
+ const globalIndexAndTransform&,
+ const label constructSize,
+ List& fld,
+ const TransformOp& top,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Reverse distribute with transforms
+ template
+ void reverseDistribute
+ (
+ const globalIndexAndTransform&,
+ const label constructSize,
+ const T& nullValue,
+ List& fld,
+ const TransformOp& top,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Debug: print layout. Can only be used on maps with sorted
+ // storage (local data first, then non-local data)
+ void printLayout(Ostream& os) const;
+
+ //- Correct for topo change.
+ void updateMesh(const polyTopoChangeMap&)
+ {
+ notImplemented
+ (
+ "distributionMap::updateMesh(const polyTopoChangeMap&)"
+ );
+ }
+
+ // Member Operators
+
+ void operator=(const distributionMap&);
+
+ // IOstream Operators
+
+ //- Read dictionary from Istream
+ friend Istream& operator>>(Istream&, distributionMap&);
+
+ //- Write dictionary to Ostream
+ friend Ostream& operator<<(Ostream&, const distributionMap&);
+
+};
+
+
+// Template specialisation for primitives that do not need transform
+template<>
+void distributionMap::transform::operator()
+(
+ const transformer&,
+ const bool,
+ List&
+) const;
+template<>
+void distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ UList&
+) const;
+template<>
+void distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ Map&
+) const;
+template<>
+void distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ EdgeMap&
+) const;
+
+template<>
+void distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ UList&
+) const;
+template<>
+void distributionMap::transform::operator()
+(
+ const transformer&,
+ const bool,
+ List&
+) const;
+template<>
+void distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ Map&
+) const;
+template<>
+void distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ EdgeMap&
+) const;
+
+template<>
+void distributionMap::transform::operator()
+(
+ const coupledPolyPatch& cpp,
+ UList& fld
+) const;
+template<>
+void distributionMap::transform::operator()
+(
+ const transformer&,
+ const bool,
+ List&
+) const;
+template<>
+void distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ Map&
+) const;
+template<>
+void distributionMap::transform::operator()
+(
+ const coupledPolyPatch&,
+ EdgeMap&
+) const;
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+ #include "distributionMapTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapBase.C b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapBase.C
new file mode 100644
index 0000000000..511b5e66b2
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapBase.C
@@ -0,0 +1,1272 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration | Website: https://openfoam.org
+ \\ / A nd | Copyright (C) 2015-2022 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 "distributionMapBase.H"
+#include "commSchedule.H"
+#include "HashSet.H"
+#include "globalIndex.H"
+#include "ListOps.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+ defineTypeNameAndDebug(distributionMapBase, 0);
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+Foam::List Foam::distributionMapBase::schedule
+(
+ const labelListList& subMap,
+ const labelListList& constructMap,
+ const int tag
+)
+{
+ // Communications: send and receive processor
+ List allComms;
+
+ {
+ HashSet> commsSet(Pstream::nProcs());
+
+ // Find what communication is required
+ forAll(subMap, proci)
+ {
+ if (proci != Pstream::myProcNo())
+ {
+ if (subMap[proci].size())
+ {
+ // I need to send to proci
+ commsSet.insert(labelPair(Pstream::myProcNo(), proci));
+ }
+ if (constructMap[proci].size())
+ {
+ // I need to receive from proci
+ commsSet.insert(labelPair(proci, Pstream::myProcNo()));
+ }
+ }
+ }
+ allComms = commsSet.toc();
+ }
+
+
+ // Reduce
+ if (Pstream::master())
+ {
+ // Receive and merge
+ for
+ (
+ int slave=Pstream::firstSlave();
+ slave<=Pstream::lastSlave();
+ slave++
+ )
+ {
+ IPstream fromSlave(Pstream::commsTypes::scheduled, slave, 0, tag);
+ List nbrData(fromSlave);
+
+ forAll(nbrData, i)
+ {
+ if (findIndex(allComms, nbrData[i]) == -1)
+ {
+ label sz = allComms.size();
+ allComms.setSize(sz+1);
+ allComms[sz] = nbrData[i];
+ }
+ }
+ }
+ // Send back
+ for
+ (
+ int slave=Pstream::firstSlave();
+ slave<=Pstream::lastSlave();
+ slave++
+ )
+ {
+ OPstream toSlave(Pstream::commsTypes::scheduled, slave, 0, tag);
+ toSlave << allComms;
+ }
+ }
+ else
+ {
+ {
+ OPstream toMaster
+ (
+ Pstream::commsTypes::scheduled,
+ Pstream::masterNo(),
+ 0,
+ tag
+ );
+ toMaster << allComms;
+ }
+ {
+ IPstream fromMaster
+ (
+ Pstream::commsTypes::scheduled,
+ Pstream::masterNo(),
+ 0,
+ tag
+ );
+ fromMaster >> allComms;
+ }
+ }
+
+
+ // Determine my schedule.
+ labelList mySchedule
+ (
+ commSchedule
+ (
+ Pstream::nProcs(),
+ allComms
+ ).procSchedule()[Pstream::myProcNo()]
+ );
+
+ // Processors involved in my schedule
+ return List(UIndirectList(allComms, mySchedule));
+
+
+ // if (debug)
+ //{
+ // Pout<< "I need to:" << endl;
+ // const List& comms = schedule();
+ // forAll(comms, i)
+ // {
+ // const labelPair& twoProcs = comms[i];
+ // label sendProc = twoProcs[0];
+ // label recvProc = twoProcs[1];
+ //
+ // if (recvProc == Pstream::myProcNo())
+ // {
+ // Pout<< " receive from " << sendProc << endl;
+ // }
+ // else
+ // {
+ // Pout<< " send to " << recvProc << endl;
+ // }
+ // }
+ //}
+}
+
+
+const Foam::List& Foam::distributionMapBase::schedule() const
+{
+ if (schedulePtr_.empty())
+ {
+ schedulePtr_.reset
+ (
+ new List
+ (
+ schedule(subMap_, constructMap_, Pstream::msgType())
+ )
+ );
+ }
+ return schedulePtr_();
+}
+
+
+void Foam::distributionMapBase::checkReceivedSize
+(
+ const label proci,
+ const label expectedSize,
+ const label receivedSize
+)
+{
+ if (receivedSize != expectedSize)
+ {
+ FatalErrorInFunction
+ << "Expected from processor " << proci
+ << " " << expectedSize << " but received "
+ << receivedSize << " elements."
+ << abort(FatalError);
+ }
+}
+
+
+void Foam::distributionMapBase::printLayout(Ostream& os) const
+{
+ // Determine offsets of remote data.
+ labelList minIndex(Pstream::nProcs(), labelMax);
+ labelList maxIndex(Pstream::nProcs(), labelMin);
+ forAll(constructMap_, proci)
+ {
+ const labelList& construct = constructMap_[proci];
+ if (constructHasFlip_)
+ {
+ forAll(construct, i)
+ {
+ label index = mag(construct[i])-1;
+ minIndex[proci] = min(minIndex[proci], index);
+ maxIndex[proci] = max(maxIndex[proci], index);
+ }
+ }
+ else
+ {
+ forAll(construct, i)
+ {
+ label index = construct[i];
+ minIndex[proci] = min(minIndex[proci], index);
+ maxIndex[proci] = max(maxIndex[proci], index);
+ }
+ }
+ }
+
+ label localSize;
+ if (maxIndex[Pstream::myProcNo()] == labelMin)
+ {
+ localSize = 0;
+ }
+ else
+ {
+ localSize = maxIndex[Pstream::myProcNo()]+1;
+ }
+
+ os << "Layout: (constructSize:" << constructSize_
+ << " subHasFlip:" << subHasFlip_
+ << " constructHasFlip:" << constructHasFlip_
+ << ")" << endl
+ << "local (processor " << Pstream::myProcNo() << "):" << endl
+ << " start : 0" << endl
+ << " size : " << localSize << endl;
+
+ label offset = localSize;
+ forAll(minIndex, proci)
+ {
+ if (proci != Pstream::myProcNo())
+ {
+ if (constructMap_[proci].size() > 0)
+ {
+ if (minIndex[proci] != offset)
+ {
+ FatalErrorInFunction
+ << "offset:" << offset
+ << " proci:" << proci
+ << " minIndex:" << minIndex[proci]
+ << abort(FatalError);
+ }
+
+ label size = maxIndex[proci]-minIndex[proci]+1;
+ os << "processor " << proci << ':' << endl
+ << " start : " << offset << endl
+ << " size : " << size << endl;
+
+ offset += size;
+ }
+ }
+ }
+}
+
+
+void Foam::distributionMapBase::calcCompactAddressing
+(
+ const globalIndex& globalNumbering,
+ const labelList& elements,
+ List>& compactMap
+) const
+{
+ compactMap.setSize(Pstream::nProcs());
+
+ // Count all (non-local) elements needed. Just for presizing map.
+ labelList nNonLocal(Pstream::nProcs(), 0);
+
+ forAll(elements, i)
+ {
+ label globalIndex = elements[i];
+
+ if (globalIndex != -1 && !globalNumbering.isLocal(globalIndex))
+ {
+ label proci = globalNumbering.whichProcID(globalIndex);
+ nNonLocal[proci]++;
+ }
+ }
+
+ forAll(compactMap, proci)
+ {
+ compactMap[proci].clear();
+ if (proci != Pstream::myProcNo())
+ {
+ compactMap[proci].resize(2*nNonLocal[proci]);
+ }
+ }
+
+
+ // Collect all (non-local) elements needed.
+ forAll(elements, i)
+ {
+ label globalIndex = elements[i];
+
+ if (globalIndex != -1 && !globalNumbering.isLocal(globalIndex))
+ {
+ label proci = globalNumbering.whichProcID(globalIndex);
+ label index = globalNumbering.toLocal(proci, globalIndex);
+ label nCompact = compactMap[proci].size();
+ compactMap[proci].insert(index, nCompact);
+ }
+ }
+}
+
+
+void Foam::distributionMapBase::calcCompactAddressing
+(
+ const globalIndex& globalNumbering,
+ const labelListList& cellCells,
+ List>& compactMap
+) const
+{
+ compactMap.setSize(Pstream::nProcs());
+
+ // Count all (non-local) elements needed. Just for presizing map.
+ labelList nNonLocal(Pstream::nProcs(), 0);
+
+ forAll(cellCells, cellI)
+ {
+ const labelList& cCells = cellCells[cellI];
+
+ forAll(cCells, i)
+ {
+ label globalIndex = cCells[i];
+
+ if (globalIndex != -1 && !globalNumbering.isLocal(globalIndex))
+ {
+ label proci = globalNumbering.whichProcID(globalIndex);
+ nNonLocal[proci]++;
+ }
+ }
+ }
+
+ forAll(compactMap, proci)
+ {
+ compactMap[proci].clear();
+ if (proci != Pstream::myProcNo())
+ {
+ compactMap[proci].resize(2*nNonLocal[proci]);
+ }
+ }
+
+
+ // Collect all (non-local) elements needed.
+ forAll(cellCells, cellI)
+ {
+ const labelList& cCells = cellCells[cellI];
+
+ forAll(cCells, i)
+ {
+ label globalIndex = cCells[i];
+
+ if (globalIndex != -1 && !globalNumbering.isLocal(globalIndex))
+ {
+ label proci = globalNumbering.whichProcID(globalIndex);
+ label index = globalNumbering.toLocal(proci, globalIndex);
+ label nCompact = compactMap[proci].size();
+ compactMap[proci].insert(index, nCompact);
+ }
+ }
+ }
+}
+
+
+void Foam::distributionMapBase::exchangeAddressing
+(
+ const int tag,
+ const globalIndex& globalNumbering,
+ labelList& elements,
+ List>& compactMap,
+ labelList& compactStart
+)
+{
+ // The overall compact addressing is
+ // - myProcNo data first (uncompacted)
+ // - all other processors consecutively
+
+ compactStart.setSize(Pstream::nProcs());
+ compactStart[Pstream::myProcNo()] = 0;
+ constructSize_ = globalNumbering.localSize();
+ forAll(compactStart, proci)
+ {
+ if (proci != Pstream::myProcNo())
+ {
+ compactStart[proci] = constructSize_;
+ constructSize_ += compactMap[proci].size();
+ }
+ }
+
+
+
+ // Find out what to receive/send in compact addressing.
+
+ // What I want to receive is what others have to send
+ labelListList wantedRemoteElements(Pstream::nProcs());
+ // Compact addressing for received data
+ constructMap_.setSize(Pstream::nProcs());
+ forAll(compactMap, proci)
+ {
+ if (proci == Pstream::myProcNo())
+ {
+ // All my own elements are used
+ label nLocal = globalNumbering.localSize();
+ wantedRemoteElements[proci] = identity(nLocal);
+ constructMap_[proci] = identity(nLocal);
+ }
+ else
+ {
+ // Remote elements wanted from processor proci
+ labelList& remoteElem = wantedRemoteElements[proci];
+ labelList& localElem = constructMap_[proci];
+ remoteElem.setSize(compactMap[proci].size());
+ localElem.setSize(compactMap[proci].size());
+ label i = 0;
+ forAllIter(Map, compactMap[proci], iter)
+ {
+ const label compactI = compactStart[proci] + iter();
+ remoteElem[i] = iter.key();
+ localElem[i] = compactI;
+ iter() = compactI;
+ i++;
+ }
+ }
+ }
+
+ subMap_.setSize(Pstream::nProcs());
+ Pstream::exchange
+ (
+ wantedRemoteElements,
+ subMap_,
+ tag,
+ Pstream::worldComm // TBD
+ );
+
+ // Renumber elements
+ forAll(elements, i)
+ {
+ elements[i] = renumber(globalNumbering, compactMap, elements[i]);
+ }
+}
+
+
+void Foam::distributionMapBase::exchangeAddressing
+(
+ const int tag,
+ const globalIndex& globalNumbering,
+ labelListList& cellCells,
+ List>& compactMap,
+ labelList& compactStart
+)
+{
+ // The overall compact addressing is
+ // - myProcNo data first (uncompacted)
+ // - all other processors consecutively
+
+ compactStart.setSize(Pstream::nProcs());
+ compactStart[Pstream::myProcNo()] = 0;
+ constructSize_ = globalNumbering.localSize();
+ forAll(compactStart, proci)
+ {
+ if (proci != Pstream::myProcNo())
+ {
+ compactStart[proci] = constructSize_;
+ constructSize_ += compactMap[proci].size();
+ }
+ }
+
+
+
+ // Find out what to receive/send in compact addressing.
+
+ // What I want to receive is what others have to send
+ labelListList wantedRemoteElements(Pstream::nProcs());
+ // Compact addressing for received data
+ constructMap_.setSize(Pstream::nProcs());
+ forAll(compactMap, proci)
+ {
+ if (proci == Pstream::myProcNo())
+ {
+ // All my own elements are used
+ label nLocal = globalNumbering.localSize();
+ wantedRemoteElements[proci] = identity(nLocal);
+ constructMap_[proci] = identity(nLocal);
+ }
+ else
+ {
+ // Remote elements wanted from processor proci
+ labelList& remoteElem = wantedRemoteElements[proci];
+ labelList& localElem = constructMap_[proci];
+ remoteElem.setSize(compactMap[proci].size());
+ localElem.setSize(compactMap[proci].size());
+ label i = 0;
+ forAllIter(Map, compactMap[proci], iter)
+ {
+ const label compactI = compactStart[proci] + iter();
+ remoteElem[i] = iter.key();
+ localElem[i] = compactI;
+ iter() = compactI;
+ i++;
+ }
+ }
+ }
+
+ subMap_.setSize(Pstream::nProcs());
+ Pstream::exchange
+ (
+ wantedRemoteElements,
+ subMap_,
+ tag,
+ Pstream::worldComm // TBD
+ );
+
+ // Renumber elements
+ forAll(cellCells, cellI)
+ {
+ labelList& cCells = cellCells[cellI];
+
+ forAll(cCells, i)
+ {
+ cCells[i] = renumber(globalNumbering, compactMap, cCells[i]);
+ }
+ }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::distributionMapBase::distributionMapBase()
+:
+ constructSize_(0),
+ subHasFlip_(false),
+ constructHasFlip_(false),
+ schedulePtr_()
+{}
+
+
+Foam::distributionMapBase::distributionMapBase
+(
+ const label constructSize,
+ const labelListList&& subMap,
+ const labelListList&& constructMap,
+ const bool subHasFlip,
+ const bool constructHasFlip
+)
+:
+ constructSize_(constructSize),
+ subMap_(move(subMap)),
+ constructMap_(move(constructMap)),
+ subHasFlip_(subHasFlip),
+ constructHasFlip_(constructHasFlip),
+ schedulePtr_()
+{}
+
+
+Foam::distributionMapBase::distributionMapBase
+(
+ const labelList& sendProcs,
+ const labelList& recvProcs
+)
+:
+ constructSize_(0),
+ subHasFlip_(false),
+ constructHasFlip_(false),
+ schedulePtr_()
+{
+ if (sendProcs.size() != recvProcs.size())
+ {
+ FatalErrorInFunction
+ << "The send and receive data is not the same length. sendProcs:"
+ << sendProcs.size() << " recvProcs:" << recvProcs.size()
+ << abort(FatalError);
+ }
+
+ // Per processor the number of samples we have to send/receive.
+ labelList nSend(Pstream::nProcs(), 0);
+ labelList nRecv(Pstream::nProcs(), 0);
+
+ forAll(sendProcs, sampleI)
+ {
+ label sendProc = sendProcs[sampleI];
+ label recvProc = recvProcs[sampleI];
+
+ // Note that also need to include local communication (both
+ // RecvProc and sendProc on local processor)
+
+ if (Pstream::myProcNo() == sendProc)
+ {
+ // I am the sender. Count destination processor.
+ nSend[recvProc]++;
+ }
+ if (Pstream::myProcNo() == recvProc)
+ {
+ // I am the receiver.
+ nRecv[sendProc]++;
+ }
+ }
+
+ subMap_.setSize(Pstream::nProcs());
+ constructMap_.setSize(Pstream::nProcs());
+ forAll(nSend, proci)
+ {
+ subMap_[proci].setSize(nSend[proci]);
+ constructMap_[proci].setSize(nRecv[proci]);
+ }
+ nSend = 0;
+ nRecv = 0;
+
+ forAll(sendProcs, sampleI)
+ {
+ label sendProc = sendProcs[sampleI];
+ label recvProc = recvProcs[sampleI];
+
+ if (Pstream::myProcNo() == sendProc)
+ {
+ // I am the sender. Store index I need to send.
+ subMap_[recvProc][nSend[recvProc]++] = sampleI;
+ }
+ if (Pstream::myProcNo() == recvProc)
+ {
+ // I am the receiver.
+ constructMap_[sendProc][nRecv[sendProc]++] = sampleI;
+ // Largest entry inside constructMap
+ constructSize_ = sampleI+1;
+ }
+ }
+}
+
+
+Foam::distributionMapBase::distributionMapBase
+(
+ const globalIndex& globalNumbering,
+ labelList& elements,
+ List>& compactMap,
+ const int tag
+)
+:
+ constructSize_(0),
+ subHasFlip_(false),
+ constructHasFlip_(false),
+ schedulePtr_()
+{
+ // Construct per processor compact addressing of the global elements
+ // needed. The ones from the local processor are not included since
+ // these are always all needed.
+ calcCompactAddressing
+ (
+ globalNumbering,
+ elements,
+ compactMap
+ );
+
+ //// Sort remote elements needed (not really necessary)
+ // forAll(compactMap, proci)
+ //{
+ // if (proci != Pstream::myProcNo())
+ // {
+ // Map& globalMap = compactMap[proci];
+ //
+ // SortableList sorted(move(globalMap.toc()));
+ //
+ // forAll(sorted, i)
+ // {
+ // Map::iterator iter = globalMap.find(sorted[i]);
+ // iter() = i;
+ // }
+ // }
+ //}
+
+
+ // Exchange what I need with processor that supplies it. Renumber elements
+ // into compact numbering
+ labelList compactStart;
+ exchangeAddressing
+ (
+ tag,
+ globalNumbering,
+ elements,
+ compactMap,
+ compactStart
+ );
+
+ if (debug)
+ {
+ printLayout(Pout);
+ }
+}
+
+
+Foam::distributionMapBase::distributionMapBase
+(
+ const globalIndex& globalNumbering,
+ labelListList& cellCells,
+ List>& compactMap,
+ const int tag
+)
+:
+ constructSize_(0),
+ subHasFlip_(false),
+ constructHasFlip_(false),
+ schedulePtr_()
+{
+ // Construct per processor compact addressing of the global elements
+ // needed. The ones from the local processor are not included since
+ // these are always all needed.
+ calcCompactAddressing
+ (
+ globalNumbering,
+ cellCells,
+ compactMap
+ );
+
+ //// Sort remote elements needed (not really necessary)
+ // forAll(compactMap, proci)
+ //{
+ // if (proci != Pstream::myProcNo())
+ // {
+ // Map& globalMap = compactMap[proci];
+ //
+ // SortableList sorted(move(globalMap.toc()));
+ //
+ // forAll(sorted, i)
+ // {
+ // Map::iterator iter = globalMap.find(sorted[i]);
+ // iter() = i;
+ // }
+ // }
+ //}
+
+
+ // Exchange what I need with processor that supplies it. Renumber elements
+ // into compact numbering
+ labelList compactStart;
+ exchangeAddressing
+ (
+ tag,
+ globalNumbering,
+ cellCells,
+ compactMap,
+ compactStart
+ );
+
+ if (debug)
+ {
+ printLayout(Pout);
+ }
+}
+
+
+Foam::distributionMapBase::distributionMapBase(const distributionMapBase& map)
+:
+ constructSize_(map.constructSize_),
+ subMap_(map.subMap_),
+ constructMap_(map.constructMap_),
+ subHasFlip_(map.subHasFlip_),
+ constructHasFlip_(map.constructHasFlip_),
+ schedulePtr_()
+{}
+
+
+Foam::distributionMapBase::distributionMapBase(distributionMapBase&& map)
+:
+ constructSize_(map.constructSize_),
+ subMap_(move(map.subMap_)),
+ constructMap_(move(map.constructMap_)),
+ subHasFlip_(map.subHasFlip_),
+ constructHasFlip_(map.constructHasFlip_),
+ schedulePtr_()
+{}
+
+
+Foam::distributionMapBase::distributionMapBase(Istream& is)
+{
+ is >> *this;
+}
+
+
+// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
+
+void Foam::distributionMapBase::transfer(distributionMapBase& rhs)
+{
+ constructSize_ = rhs.constructSize_;
+ subMap_.transfer(rhs.subMap_);
+ constructMap_.transfer(rhs.constructMap_);
+ subHasFlip_ = rhs.subHasFlip_;
+ constructHasFlip_ = rhs.constructHasFlip_;
+ schedulePtr_.clear();
+}
+
+
+Foam::label Foam::distributionMapBase::renumber
+(
+ const globalIndex& globalNumbering,
+ const List>& compactMap,
+ const label globalI
+)
+{
+ if (globalI == -1)
+ {
+ return globalI;
+ }
+ if (globalNumbering.isLocal(globalI))
+ {
+ return globalNumbering.toLocal(globalI);
+ }
+ else
+ {
+ label proci = globalNumbering.whichProcID(globalI);
+ label index = globalNumbering.toLocal(proci, globalI);
+ return compactMap[proci][index];
+ }
+}
+
+
+void Foam::distributionMapBase::compact
+(
+ const boolList& elemIsUsed,
+ const int tag
+)
+{
+ // 1. send back to sender. Have sender delete the corresponding element
+ // from the submap and do the same to the constructMap locally
+ // (and in same order).
+
+ // Send elemIsUsed field to neighbour. Use nonblocking code from
+ // distributionMapBase but in reverse order.
+ if (Pstream::parRun())
+ {
+ label startOfRequests = Pstream::nRequests();
+
+ // Set up receives from neighbours
+
+ List recvFields(Pstream::nProcs());
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap_[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ recvFields[domain].setSize(map.size());
+ IPstream::read
+ (
+ Pstream::commsTypes::nonBlocking,
+ domain,
+ reinterpret_cast(recvFields[domain].begin()),
+ recvFields[domain].size()*sizeof(bool),
+ tag
+ );
+ }
+ }
+
+
+ List sendFields(Pstream::nProcs());
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap_[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ boolList& subField = sendFields[domain];
+ subField.setSize(map.size());
+ forAll(map, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ elemIsUsed,
+ map[i],
+ constructHasFlip_,
+ noOp() // do not flip elemIsUsed value
+ );
+ }
+
+ OPstream::write
+ (
+ Pstream::commsTypes::nonBlocking,
+ domain,
+ reinterpret_cast(subField.begin()),
+ subField.size()*sizeof(bool),
+ tag
+ );
+ }
+ }
+
+
+
+ // Set up 'send' to myself - write directly into recvFields
+
+ {
+ const labelList& map = constructMap_[Pstream::myProcNo()];
+
+ recvFields[Pstream::myProcNo()].setSize(map.size());
+ forAll(map, i)
+ {
+ recvFields[Pstream::myProcNo()][i] = accessAndFlip
+ (
+ elemIsUsed,
+ map[i],
+ constructHasFlip_,
+ noOp() // do not flip elemIsUsed value
+ );
+ }
+ }
+
+
+ // Wait for all to finish
+
+ Pstream::waitRequests(startOfRequests);
+
+
+ // Compact out all submap entries that are referring to unused elements
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap_[domain];
+
+ labelList newMap(map.size());
+ label newI = 0;
+
+ forAll(map, i)
+ {
+ if (recvFields[domain][i])
+ {
+ // So element is used on destination side
+ newMap[newI++] = map[i];
+ }
+ }
+ if (newI < map.size())
+ {
+ newMap.setSize(newI);
+ subMap_[domain].transfer(newMap);
+ }
+ }
+ }
+
+
+ // 2. remove from construct map - since end-result (element in elemIsUsed)
+ // not used.
+
+ label maxConstructIndex = -1;
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap_[domain];
+
+ labelList newMap(map.size());
+ label newI = 0;
+
+ forAll(map, i)
+ {
+ label destinationI = map[i];
+ if (constructHasFlip_)
+ {
+ destinationI = mag(destinationI)-1;
+ }
+
+ // Is element is used on destination side
+ if (elemIsUsed[destinationI])
+ {
+ maxConstructIndex = max(maxConstructIndex, destinationI);
+
+ newMap[newI++] = map[i];
+ }
+ }
+ if (newI < map.size())
+ {
+ newMap.setSize(newI);
+ constructMap_[domain].transfer(newMap);
+ }
+ }
+
+ constructSize_ = maxConstructIndex+1;
+
+ // Clear the schedule (note:not necessary if nothing changed)
+ schedulePtr_.clear();
+}
+
+
+void Foam::distributionMapBase::compact
+(
+ const boolList& elemIsUsed,
+ const label localSize, // max index for subMap
+ labelList& oldToNewSub,
+ labelList& oldToNewConstruct,
+ const int tag
+)
+{
+ // 1. send back to sender. Have sender delete the corresponding element
+ // from the submap and do the same to the constructMap locally
+ // (and in same order).
+
+ // Send elemIsUsed field to neighbour. Use nonblocking code from
+ // distributionMapBase but in reverse order.
+ if (Pstream::parRun())
+ {
+ label startOfRequests = Pstream::nRequests();
+
+ // Set up receives from neighbours
+
+ List recvFields(Pstream::nProcs());
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap_[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ recvFields[domain].setSize(map.size());
+ IPstream::read
+ (
+ Pstream::commsTypes::nonBlocking,
+ domain,
+ reinterpret_cast(recvFields[domain].begin()),
+ recvFields[domain].size()*sizeof(bool),
+ tag
+ );
+ }
+ }
+
+
+ List sendFields(Pstream::nProcs());
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap_[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ boolList& subField = sendFields[domain];
+ subField.setSize(map.size());
+ forAll(map, i)
+ {
+ label index = map[i];
+ if (constructHasFlip_)
+ {
+ index = mag(index)-1;
+ }
+ subField[i] = elemIsUsed[index];
+ }
+
+ OPstream::write
+ (
+ Pstream::commsTypes::nonBlocking,
+ domain,
+ reinterpret_cast(subField.begin()),
+ subField.size()*sizeof(bool),
+ tag
+ );
+ }
+ }
+
+
+
+ // Set up 'send' to myself - write directly into recvFields
+
+ {
+ const labelList& map = constructMap_[Pstream::myProcNo()];
+
+ recvFields[Pstream::myProcNo()].setSize(map.size());
+ forAll(map, i)
+ {
+ label index = map[i];
+ if (constructHasFlip_)
+ {
+ index = mag(index)-1;
+ }
+ recvFields[Pstream::myProcNo()][i] = elemIsUsed[index];
+ }
+ }
+
+
+ // Wait for all to finish
+
+ Pstream::waitRequests(startOfRequests);
+
+
+
+
+ // Work out which elements on the sending side are needed
+ {
+ oldToNewSub.setSize(localSize, -1);
+
+ boolList sendElemIsUsed(localSize, false);
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap_[domain];
+ forAll(map, i)
+ {
+ if (recvFields[domain][i])
+ {
+ label index = map[i];
+ if (subHasFlip_)
+ {
+ index = mag(index)-1;
+ }
+ sendElemIsUsed[index] = true;
+ }
+ }
+ }
+
+ label newI = 0;
+ forAll(sendElemIsUsed, i)
+ {
+ if (sendElemIsUsed[i])
+ {
+ oldToNewSub[i] = newI++;
+ }
+ }
+ }
+
+
+ // Compact out all submap entries that are referring to unused elements
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap_[domain];
+
+ labelList newMap(map.size());
+ label newI = 0;
+
+ forAll(map, i)
+ {
+ if (recvFields[domain][i])
+ {
+ // So element is used on destination side
+ label index = map[i];
+ label sign = 1;
+ if (subHasFlip_)
+ {
+ if (index < 0)
+ {
+ sign = -1;
+ }
+ index = mag(index)-1;
+ }
+ label newIndex = oldToNewSub[index];
+ if (subHasFlip_)
+ {
+ newIndex = sign*(newIndex+1);
+ }
+ newMap[newI++] = newIndex;
+ }
+ }
+ newMap.setSize(newI);
+ subMap_[domain].transfer(newMap);
+ }
+ }
+
+
+ // 2. remove from construct map - since end-result (element in elemIsUsed)
+ // not used.
+
+
+ oldToNewConstruct.setSize(elemIsUsed.size(), -1);
+ constructSize_ = 0;
+ forAll(elemIsUsed, i)
+ {
+ if (elemIsUsed[i])
+ {
+ oldToNewConstruct[i] = constructSize_++;
+ }
+ }
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap_[domain];
+
+ labelList newMap(map.size());
+ label newI = 0;
+
+ forAll(map, i)
+ {
+ label destinationI = map[i];
+ label sign = 1;
+ if (constructHasFlip_)
+ {
+ if (destinationI < 0)
+ {
+ sign = -1;
+ }
+ destinationI = mag(destinationI)-1;
+ }
+
+ // Is element is used on destination side
+ if (elemIsUsed[destinationI])
+ {
+ label newIndex = oldToNewConstruct[destinationI];
+ if (constructHasFlip_)
+ {
+ newIndex = sign*(newIndex+1);
+ }
+ newMap[newI++] = newIndex;
+ }
+ }
+ newMap.setSize(newI);
+ constructMap_[domain].transfer(newMap);
+ }
+}
+
+
+// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
+
+void Foam::distributionMapBase::operator=(const distributionMapBase& rhs)
+{
+ // Check for assignment to self
+ if (this == &rhs)
+ {
+ FatalErrorInFunction
+ << "Attempted assignment to self"
+ << abort(FatalError);
+ }
+ constructSize_ = rhs.constructSize_;
+ subMap_ = rhs.subMap_;
+ constructMap_ = rhs.constructMap_;
+ subHasFlip_ = rhs.subHasFlip_;
+ constructHasFlip_ = rhs.constructHasFlip_;
+ schedulePtr_.clear();
+}
+
+
+// * * * * * * * * * * * * * * Istream Operator * * * * * * * * * * * * * * //
+
+Foam::Istream& Foam::operator>>(Istream& is, distributionMapBase& map)
+{
+ is.fatalCheck("operator>>(Istream&, distributionMapBase&)");
+
+ is >> map.constructSize_ >> map.subMap_ >> map.constructMap_
+ >> map.subHasFlip_ >> map.constructHasFlip_;
+
+ return is;
+}
+
+
+// * * * * * * * * * * * * * * Ostream Operator * * * * * * * * * * * * * * //
+
+Foam::Ostream& Foam::operator<<(Ostream& os, const distributionMapBase& map)
+{
+ os << map.constructSize_ << token::NL
+ << map.subMap_ << token::NL
+ << map.constructMap_ << token::NL
+ << map.subHasFlip_ << token::SPACE << map.constructHasFlip_
+ << token::NL;
+
+ return os;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapBase.H b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapBase.H
new file mode 100644
index 0000000000..95ea2af1bf
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapBase.H
@@ -0,0 +1,495 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration | Website: https://openfoam.org
+ \\ / A nd | Copyright (C) 2015-2022 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 .
+
+Class
+ Foam::distributionMapBase
+
+Description
+ Class containing processor-to-processor mapping information.
+
+ We store mapping from the bits-to-send to the complete starting list
+ (subXXXMap) and from the received bits to their location in the new
+ list (constructXXXMap).
+
+Note:
+ Schedule is a list of processor pairs (one send, one receive. One of
+ them will be myself) which forms a scheduled (i.e. non-buffered) exchange.
+ See distribute on how to use it.
+ Note2: number of items sent on one processor have to equal the number
+ of items received on the other processor.
+
+ To aid constructing these maps there are the constructors from global
+ numbering, either with or without transforms.
+
+ Constructors using compact numbering: layout is
+ - all my own elements first (whether used or not)
+ - followed by used-only remote elements sorted by remote processor.
+ So e.g 4 procs and on proc 1 the compact
+ table will first have all globalIndex.localSize() elements from proc1
+ followed by used-only elements of proc0, proc2, proc3.
+ The constructed distributionMapBase sends the local elements from and
+ receives the remote elements into their compact position.
+ compactMap[proci] is the position of elements from proci in the compact
+ map. compactMap[myProcNo()] is empty since trivial addressing.
+
+ It rewrites the input global indices into indices into the constructed
+ data.
+
+ When constructing from components optionally a 'flip' on
+ the maps can be specified. This will interpret the map
+ values as index+flip, similar to e.g. faceProcAddressing. The flip
+ will only be applied to fieldTypes (scalar, vector, .. triad)
+
+
+SourceFiles
+ distributionMapBase.C
+ distributionMapBaseTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef distributionMapBase_H
+#define distributionMapBase_H
+
+#include "labelList.H"
+#include "labelPair.H"
+#include "Pstream.H"
+#include "boolList.H"
+#include "Map.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+class polyTopoChangeMap;
+class globalIndex;
+class PstreamBuffers;
+
+
+// Forward declaration of friend functions and operators
+
+class distributionMapBase;
+
+Istream& operator>>(Istream&, distributionMapBase&);
+Ostream& operator<<(Ostream&, const distributionMapBase&);
+
+
+/*---------------------------------------------------------------------------*\
+ Class distributionMapBase Declaration
+\*---------------------------------------------------------------------------*/
+
+class distributionMapBase
+{
+protected:
+
+ // Protected data
+
+ //- Size of reconstructed data
+ label constructSize_;
+
+ //- Maps from subsetted data back to original data
+ labelListList subMap_;
+
+ //- Maps from subsetted data to new reconstructed data
+ labelListList constructMap_;
+
+ //- Whether subMap includes flip or not
+ bool subHasFlip_;
+
+ //- Whether constructMap includes flip or not
+ bool constructHasFlip_;
+
+
+ //- Schedule
+ mutable autoPtr> schedulePtr_;
+
+
+ // Private Member Functions
+
+ static void checkReceivedSize
+ (
+ const label proci,
+ const label expectedSize,
+ const label receivedSize
+ );
+
+ //- Construct per processor compact addressing of the global elements
+ // needed. The ones from the local processor are not included since
+ // these are always all needed.
+ void calcCompactAddressing
+ (
+ const globalIndex& globalNumbering,
+ const labelList& elements,
+ List>& compactMap
+ ) const;
+
+ void calcCompactAddressing
+ (
+ const globalIndex& globalNumbering,
+ const labelListList& elements,
+ List>& compactMap
+ ) const;
+
+ void exchangeAddressing
+ (
+ const int tag,
+ const globalIndex& globalNumbering,
+ labelList& elements,
+ List>& compactMap,
+ labelList& compactStart
+ );
+ void exchangeAddressing
+ (
+ const int tag,
+ const globalIndex& globalNumbering,
+ labelListList& elements,
+ List>& compactMap,
+ labelList& compactStart
+ );
+
+ template
+ static void flipAndCombine
+ (
+ const UList& map,
+ const bool hasFlip,
+ const UList& rhs,
+ const CombineOp& cop,
+ const negateOp& negOp,
+ List& lhs
+ );
+
+ template
+ static T accessAndFlip
+ (
+ const UList& fld,
+ const label index,
+ const bool hasFlip,
+ const negateOp& negOp
+ );
+
+public:
+
+ // Declare name of the class and its debug switch
+ ClassName("distributionMapBase");
+
+
+ // Constructors
+
+ //- Construct null
+ distributionMapBase();
+
+ //- Move construct from components
+ distributionMapBase
+ (
+ const label constructSize,
+ const labelListList&& subMap,
+ const labelListList&& constructMap,
+ const bool subHasFlip = false,
+ const bool constructHasFlip = false
+ );
+
+ //- Construct from reverse addressing: per data item the send
+ // processor and the receive processor. (note: data is not stored
+ // sorted per processor so cannot use printLayout).
+ distributionMapBase
+ (
+ const labelList& sendProcs,
+ const labelList& recvProcs
+ );
+
+ //- Construct from list of (possibly) remote elements in globalIndex
+ // numbering (or -1). Determines compact numbering (see above) and
+ // distribute map to get data into this ordering and renumbers the
+ // elements to be in compact numbering.
+ distributionMapBase
+ (
+ const globalIndex&,
+ labelList& elements,
+ List>& compactMap,
+ const int tag = Pstream::msgType()
+ );
+
+ //- Special variant that works with the info sorted into bins
+ // according to local indices. E.g. think cellCells where
+ // cellCells[localCellI] is a list of global cells
+ distributionMapBase
+ (
+ const globalIndex&,
+ labelListList& cellCells,
+ List>& compactMap,
+ const int tag = Pstream::msgType()
+ );
+
+ //- Construct copy
+ distributionMapBase(const distributionMapBase&);
+
+ //- Move constructor
+ distributionMapBase(distributionMapBase&&);
+
+ //- Construct from Istream
+ distributionMapBase(Istream&);
+
+
+ // Member Functions
+
+ // Access
+
+ //- Constructed data size
+ label constructSize() const
+ {
+ return constructSize_;
+ }
+
+ //- Constructed data size
+ label& constructSize()
+ {
+ return constructSize_;
+ }
+
+ //- From subsetted data back to original data
+ const labelListList& subMap() const
+ {
+ return subMap_;
+ }
+
+ //- From subsetted data back to original data
+ labelListList& subMap()
+ {
+ return subMap_;
+ }
+
+ //- From subsetted data to new reconstructed data
+ const labelListList& constructMap() const
+ {
+ return constructMap_;
+ }
+
+ //- From subsetted data to new reconstructed data
+ labelListList& constructMap()
+ {
+ return constructMap_;
+ }
+
+ //- Does subMap include a sign
+ bool subHasFlip() const
+ {
+ return subHasFlip_;
+ }
+
+ //- Does subMap include a sign
+ bool& subHasFlip()
+ {
+ return subHasFlip_;
+ }
+
+ //- Does constructMap include a sign
+ bool constructHasFlip() const
+ {
+ return constructHasFlip_;
+ }
+
+ //- Does constructMap include a sign
+ bool& constructHasFlip()
+ {
+ return constructHasFlip_;
+ }
+
+ //- Calculate a schedule. See above.
+ static List schedule
+ (
+ const labelListList& subMap,
+ const labelListList& constructMap,
+ const int tag
+ );
+
+ //- Return a schedule. Demand driven. See above.
+ const List& schedule() const;
+
+
+ // Other
+
+ //- Transfer the contents of the argument and annul the argument.
+ void transfer(distributionMapBase&);
+
+ //- Helper for construct from globalIndex. Renumbers element
+ // (in globalIndex numbering) into compact indices.
+ static label renumber
+ (
+ const globalIndex&,
+ const List>& compactMap,
+ const label globalElement
+ );
+
+ //- Compact maps. Gets per field a bool whether it is used (locally)
+ // and works out itself what this side and sender side can remove
+ // from maps. Only compacts non-local elements (i.e. the stuff
+ // that gets sent over), does not change the local layout
+ void compact
+ (
+ const boolList& elemIsUsed,
+ const int tag = UPstream::msgType()
+ );
+
+ //- Compact all maps and layout. Returns compaction maps for
+ // subMap and constructMap
+ void compact
+ (
+ const boolList& elemIsUsed,
+ const label localSize, // max index for subMap
+ labelList& oldToNewSub,
+ labelList& oldToNewConstruct,
+ const int tag = UPstream::msgType()
+ );
+
+ //- Distribute data. Note:schedule only used for
+ // Pstream::commsTypes::scheduled for now, all others just use
+ // send-to-all, receive-from-all.
+ template
+ static void distribute
+ (
+ const Pstream::commsTypes commsType,
+ const List& schedule,
+ const label constructSize,
+ const labelListList& subMap,
+ const bool subHasFlip,
+ const labelListList& constructMap,
+ const bool constructHasFlip,
+ List&,
+ const negateOp& negOp,
+ const int tag = UPstream::msgType()
+ );
+
+ //- Distribute data. If multiple processors writing to same
+ // position adds contributions using cop.
+ template
+ static void distribute
+ (
+ const Pstream::commsTypes commsType,
+ const List& schedule,
+ const label constructSize,
+ const labelListList& subMap,
+ const bool subHasFlip,
+ const labelListList& constructMap,
+ const bool constructHasFlip,
+ List&,
+ const CombineOp& cop,
+ const negateOp& negOp,
+ const T& nullValue,
+ const int tag = UPstream::msgType()
+ );
+
+ //- Distribute data using default commsType.
+ template
+ void distribute
+ (
+ List& fld,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Distribute data using default commsType.
+ template
+ void distribute
+ (
+ List& fld,
+ const negateOp& negOp,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Distribute data using default commsType.
+ template
+ void distribute
+ (
+ DynamicList& fld,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Reverse distribute data using default commsType.
+ template
+ void reverseDistribute
+ (
+ const label constructSize,
+ List&,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Reverse distribute data using default commsType.
+ // Since constructSize might be larger than supplied size supply
+ // a nullValue
+ template
+ void reverseDistribute
+ (
+ const label constructSize,
+ const T& nullValue,
+ List& fld,
+ const int tag = UPstream::msgType()
+ ) const;
+
+ //- Do all sends using PstreamBuffers
+ template
+ void send(PstreamBuffers&, const List&) const;
+ //- Do all receives using PstreamBuffers
+ template
+ void receive(PstreamBuffers&, List&) const;
+
+ //- Debug: print layout. Can only be used on maps with sorted
+ // storage (local data first, then non-local data)
+ void printLayout(Ostream& os) const;
+
+ //- Correct for topo change.
+ void updateMesh(const polyTopoChangeMap&)
+ {
+ NotImplemented;
+ }
+
+
+ // Member Operators
+
+ void operator=(const distributionMapBase&);
+ void operator=(distributionMapBase&&);
+
+
+ // IOstream Operators
+
+ //- Read dictionary from Istream
+ friend Istream& operator>>(Istream&, distributionMapBase&);
+
+ //- Write dictionary to Ostream
+ friend Ostream& operator<<(Ostream&, const distributionMapBase&);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+ #include "distributionMapBaseTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapBaseTemplates.C b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapBaseTemplates.C
new file mode 100644
index 0000000000..a0c263079c
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapBaseTemplates.C
@@ -0,0 +1,1420 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration | Website: https://openfoam.org
+ \\ / A nd | Copyright (C) 2015-2022 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 "Pstream.H"
+#include "PstreamBuffers.H"
+#include "PstreamCombineReduceOps.H"
+#include "flipOp.H"
+
+// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
+
+template
+void Foam::distributionMapBase::flipAndCombine
+(
+ const UList& map,
+ const bool hasFlip,
+ const UList& rhs,
+ const CombineOp& cop,
+ const negateOp& negOp,
+ List& lhs
+)
+{
+ if (hasFlip)
+ {
+ forAll(map, i)
+ {
+ if (map[i] > 0)
+ {
+ label index = map[i]-1;
+ cop(lhs[index], rhs[i]);
+ }
+ else if (map[i] < 0)
+ {
+ label index = -map[i]-1;
+ cop(lhs[index], negOp(rhs[i]));
+ }
+ else
+ {
+ FatalErrorInFunction
+ << "At index " << i << " out of " << map.size()
+ << " have illegal index " << map[i]
+ << " for field " << rhs.size() << " with flipMap"
+ << exit(FatalError);
+ }
+ }
+ }
+ else
+ {
+ forAll(map, i)
+ {
+ cop(lhs[map[i]], rhs[i]);
+ }
+ }
+}
+
+
+template
+T Foam::distributionMapBase::accessAndFlip
+(
+ const UList& fld,
+ const label index,
+ const bool hasFlip,
+ const negateOp& negOp
+)
+{
+ T t;
+ if (hasFlip)
+ {
+ if (index > 0)
+ {
+ t = fld[index-1];
+ }
+ else if (index < 0)
+ {
+ t = negOp(fld[-index-1]);
+ }
+ else
+ {
+ FatalErrorInFunction
+ << "Illegal index " << index
+ << " into field of size " << fld.size()
+ << " with face-flipping"
+ << exit(FatalError);
+ t = fld[index];
+ }
+ }
+ else
+ {
+ t = fld[index];
+ }
+ return t;
+}
+
+
+// Distribute list.
+template
+void Foam::distributionMapBase::distribute
+(
+ const Pstream::commsTypes commsType,
+ const List& schedule,
+ const label constructSize,
+ const labelListList& subMap,
+ const bool subHasFlip,
+ const labelListList& constructMap,
+ const bool constructHasFlip,
+ List& field,
+ const negateOp& negOp,
+ const int tag
+)
+{
+ if (!Pstream::parRun())
+ {
+ // Do only me to me.
+
+ const labelList& mySubMap = subMap[Pstream::myProcNo()];
+
+ List subField(mySubMap.size());
+ forAll(mySubMap, i)
+ {
+ subField[i] = accessAndFlip(field, mySubMap[i], subHasFlip, negOp);
+ }
+
+ // Receive sub field from myself (subField)
+ const labelList& map = constructMap[Pstream::myProcNo()];
+
+ field.setSize(constructSize);
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ eqOp(),
+ negOp,
+ field
+ );
+
+ return;
+ }
+
+ if (commsType == Pstream::commsTypes::blocking)
+ {
+ // Since buffered sending can reuse the field to collect the
+ // received data.
+
+ // Send sub field to neighbour
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ OPstream toNbr(Pstream::commsTypes::blocking, domain, 0, tag);
+
+ List subField(map.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ toNbr << subField;
+ }
+ }
+
+ // Subset myself
+ const labelList& mySubMap = subMap[Pstream::myProcNo()];
+
+ List subField(mySubMap.size());
+ forAll(mySubMap, i)
+ {
+ subField[i] = accessAndFlip(field, mySubMap[i], subHasFlip, negOp);
+ }
+
+ // Receive sub field from myself (subField)
+ const labelList& map = constructMap[Pstream::myProcNo()];
+
+ field.setSize(constructSize);
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ eqOp(),
+ negOp,
+ field
+ );
+
+ // Receive sub field from neighbour
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ IPstream fromNbr(Pstream::commsTypes::blocking, domain, 0, tag);
+ List subField(fromNbr);
+
+ checkReceivedSize(domain, map.size(), subField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ eqOp(),
+ negOp,
+ field
+ );
+ }
+ }
+ }
+ else if (commsType == Pstream::commsTypes::scheduled)
+ {
+ // Need to make sure I don't overwrite field with received data
+ // since the data might need to be sent to another processor. So
+ // allocate a new field for the results.
+ List newField(constructSize);
+
+ // Receive sub field from myself
+ {
+ const labelList& mySubMap = subMap[Pstream::myProcNo()];
+
+ List subField(mySubMap.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ mySubMap[i],
+ subHasFlip,
+ negOp
+ );
+ }
+
+ // Receive sub field from myself (subField)
+ flipAndCombine
+ (
+ constructMap[Pstream::myProcNo()],
+ constructHasFlip,
+ subField,
+ eqOp(),
+ negOp,
+ newField
+ );
+ }
+
+ // Schedule will already have pruned 0-sized comms
+ forAll(schedule, i)
+ {
+ const labelPair& twoProcs = schedule[i];
+ // twoProcs is a swap pair of processors. The first one is the
+ // one that needs to send first and then receive.
+
+ label sendProc = twoProcs[0];
+ label recvProc = twoProcs[1];
+
+ if (Pstream::myProcNo() == sendProc)
+ {
+ // I am send first, receive next
+ {
+ OPstream toNbr
+ (
+ Pstream::commsTypes::scheduled,
+ recvProc,
+ 0,
+ tag
+ );
+
+ const labelList& map = subMap[recvProc];
+ List subField(map.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ toNbr << subField;
+ }
+ {
+ IPstream fromNbr
+ (
+ Pstream::commsTypes::scheduled,
+ recvProc,
+ 0,
+ tag
+ );
+ List subField(fromNbr);
+
+ const labelList& map = constructMap[recvProc];
+
+ checkReceivedSize(recvProc, map.size(), subField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ eqOp(),
+ negOp,
+ newField
+ );
+ }
+ }
+ else
+ {
+ // I am receive first, send next
+ {
+ IPstream fromNbr
+ (
+ Pstream::commsTypes::scheduled,
+ sendProc,
+ 0,
+ tag
+ );
+ List subField(fromNbr);
+
+ const labelList& map = constructMap[sendProc];
+
+ checkReceivedSize(sendProc, map.size(), subField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ eqOp(),
+ negOp,
+ newField
+ );
+ }
+ {
+ OPstream toNbr
+ (
+ Pstream::commsTypes::scheduled,
+ sendProc,
+ 0,
+ tag
+ );
+
+ const labelList& map = subMap[sendProc];
+ List subField(map.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ toNbr << subField;
+ }
+ }
+ }
+ field.transfer(newField);
+ }
+ else if (commsType == Pstream::commsTypes::nonBlocking)
+ {
+ label nOutstanding = Pstream::nRequests();
+
+ if (!contiguous())
+ {
+ PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking, tag);
+
+ // Stream data into buffer
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ // Put data into send buffer
+ UOPstream toDomain(domain, pBufs);
+
+ List subField(map.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ toDomain << subField;
+ }
+ }
+
+ // Start receiving. Do not block.
+ pBufs.finishedSends(false);
+
+ {
+ // Set up 'send' to myself
+ const labelList& mySub = subMap[Pstream::myProcNo()];
+ List mySubField(mySub.size());
+ forAll(mySub, i)
+ {
+ mySubField[i] = accessAndFlip
+ (
+ field,
+ mySub[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ // Combine bits. Note that can reuse field storage
+ field.setSize(constructSize);
+ // Receive sub field from myself
+ {
+ const labelList& map = constructMap[Pstream::myProcNo()];
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ mySubField,
+ eqOp(),
+ negOp,
+ field
+ );
+ }
+ }
+
+ // Block ourselves, waiting only for the current comms
+ Pstream::waitRequests(nOutstanding);
+
+ // Consume
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ UIPstream str(domain, pBufs);
+ List recvField(str);
+
+ checkReceivedSize(domain, map.size(), recvField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ recvField,
+ eqOp(),
+ negOp,
+ field
+ );
+ }
+ }
+ }
+ else
+ {
+ // Set up sends to neighbours
+
+ List> sendFields(Pstream::nProcs());
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ List& subField = sendFields[domain];
+ subField.setSize(map.size());
+ forAll(map, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+
+ OPstream::write
+ (
+ Pstream::commsTypes::nonBlocking,
+ domain,
+ reinterpret_cast(subField.begin()),
+ subField.byteSize(),
+ tag
+ );
+ }
+ }
+
+ // Set up receives from neighbours
+
+ List> recvFields(Pstream::nProcs());
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ recvFields[domain].setSize(map.size());
+ IPstream::read
+ (
+ Pstream::commsTypes::nonBlocking,
+ domain,
+ reinterpret_cast(recvFields[domain].begin()),
+ recvFields[domain].byteSize(),
+ tag
+ );
+ }
+ }
+
+
+ // Set up 'send' to myself
+
+ {
+ const labelList& map = subMap[Pstream::myProcNo()];
+
+ List& subField = sendFields[Pstream::myProcNo()];
+ subField.setSize(map.size());
+ forAll(map, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ }
+
+
+ // Combine bits. Note that can reuse field storage
+
+ field.setSize(constructSize);
+
+
+ // Receive sub field from myself (sendFields[Pstream::myProcNo()])
+ {
+ const labelList& map = constructMap[Pstream::myProcNo()];
+ const List& subField = sendFields[Pstream::myProcNo()];
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ eqOp(),
+ negOp,
+ field
+ );
+ }
+
+
+ // Wait for all to finish
+
+ Pstream::waitRequests(nOutstanding);
+
+
+ // Collect neighbour fields
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ const List& subField = recvFields[domain];
+
+ checkReceivedSize(domain, map.size(), subField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ eqOp(),
+ negOp,
+ field
+ );
+ }
+ }
+ }
+ }
+ else
+ {
+ FatalErrorInFunction
+ << "Unknown communication schedule " << int(commsType)
+ << abort(FatalError);
+ }
+}
+
+
+// Distribute list.
+template
+void Foam::distributionMapBase::distribute
+(
+ const Pstream::commsTypes commsType,
+ const List& schedule,
+ const label constructSize,
+ const labelListList& subMap,
+ const bool subHasFlip,
+ const labelListList& constructMap,
+ const bool constructHasFlip,
+ List& field,
+ const CombineOp& cop,
+ const negateOp& negOp,
+ const T& nullValue,
+ const int tag
+)
+{
+ if (!Pstream::parRun())
+ {
+ // Do only me to me.
+
+ const labelList& mySubMap = subMap[Pstream::myProcNo()];
+
+ List subField(mySubMap.size());
+ forAll(mySubMap, i)
+ {
+ subField[i] = accessAndFlip(field, mySubMap[i], subHasFlip, negOp);
+ }
+
+ // Receive sub field from myself (subField)
+ const labelList& map = constructMap[Pstream::myProcNo()];
+
+ field.setSize(constructSize);
+ field = nullValue;
+
+ flipAndCombine(map, constructHasFlip, subField, cop, negOp, field);
+
+ return;
+ }
+
+ if (commsType == Pstream::commsTypes::blocking)
+ {
+ // Since buffered sending can reuse the field to collect the
+ // received data.
+
+ // Send sub field to neighbour
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ OPstream toNbr(Pstream::commsTypes::blocking, domain, 0, tag);
+ List subField(map.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ toNbr << subField;
+ }
+ }
+
+ // Subset myself
+ const labelList& mySubMap = subMap[Pstream::myProcNo()];
+
+ List subField(mySubMap.size());
+ forAll(mySubMap, i)
+ {
+ subField[i] = accessAndFlip(field, mySubMap[i], subHasFlip, negOp);
+ }
+
+ // Receive sub field from myself (subField)
+ const labelList& map = constructMap[Pstream::myProcNo()];
+
+ field.setSize(constructSize);
+ field = nullValue;
+
+ flipAndCombine(map, constructHasFlip, subField, cop, negOp, field);
+
+ // Receive sub field from neighbour
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ IPstream fromNbr(Pstream::commsTypes::blocking, domain, 0, tag);
+ List subField(fromNbr);
+
+ checkReceivedSize(domain, map.size(), subField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ cop,
+ negOp,
+ field
+ );
+ }
+ }
+ }
+ else if (commsType == Pstream::commsTypes::scheduled)
+ {
+ // Need to make sure I don't overwrite field with received data
+ // since the data might need to be sent to another processor. So
+ // allocate a new field for the results.
+ List newField(constructSize, nullValue);
+
+ {
+ const labelList& mySubMap = subMap[Pstream::myProcNo()];
+
+ // Subset myself
+ List subField(mySubMap.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ mySubMap[i],
+ subHasFlip,
+ negOp
+ );
+ }
+
+ // Receive sub field from myself (subField)
+ const labelList& map = constructMap[Pstream::myProcNo()];
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ cop,
+ negOp,
+ newField
+ );
+ }
+
+
+ // Schedule will already have pruned 0-sized comms
+ forAll(schedule, i)
+ {
+ const labelPair& twoProcs = schedule[i];
+ // twoProcs is a swap pair of processors. The first one is the
+ // one that needs to send first and then receive.
+
+ label sendProc = twoProcs[0];
+ label recvProc = twoProcs[1];
+
+ if (Pstream::myProcNo() == sendProc)
+ {
+ // I am send first, receive next
+ {
+ OPstream toNbr
+ (
+ Pstream::commsTypes::scheduled,
+ recvProc,
+ 0,
+ tag
+ );
+
+ const labelList& map = subMap[recvProc];
+
+ List subField(map.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ toNbr << subField;
+ }
+ {
+ IPstream fromNbr
+ (
+ Pstream::commsTypes::scheduled,
+ recvProc,
+ 0,
+ tag
+ );
+ List subField(fromNbr);
+ const labelList& map = constructMap[recvProc];
+
+ checkReceivedSize(recvProc, map.size(), subField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ cop,
+ negOp,
+ newField
+ );
+ }
+ }
+ else
+ {
+ // I am receive first, send next
+ {
+ IPstream fromNbr
+ (
+ Pstream::commsTypes::scheduled,
+ sendProc,
+ 0,
+ tag
+ );
+ List subField(fromNbr);
+ const labelList& map = constructMap[sendProc];
+
+ checkReceivedSize(sendProc, map.size(), subField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ cop,
+ negOp,
+ newField
+ );
+ }
+ {
+ OPstream toNbr
+ (
+ Pstream::commsTypes::scheduled,
+ sendProc,
+ 0,
+ tag
+ );
+
+ const labelList& map = subMap[sendProc];
+
+ List subField(map.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ toNbr << subField;
+ }
+ }
+ }
+ field.transfer(newField);
+ }
+ else if (commsType == Pstream::commsTypes::nonBlocking)
+ {
+ label nOutstanding = Pstream::nRequests();
+
+ if (!contiguous())
+ {
+ PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking, tag);
+
+ // Stream data into buffer
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ // Put data into send buffer
+ UOPstream toDomain(domain, pBufs);
+
+ List subField(map.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ toDomain << subField;
+ }
+ }
+
+ // Start receiving. Do not block.
+ pBufs.finishedSends(false);
+
+ {
+ // Set up 'send' to myself
+ const labelList& myMap = subMap[Pstream::myProcNo()];
+
+ List mySubField(myMap.size());
+ forAll(myMap, i)
+ {
+ mySubField[i] = accessAndFlip
+ (
+ field,
+ myMap[i],
+ subHasFlip,
+ negOp
+ );
+ }
+
+ // Combine bits. Note that can reuse field storage
+ field.setSize(constructSize);
+ field = nullValue;
+ // Receive sub field from myself
+ {
+ const labelList& map = constructMap[Pstream::myProcNo()];
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ mySubField,
+ cop,
+ negOp,
+ field
+ );
+ }
+ }
+
+ // Block ourselves, waiting only for the current comms
+ Pstream::waitRequests(nOutstanding);
+
+ // Consume
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ UIPstream str(domain, pBufs);
+ List recvField(str);
+
+ checkReceivedSize(domain, map.size(), recvField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ recvField,
+ cop,
+ negOp,
+ field
+ );
+ }
+ }
+ }
+ else
+ {
+ // Set up sends to neighbours
+
+ List> sendFields(Pstream::nProcs());
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ List& subField = sendFields[domain];
+ subField.setSize(map.size());
+ forAll(map, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+
+ OPstream::write
+ (
+ Pstream::commsTypes::nonBlocking,
+ domain,
+ reinterpret_cast(subField.begin()),
+ subField.size()*sizeof(T),
+ tag
+ );
+ }
+ }
+
+ // Set up receives from neighbours
+
+ List> recvFields(Pstream::nProcs());
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ recvFields[domain].setSize(map.size());
+ UIPstream::read
+ (
+ Pstream::commsTypes::nonBlocking,
+ domain,
+ reinterpret_cast(recvFields[domain].begin()),
+ recvFields[domain].size()*sizeof(T),
+ tag
+ );
+ }
+ }
+
+ // Set up 'send' to myself
+
+ {
+ const labelList& map = subMap[Pstream::myProcNo()];
+
+ List& subField = sendFields[Pstream::myProcNo()];
+ subField.setSize(map.size());
+ forAll(map, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip,
+ negOp
+ );
+ }
+ }
+
+
+ // Combine bits. Note that can reuse field storage
+
+ field.setSize(constructSize);
+ field = nullValue;
+
+ // Receive sub field from myself (subField)
+ {
+ const labelList& map = constructMap[Pstream::myProcNo()];
+ const List& subField = sendFields[Pstream::myProcNo()];
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ cop,
+ negOp,
+ field
+ );
+ }
+
+
+ // Wait for all to finish
+
+ Pstream::waitRequests(nOutstanding);
+
+
+ // Collect neighbour fields
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap[domain];
+
+ if (domain != Pstream::myProcNo() && map.size())
+ {
+ const List& subField = recvFields[domain];
+
+ checkReceivedSize(domain, map.size(), subField.size());
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip,
+ subField,
+ cop,
+ negOp,
+ field
+ );
+ }
+ }
+ }
+ }
+ else
+ {
+ FatalErrorInFunction
+ << "Unknown communication schedule " << int(commsType)
+ << abort(FatalError);
+ }
+}
+
+
+template
+void Foam::distributionMapBase::send
+(
+ PstreamBuffers& pBufs,
+ const List& field
+)
+const
+{
+ // Stream data into buffer
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = subMap_[domain];
+
+ if (map.size())
+ {
+ // Put data into send buffer
+ UOPstream toDomain(domain, pBufs);
+
+ List subField(map.size());
+ forAll(subField, i)
+ {
+ subField[i] = accessAndFlip
+ (
+ field,
+ map[i],
+ subHasFlip_,
+ flipOp()
+ );
+ }
+ toDomain << subField;
+ }
+ }
+
+ // Start sending and receiving but do not block.
+ pBufs.finishedSends(false);
+}
+
+
+template
+void Foam::distributionMapBase::receive(PstreamBuffers& pBufs, List& field)
+const
+{
+ // Consume
+ field.setSize(constructSize_);
+
+ for (label domain = 0; domain < Pstream::nProcs(); domain++)
+ {
+ const labelList& map = constructMap_[domain];
+
+ if (map.size())
+ {
+ UIPstream str(domain, pBufs);
+ List recvField(str);
+
+ if (recvField.size() != map.size())
+ {
+ FatalErrorInFunction
+ << "Expected from processor " << domain
+ << " " << map.size() << " but received "
+ << recvField.size() << " elements."
+ << abort(FatalError);
+ }
+
+ flipAndCombine
+ (
+ map,
+ constructHasFlip_,
+ recvField,
+ eqOp(),
+ flipOp(),
+ field
+ );
+ }
+ }
+}
+
+
+//- Distribute data using default commsType.
+template
+void Foam::distributionMapBase::distribute
+(
+ List& fld,
+ const negateOp& negOp,
+ const int tag
+) const
+{
+ if (Pstream::defaultCommsType == Pstream::commsTypes::nonBlocking)
+ {
+ distribute
+ (
+ Pstream::commsTypes::nonBlocking,
+ List(),
+ constructSize_,
+ subMap_,
+ subHasFlip_,
+ constructMap_,
+ constructHasFlip_,
+ fld,
+ negOp,
+ tag
+ );
+ }
+ else if (Pstream::defaultCommsType == Pstream::commsTypes::scheduled)
+ {
+ distribute
+ (
+ Pstream::commsTypes::scheduled,
+ schedule(),
+ constructSize_,
+ subMap_,
+ subHasFlip_,
+ constructMap_,
+ constructHasFlip_,
+ fld,
+ negOp,
+ tag
+ );
+ }
+ else
+ {
+ distribute
+ (
+ Pstream::commsTypes::blocking,
+ List(),
+ constructSize_,
+ subMap_,
+ subHasFlip_,
+ constructMap_,
+ constructHasFlip_,
+ fld,
+ negOp,
+ tag
+ );
+ }
+}
+
+
+//- Distribute data using default commsType.
+template
+void Foam::distributionMapBase::distribute
+(
+ List& fld,
+ const int tag
+) const
+{
+ distribute(fld, flipOp(), tag);
+}
+
+
+//- Distribute data using default commsType.
+template
+void Foam::distributionMapBase::distribute
+(
+ DynamicList& fld,
+ const int tag
+) const
+{
+ fld.shrink();
+
+ List& fldList = static_cast& >(fld);
+
+ distribute(fldList, tag);
+
+ fld.setCapacity(fldList.size());
+}
+
+
+//- Reverse distribute data using default commsType.
+template
+void Foam::distributionMapBase::reverseDistribute
+(
+ const label constructSize,
+ List& fld,
+ const int tag
+) const
+{
+ if (Pstream::defaultCommsType == Pstream::commsTypes::nonBlocking)
+ {
+ distribute
+ (
+ Pstream::commsTypes::nonBlocking,
+ List(),
+ constructSize,
+ constructMap_,
+ constructHasFlip_,
+ subMap_,
+ subHasFlip_,
+ fld,
+ flipOp(),
+ tag
+ );
+ }
+ else if (Pstream::defaultCommsType == Pstream::commsTypes::scheduled)
+ {
+ distribute
+ (
+ Pstream::commsTypes::scheduled,
+ schedule(),
+ constructSize,
+ constructMap_,
+ constructHasFlip_,
+ subMap_,
+ subHasFlip_,
+ fld,
+ flipOp(),
+ tag
+ );
+ }
+ else
+ {
+ distribute
+ (
+ Pstream::commsTypes::blocking,
+ List(),
+ constructSize,
+ constructMap_,
+ constructHasFlip_,
+ subMap_,
+ subHasFlip_,
+ fld,
+ flipOp(),
+ tag
+ );
+ }
+}
+
+
+//- Reverse distribute data using default commsType.
+// Since constructSize might be larger than supplied size supply
+// a nullValue
+template
+void Foam::distributionMapBase::reverseDistribute
+(
+ const label constructSize,
+ const T& nullValue,
+ List& fld,
+ const int tag
+) const
+{
+ if (Pstream::defaultCommsType == Pstream::commsTypes::nonBlocking)
+ {
+ distribute
+ (
+ Pstream::commsTypes::nonBlocking,
+ List(),
+ constructSize,
+ constructMap_,
+ constructHasFlip_,
+ subMap_,
+ subHasFlip_,
+ fld,
+ eqOp(),
+ flipOp(),
+ nullValue,
+ tag
+ );
+ }
+ else if (Pstream::defaultCommsType == Pstream::commsTypes::scheduled)
+ {
+ distribute
+ (
+ Pstream::commsTypes::scheduled,
+ schedule(),
+ constructSize,
+ constructMap_,
+ constructHasFlip_,
+ subMap_,
+ subHasFlip_,
+ fld,
+ eqOp(),
+ flipOp(),
+ nullValue,
+ tag
+ );
+ }
+ else
+ {
+ distribute
+ (
+ Pstream::commsTypes::blocking,
+ List(),
+ constructSize,
+ constructMap_,
+ constructHasFlip_,
+ subMap_,
+ subHasFlip_,
+ fld,
+ eqOp(),
+ flipOp(),
+ nullValue,
+ tag
+ );
+ }
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapTemplates.C b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapTemplates.C
new file mode 100644
index 0000000000..a6bbc6cde7
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyDistributionMap/distributionMapTemplates.C
@@ -0,0 +1,271 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration | Website: https://openfoam.org
+ \\ / A nd | Copyright (C) 2011-2022 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 "Pstream.H"
+#include "PstreamBuffers.H"
+#include "PstreamCombineReduceOps.H"
+#include "globalIndexAndTransform.H"
+#include "transformField.H"
+#include "flipOp.H"
+
+// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
+
+template
+void Foam::distributionMap::applyDummyTransforms(List& field) const
+{
+ forAll(transformElements_, trafoI)
+ {
+ const labelList& elems = transformElements_[trafoI];
+
+ label n = transformStart_[trafoI];
+
+ forAll(elems, i)
+ {
+ field[n++] = field[elems[i]];
+ }
+ }
+}
+
+
+template
+void Foam::distributionMap::applyDummyInverseTransforms(List& field) const
+{
+ forAll(transformElements_, trafoI)
+ {
+ const labelList& elems = transformElements_[trafoI];
+ label n = transformStart_[trafoI];
+
+ forAll(elems, i)
+ {
+ field[elems[i]] = field[n++];
+ }
+ }
+}
+
+
+template //, class CombineOp>
+void Foam::distributionMap::applyTransforms
+(
+ const globalIndexAndTransform& globalTransforms,
+ List& field,
+ const TransformOp& top
+) const
+{
+ const List& totalTransform =
+ globalTransforms.transformPermutations();
+
+ forAll(totalTransform, trafoI)
+ {
+ const transformer& vt = totalTransform[trafoI];
+ const labelList& elems = transformElements_[trafoI];
+ label n = transformStart_[trafoI];
+
+ // Could be optimised to avoid memory allocations
+ List transformFld(UIndirectList(field, elems));
+ top(vt, true, transformFld);
+
+ forAll(transformFld, i)
+ {
+ // cop(field[n++], transformFld[i]);
+ field[n++] = transformFld[i];
+ }
+ }
+}
+
+
+template //, class CombineOp>
+void Foam::distributionMap::applyInverseTransforms
+(
+ const globalIndexAndTransform& globalTransforms,
+ List& field,
+ const TransformOp& top
+) const
+{
+ const List& totalTransform =
+ globalTransforms.transformPermutations();
+
+ forAll(totalTransform, trafoI)
+ {
+ const transformer& vt = totalTransform[trafoI];
+ const labelList& elems = transformElements_[trafoI];
+ label n = transformStart_[trafoI];
+
+ // Could be optimised to avoid memory allocations
+ List