From 84db37f62f12f876860e2407bb9c586f4448fb6a Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Fri, 9 Sep 2022 16:40:00 +0200
Subject: [PATCH] ENH: improved bookkeeping for finite-area to volume mesh
 correspondence

- whichPolyPatches() = the polyPatches related to the areaMesh.

  This helps when pre-calculating (and caching) any patch-specific
  content.

- whichPatchFaces() = the poly-patch/patch-face for each of the faceLabels.

  This allows more convenient lookups and, since the list is cached on
  the area mesh, reduces the number of calls to whichPatch() etc.

- whichFace() = the area-face corresponding to the given mesh-face

ENH: more flexible/consistent volume->area mapper functions
---
 .../finiteArea/checkFaMesh/printMeshSummary.H |  12 +-
 .../finiteArea/makeFaMesh/printMeshSummary.H  |  15 +-
 .../faMesh/faBoundaryMesh/faBoundaryMesh.C    |  18 +-
 .../faMesh/faBoundaryMesh/faBoundaryMesh.H    |   3 +-
 src/finiteArea/faMesh/faMesh.C                |  24 +
 src/finiteArea/faMesh/faMesh.H                |  24 +
 .../faMesh/faMeshDemandDrivenData.C           |  40 ++
 src/finiteArea/faMesh/faMeshI.H               |  26 +
 .../volSurfaceMapping/volSurfaceMapping.C     | 489 +++++++++++++++---
 .../volSurfaceMapping/volSurfaceMapping.H     | 261 +++++++++-
 10 files changed, 798 insertions(+), 114 deletions(-)

diff --git a/applications/utilities/finiteArea/checkFaMesh/printMeshSummary.H b/applications/utilities/finiteArea/checkFaMesh/printMeshSummary.H
index e3d255d9613..a0ebeb01c51 100644
--- a/applications/utilities/finiteArea/checkFaMesh/printMeshSummary.H
+++ b/applications/utilities/finiteArea/checkFaMesh/printMeshSummary.H
@@ -116,12 +116,16 @@ Description
     {
         const faPatch& p = patches[patchi];
 
-         Info<< "  " << "patch " << p.index()
-             << " (size: " << returnReduce(p.size(), sumOp<label>())
-             << ") name: " << p.name()
-             << nl;
+        // Report physical size (nEdges) not virtual size
+        Info<< "  " << "patch " << p.index()
+            << " (size: " << returnReduce(p.nEdges(), sumOp<label>())
+            << ") name: " << p.name()
+            << nl;
     }
 
+    Info<< "----------------" << nl
+        << "Used polyPatches: " << flatOutput(aMesh.whichPolyPatches()) << nl;
+
 
     // Geometry information
     Info<< nl;
diff --git a/applications/utilities/finiteArea/makeFaMesh/printMeshSummary.H b/applications/utilities/finiteArea/makeFaMesh/printMeshSummary.H
index 5dd75c8804d..084759849fe 100644
--- a/applications/utilities/finiteArea/makeFaMesh/printMeshSummary.H
+++ b/applications/utilities/finiteArea/makeFaMesh/printMeshSummary.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2021 OpenCFD Ltd.
+    Copyright (C) 2021-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
@@ -36,12 +36,17 @@ Description
     {
         const faPatch& p = patches[patchi];
 
-         Info<< "  " << "patch " << p.index()
-             << " (size: " << returnReduce(p.size(), sumOp<label>())
-             << ") name: " << p.name()
-             << nl;
+        // Report physical size (nEdges) not virtual size
+        Info<< "  " << "patch " << p.index()
+            << " (size: " << returnReduce(p.nEdges(), sumOp<label>())
+            << ") name: " << p.name()
+            << nl;
     }
 
+    Info<< "----------------" << nl
+        << "Used polyPatches: " << flatOutput(aMesh.whichPolyPatches()) << nl;
+
+
     // Geometry information
     Info<< nl;
     {
diff --git a/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.C b/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.C
index 1098eb95fa2..df80a98e6de 100644
--- a/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.C
+++ b/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.C
@@ -609,32 +609,30 @@ Foam::label Foam::faBoundaryMesh::findPatchID
 
 Foam::label Foam::faBoundaryMesh::whichPatch(const label edgeIndex) const
 {
-    // Find out which patch the current face belongs to by comparing label
-    // with patch start labels.
-    // If the face is internal, return -1;
-    // if it is off the end of the list, abort
     if (edgeIndex < mesh().nInternalEdges())
     {
+        // Internal edge
         return -1;
     }
     else if (edgeIndex >= mesh().nEdges())
     {
+        // Bounds error: abort
         FatalErrorInFunction
             << "Edge " << edgeIndex
             << " out of bounds. Number of geometric edges " << mesh().nEdges()
             << abort(FatalError);
+
+        return -1;
     }
 
+    // Find patch that the edgeIndex belongs to.
+
     forAll(*this, patchi)
     {
         label start = mesh_.patchStarts()[patchi];
         label size = operator[](patchi).faPatch::size();
 
-        if
-        (
-            edgeIndex >= start
-         && edgeIndex < start + size
-        )
+        if (edgeIndex >= start && edgeIndex < start + size)
         {
             return patchi;
         }
@@ -642,7 +640,7 @@ Foam::label Foam::faBoundaryMesh::whichPatch(const label edgeIndex) const
 
     // If not in any of above, it's trouble!
     FatalErrorInFunction
-        << "error in patch search algorithm"
+        << "Error in patch search algorithm"
         << abort(FatalError);
 
     return -1;
diff --git a/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.H b/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.H
index 989e1b9e04f..e3945375eac 100644
--- a/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.H
+++ b/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.H
@@ -42,11 +42,12 @@ Author
 #ifndef Foam_faBoundaryMesh_H
 #define Foam_faBoundaryMesh_H
 
+#include "regIOobject.H"
 #include "faPatch.H"
+#include "labelPair.H"
 #include "lduInterfacePtrsList.H"
 #include "wordList.H"
 #include "pointField.H"
-#include "regIOobject.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
diff --git a/src/finiteArea/faMesh/faMesh.C b/src/finiteArea/faMesh/faMesh.C
index 34b43b2380b..67f18252ff2 100644
--- a/src/finiteArea/faMesh/faMesh.C
+++ b/src/finiteArea/faMesh/faMesh.C
@@ -221,6 +221,8 @@ void Foam::faMesh::clearGeomNotAreas() const
 
     clearHalo();
     patchPtr_.reset(nullptr);
+    polyPatchFacesPtr_.reset(nullptr);
+    polyPatchIdsPtr_.reset(nullptr);
     bndConnectPtr_.reset(nullptr);
     deleteDemandDrivenData(SPtr_);
     deleteDemandDrivenData(patchStartsPtr_);
@@ -360,6 +362,8 @@ Foam::faMesh::faMesh
     curTimeIndex_(time().timeIndex()),
 
     patchPtr_(nullptr),
+    polyPatchFacesPtr_(nullptr),
+    polyPatchIdsPtr_(nullptr),
     bndConnectPtr_(nullptr),
     lduPtr_(nullptr),
 
@@ -464,6 +468,8 @@ Foam::faMesh::faMesh
     curTimeIndex_(time().timeIndex()),
 
     patchPtr_(nullptr),
+    polyPatchFacesPtr_(nullptr),
+    polyPatchIdsPtr_(nullptr),
     bndConnectPtr_(nullptr),
     lduPtr_(nullptr),
 
@@ -543,6 +549,8 @@ Foam::faMesh::faMesh
     curTimeIndex_(time().timeIndex()),
 
     patchPtr_(nullptr),
+    polyPatchFacesPtr_(nullptr),
+    polyPatchIdsPtr_(nullptr),
     bndConnectPtr_(nullptr),
     lduPtr_(nullptr),
 
@@ -706,6 +714,22 @@ const Foam::word& Foam::faMesh::regionName() const
 }
 
 
+Foam::labelList Foam::faMesh::faceCells() const
+{
+    const labelList& faceOwner = this->mesh().faceOwner();
+
+    labelList list(faceLabels_);
+
+    for (label& val : list)
+    {
+        // Transcribe from faceId to cellId (owner)
+        val = faceOwner[val];
+    }
+
+    return list;
+}
+
+
 void Foam::faMesh::removeFiles(const fileName& instanceDir) const
 {
     fileName meshFilesPath = thisDb().time().path()/instanceDir/meshDir();
diff --git a/src/finiteArea/faMesh/faMesh.H b/src/finiteArea/faMesh/faMesh.H
index 62734dfe186..7a4a17c8e3d 100644
--- a/src/finiteArea/faMesh/faMesh.H
+++ b/src/finiteArea/faMesh/faMesh.H
@@ -253,6 +253,12 @@ class faMesh
         //- Primitive patch
         mutable std::unique_ptr<uindirectPrimitivePatch> patchPtr_;
 
+        //- List of poly patch/patch-face for faceLabels
+        mutable std::unique_ptr<List<labelPair>> polyPatchFacesPtr_;
+
+        //- List of polyPatch ids used by the area mesh
+        mutable std::unique_ptr<labelList> polyPatchIdsPtr_;
+
         //- List of proc/mesh-face for boundary edge neighbours
         mutable std::unique_ptr<List<labelPair>> bndConnectPtr_;
 
@@ -368,6 +374,10 @@ class faMesh
             //- Calculate patch starts in the edge list
             void calcPatchStarts() const;
 
+            //- Calculate which polyPatches, polyPatch/local-face
+            //- are related to the areaMesh
+            void calcWhichPatchFaces() const;
+
             //- Calculate edge lengths
             //  Triggers communication via calcEdgeAreaNormals
             void calcLe() const;
@@ -702,6 +712,14 @@ public:
             //- Return the underlying polyMesh face labels
             inline const labelList& faceLabels() const noexcept;
 
+            //- The area-face corresponding to the mesh-face, -1 if not found
+            inline label whichFace(const label meshFacei) const;
+
+            //- The polyPatches related to the areaMesh, in sorted order
+            inline const labelList& whichPolyPatches() const;
+
+            //- The polyPatch/local-face for each faceLabels()
+            inline const List<labelPair>& whichPatchFaces() const;
 
             //- Return parallel info
             const faGlobalMeshData& globalData() const;
@@ -762,6 +780,12 @@ public:
             tmp<vectorField> haloFaceNormals(const label patchi) const;
 
 
+    // Interfacing
+
+        //- The volume (owner) cells associated with the area-mesh
+        labelList faceCells() const;
+
+
     // Storage management
 
         //- Remove all files from mesh instance
diff --git a/src/finiteArea/faMesh/faMeshDemandDrivenData.C b/src/finiteArea/faMesh/faMeshDemandDrivenData.C
index c5df1546b9c..7fd15f4db4d 100644
--- a/src/finiteArea/faMesh/faMeshDemandDrivenData.C
+++ b/src/finiteArea/faMesh/faMeshDemandDrivenData.C
@@ -277,6 +277,46 @@ void Foam::faMesh::calcPatchStarts() const
 }
 
 
+void Foam::faMesh::calcWhichPatchFaces() const
+{
+    // Usually need both together
+    if (polyPatchFacesPtr_ || polyPatchIdsPtr_)
+    {
+        FatalErrorInFunction
+            << "Already allocated polyPatchFaces/polyPatchIds"
+            << abort(FatalError);
+    }
+
+    const polyBoundaryMesh& pbm = mesh().boundaryMesh();
+
+    polyPatchFacesPtr_.reset
+    (
+        new List<labelPair>(pbm.whichPatchFace(faceLabels_))
+    );
+
+    labelHashSet ids;
+
+    // Extract patch ids from (patch, facei) tuples
+    for (const labelPair& tup : *polyPatchFacesPtr_)
+    {
+        ids.insert(tup.first());
+    }
+
+    ids.erase(-1);  // Without internal faces (patchi == -1)
+
+    // parSync
+    Foam::reduce
+    (
+        ids,
+        bitOrOp<labelHashSet>(),
+        UPstream::msgType(),
+        this->comm()
+    );
+
+    polyPatchIdsPtr_.reset(new labelList(ids.sortedToc()));
+}
+
+
 void Foam::faMesh::calcLe() const
 {
     DebugInFunction
diff --git a/src/finiteArea/faMesh/faMeshI.H b/src/finiteArea/faMesh/faMeshI.H
index dfdbc00f796..3d3db7b958e 100644
--- a/src/finiteArea/faMesh/faMeshI.H
+++ b/src/finiteArea/faMesh/faMeshI.H
@@ -145,6 +145,32 @@ inline Foam::uindirectPrimitivePatch& Foam::faMesh::patch()
 }
 
 
+inline Foam::label Foam::faMesh::whichFace(const label meshFacei) const
+{
+    return faceLabels_.find(meshFacei);
+}
+
+
+inline const Foam::labelList& Foam::faMesh::whichPolyPatches() const
+{
+    if (!polyPatchFacesPtr_ || !polyPatchIdsPtr_)
+    {
+        calcWhichPatchFaces();
+    }
+    return *polyPatchIdsPtr_;
+}
+
+
+inline const Foam::List<Foam::labelPair>& Foam::faMesh::whichPatchFaces() const
+{
+    if (!polyPatchFacesPtr_ || !polyPatchIdsPtr_)
+    {
+        calcWhichPatchFaces();
+    }
+    return *polyPatchFacesPtr_;
+}
+
+
 inline bool Foam::faMesh::hasInternalEdgeLabels() const noexcept
 {
     return false;
diff --git a/src/finiteArea/interpolation/volSurfaceMapping/volSurfaceMapping.C b/src/finiteArea/interpolation/volSurfaceMapping/volSurfaceMapping.C
index ee03325dc61..03bbb802f69 100644
--- a/src/finiteArea/interpolation/volSurfaceMapping/volSurfaceMapping.C
+++ b/src/finiteArea/interpolation/volSurfaceMapping/volSurfaceMapping.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2016-2017 Wikki Ltd
-    Copyright (C) 2020-2021 OpenCFD Ltd.
+    Copyright (C) 2020-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -24,157 +24,502 @@ License
     You should have received a copy of the GNU General Public License
     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
-\*----------------------------------------------------------------------------*/
+\*---------------------------------------------------------------------------*/
 
 #include "volSurfaceMapping.H"
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+template<class Type>
+void Foam::volSurfaceMapping::mapToSurface
+(
+    const GeometricBoundaryField<Type, fvPatchField, volMesh>& bfld,
+    Field<Type>& result
+) const
+{
+    // The polyPatch/local-face for each of the faceLabels
+    const List<labelPair>& patchFaces = mesh_.whichPatchFaces();
+
+    // FULLDEBUG: checkSize ?
+    // or simply result.resize(mesh_.nFaces());
+
+    forAll(patchFaces, i)
+    {
+        const labelPair& patchAndFace = patchFaces[i];
+
+        if (patchAndFace.first() >= 0)
+        {
+            result[i] = bfld[patchAndFace];
+        }
+    }
+}
+
+
 template<class Type>
 Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapToSurface
 (
-    const GeometricBoundaryField<Type, fvPatchField, volMesh>& df
+    const GeometricBoundaryField<Type, fvPatchField, volMesh>& bfld
 ) const
 {
-    // Grab labels for all faces in faMesh
-    const labelList& faceLabels = mesh_.faceLabels();
+    auto tresult = tmp<Field<Type>>::New(mesh_.nFaces(), Zero);
+    mapToSurface(bfld, tresult.ref());
+    return tresult;
+}
 
-    auto tresult = tmp<Field<Type>>::New(faceLabels.size(), Zero);
-    auto& result = tresult.ref();
 
-    // Get reference to volume mesh
-    const polyMesh& pMesh = mesh_();
-    const polyBoundaryMesh& bm = pMesh.boundaryMesh();
+template<class Type>
+void Foam::volSurfaceMapping::mapToSurface
+(
+    const GeometricField<Type, fvPatchField, volMesh>& vfld,
+    Field<Type>& result
+) const
+{
+    mapToSurface(vfld.boundaryField(), result);
+}
 
-    label patchID, faceID;
 
-    // Grab droplet cloud source by identifying patch and face
-    forAll(faceLabels, i)
+template<class Type>
+void Foam::volSurfaceMapping::mapToSurface
+(
+    const tmp<GeometricField<Type, fvPatchField, volMesh>>& tvf,
+    Field<Type>& result
+) const
+{
+    mapToSurface(tvf().boundaryField(), result);
+    tvf.clear();
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapToSurface
+(
+    const GeometricField<Type, fvPatchField, volMesh>& vfld
+) const
+{
+    return mapToSurface(vfld.boundaryField());
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapToSurface
+(
+    const tmp<GeometricField<Type, fvPatchField, volMesh>>& tvf
+) const
+{
+    tmp<Field<Type>> tresult(mapToSurface(tvf().boundaryField()));
+    tvf.clear();
+    return tresult;
+}
+
+
+template<class Type>
+void Foam::volSurfaceMapping::mapToSurface
+(
+    const GeometricField<Type, fvsPatchField, surfaceMesh>& fld,
+    Field<Type>& result
+) const
+{
+    const auto& bfld = fld.boundaryField();
+
+    // The polyPatch/local-face for each of the faceLabels
+    const List<labelPair>& patchFaces = mesh_.whichPatchFaces();
+
+    // FULLDEBUG: checkSize ?
+    // or simply result.resize(mesh_.nFaces());
+
+    forAll(patchFaces, i)
     {
-        // Escape if face is beyond active faces, eg belongs to a face zone
-        if (faceLabels[i] < pMesh.nFaces())
-        {
-            patchID = bm.whichPatch(faceLabels[i]);
-            faceID = bm[patchID].whichFace(faceLabels[i]);
+        const labelPair& patchAndFace = patchFaces[i];
 
-            result[i] = df[patchID][faceID];
+        if (patchAndFace.first() >= 0)
+        {
+            // Value from boundary
+            result[i] = bfld[patchAndFace];
+        }
+        else if (patchAndFace.second() >= 0)
+        {
+            // Value from internal
+            result[i] = fld[patchAndFace.second()];
         }
     }
+}
 
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapToSurface
+(
+    const GeometricField<Type, fvsPatchField, surfaceMesh>& fld
+) const
+{
+    auto tresult = tmp<Field<Type>>::New(mesh_.nFaces(), Zero);
+    mapToSurface(fld, tresult.ref());
     return tresult;
 }
 
 
+template<class Type>
+void Foam::volSurfaceMapping::mapToSurface
+(
+    const tmp<GeometricField<Type, fvsPatchField, surfaceMesh>>& tsf,
+    Field<Type>& result
+) const
+{
+    mapToSurface(tsf(), result);
+    tsf.clear();
+}
+
+
 template<class Type>
 Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapToSurface
 (
-    const Field<Type>& f
+    const tmp<GeometricField<Type, fvsPatchField, surfaceMesh>>& tsf
 ) const
 {
-    const labelList& faceLabels = mesh_.faceLabels();
+    tmp<Field<Type>> tresult(mapToSurface(tsf()));
+    tsf.clear();
+    return tresult;
+}
 
-    auto tresult = tmp<Field<Type>>::New(faceLabels.size(), Zero);
-    auto& result = tresult.ref();
 
-    const polyMesh& pMesh = mesh_();
-    const polyBoundaryMesh& bm = pMesh.boundaryMesh();
-    label patchID, faceID;
+template<class Type>
+void Foam::volSurfaceMapping::mapToSurface
+(
+    const UPtrList<Field<Type>>& patchFields,
+    Field<Type>& result
+) const
+{
+    // The polyPatch/local-face for each of the faceLabels
+    const List<labelPair>& patchFaces = mesh_.whichPatchFaces();
 
-    forAll(faceLabels, i)
+    // FULLDEBUG: checkSize ?
+    // or simply result.resize(mesh_.nFaces());
+
+    forAll(patchFaces, i)
     {
-        if (faceLabels[i] < pMesh.nFaces())
+        const label patchi = patchFaces[i].first();
+        const label facei = patchFaces[i].second();
+
+        const auto* pfld = patchFields.get(patchi);
+
+        if (pfld)
         {
-            patchID = bm.whichPatch(faceLabels[i]);
-            faceID = bm[patchID].whichFace(faceLabels[i]);
+            result[i] = (*pfld)[facei];
+        }
+    }
+}
 
-            result[i] = f[faceID];
+
+template<class Type>
+void Foam::volSurfaceMapping::mapToSurface
+(
+    const PtrMap<Field<Type>>& patchFields,
+    Field<Type>& result
+) const
+{
+    // The polyPatch/local-face for each of the faceLabels
+    const List<labelPair>& patchFaces = mesh_.whichPatchFaces();
+
+    // FULLDEBUG: checkSize ?
+    // or simply result.resize(mesh_.nFaces());
+
+    forAll(patchFaces, i)
+    {
+        const label patchi = patchFaces[i].first();
+        const label facei = patchFaces[i].second();
+
+        const auto* pfld = patchFields.get(patchi);
+
+        if (pfld)
+        {
+            result[i] = (*pfld)[facei];
         }
     }
+}
+
 
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapToSurface
+(
+    const UPtrList<Field<Type>>& patchFields
+) const
+{
+    auto tresult = tmp<Field<Type>>::New(mesh_.nFaces(), Zero);
+    mapToSurface(patchFields, tresult.ref());
     return tresult;
 }
 
 
 template<class Type>
-Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapInternalToSurface
+Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapToSurface
 (
-    const GeometricBoundaryField<Type, fvPatchField, volMesh>& df
+    const PtrMap<Field<Type>>& patchFields
 ) const
 {
-    // Grab labels for all faces in faMesh
-    const labelList& faceLabels = mesh_.faceLabels();
+    auto tresult = tmp<Field<Type>>::New(mesh_.nFaces(), Zero);
+    mapToSurface(patchFields, tresult.ref());
+    return tresult;
+}
 
-    auto tresult = tmp<Field<Type>>::New(faceLabels.size(), Zero);
-    auto& result = tresult.ref();
 
-    // Get reference to volume mesh
-    const polyMesh& pMesh = mesh_();
-    const polyBoundaryMesh& bm = pMesh.boundaryMesh();
+template<class Type>
+void Foam::volSurfaceMapping::mapInternalToSurface
+(
+    const GeometricBoundaryField<Type, fvPatchField, volMesh>& bfld,
+    Field<Type>& result
+) const
+{
+    PtrList<Field<Type>> patchFields;
 
-    label patchID, faceID;
+    // All referenced polyPatches (sorted order)
+    const labelList& patches = mesh_.whichPolyPatches();
 
-    // Grab droplet cloud source by identifying patch and face
-    forAll(faceLabels, i)
+    if (!patches.empty())
     {
-        // Escape if face is beyond active faces, eg belongs to a face zone
-        if (faceLabels[i] < pMesh.nFaces())
-        {
-            patchID = bm.whichPatch(faceLabels[i]);
-            faceID = bm[patchID].whichFace(faceLabels[i]);
+        // maxPolyPatch+1
+        patchFields.resize(patches.last()+1);
+    }
 
-            result[i] = df[patchID].patchInternalField()()[faceID];
-        }
+    // Populate patchInternalField
+    for (const label patchi : patches)
+    {
+        patchFields.set(patchi, bfld[patchi].patchInternalField());
     }
 
+    mapToSurface(patchFields, result);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapInternalToSurface
+(
+    const GeometricBoundaryField<Type, fvPatchField, volMesh>& bfld
+) const
+{
+    auto tresult = tmp<Field<Type>>::New(mesh_.nFaces(), Zero);
+
+    mapInternalToSurface(bfld, tresult.ref());
+
     return tresult;
 }
 
 
 template<class Type>
-void Foam::volSurfaceMapping::mapToVolume
+void Foam::volSurfaceMapping::mapInternalToSurface
 (
-    const GeometricField<Type, faPatchField, areaMesh>& af,
-    GeometricBoundaryField<Type, fvPatchField, volMesh>& bf
+    const GeometricField<Type, fvPatchField, volMesh>& vf,
+    Field<Type>& result
 ) const
 {
-    // Grab labels for all faces in faMesh
-    const labelList& faceLabels = mesh_.faceLabels();
+    mapInternalToSurface(vf.boundaryField(), result);
+}
 
-    // Get reference to volume mesh
-    const polyMesh& pMesh = mesh_();
-    const polyBoundaryMesh& bm = pMesh.boundaryMesh();
 
-    label patchID, faceID;
+template<class Type>
+void Foam::volSurfaceMapping::mapInternalToSurface
+(
+    const tmp<GeometricField<Type, fvPatchField, volMesh>>& tvf,
+    Field<Type>& result
+) const
+{
+    mapInternalToSurface(tvf().boundaryField(), result);
+    tvf.clear();
+}
 
-    const Field<Type>& afi = af.internalField();
 
-    forAll(faceLabels, i)
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapInternalToSurface
+(
+    const GeometricField<Type, fvPatchField, volMesh>& vfld
+) const
+{
+    return mapInternalToSurface(vfld.boundaryField());
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapInternalToSurface
+(
+    const tmp<GeometricField<Type, fvPatchField, volMesh>>& tvf
+) const
+{
+    tmp<Field<Type>> tresult(mapInternalToSurface(tvf().boundaryField()));
+    tvf.clear();
+    return tresult;
+}
+
+
+template<class Type>
+void Foam::volSurfaceMapping::mapToVolume
+(
+    const DimensionedField<Type, areaMesh>& af,
+    GeometricBoundaryField<Type, fvPatchField, volMesh>& dest,
+    const label destPatchi
+) const
+{
+    // The polyPatch/local-face for each of the faceLabels
+    const List<labelPair>& patchFaces = mesh_.whichPatchFaces();
+
+    forAll(patchFaces, i)
     {
-        // Escape if face is beyond active faces, eg belongs to a face zone
-        if (faceLabels[i] < pMesh.nFaces())
-        {
-            patchID = bm.whichPatch(faceLabels[i]);
-            faceID = bm[patchID].whichFace(faceLabels[i]);
+        const labelPair& patchAndFace = patchFaces[i];
 
-            bf[patchID][faceID] = afi[i];
+        if
+        (
+            patchAndFace.first() >= 0
+         && (patchAndFace.first() == destPatchi || destPatchi < 0)
+        )
+        {
+            dest[patchAndFace] = af[i];
         }
     }
 }
 
 
+template<class Type>
+void Foam::volSurfaceMapping::mapToVolume
+(
+    const tmp<DimensionedField<Type, areaMesh>>& taf,
+    GeometricBoundaryField<Type, fvPatchField, volMesh>& dest,
+    const label destPatchi
+) const
+{
+    mapToVolume(taf(), dest, destPatchi);
+    taf.clear();
+}
+
+
+template<class Type>
+void Foam::volSurfaceMapping::mapToVolume
+(
+    const DimensionedField<Type, areaMesh>& af,
+    GeometricField<Type, fvPatchField, volMesh>& dest,
+    const label destPatchi
+) const
+{
+    mapToVolume(af, dest.boundaryFieldRef(), destPatchi);
+}
+
+
+template<class Type>
+void Foam::volSurfaceMapping::mapToVolume
+(
+    const tmp<DimensionedField<Type, areaMesh>>& taf,
+    GeometricField<Type, fvPatchField, volMesh>& dest,
+    const label destPatchi
+) const
+{
+    mapToVolume(taf, dest.boundaryFieldRef(), destPatchi);
+}
+
+
+template<class Type>
+void Foam::volSurfaceMapping::mapToVolume
+(
+    const tmp<GeometricField<Type, faPatchField, areaMesh>>& taf,
+    GeometricBoundaryField<Type, fvPatchField, volMesh>& dest,
+    const label destPatchi
+) const
+{
+    mapToVolume(taf().internalField(), dest, destPatchi);
+    taf.clear();
+}
+
+
 template<class Type>
 void Foam::volSurfaceMapping::mapToVolume
 (
     const tmp<GeometricField<Type, faPatchField, areaMesh>>& taf,
-    GeometricBoundaryField<Type, fvPatchField, volMesh>& bf
+    GeometricField<Type, fvPatchField, volMesh>& dest,
+    const label destPatchi
+) const
+{
+    mapToVolume(taf().internalField(), dest.boundaryFieldRef(), destPatchi);
+    taf.clear();
+}
+
+
+template<class Type>
+void Foam::volSurfaceMapping::mapToVolumePatch
+(
+    const DimensionedField<Type, areaMesh>& af,
+    Field<Type>& dest,
+    const label destPatchi
 ) const
 {
-    mapToVolume(taf(), bf);
+    // The polyPatch/local-face for each of the faceLabels
+    const List<labelPair>& patchFaces = mesh_.whichPatchFaces();
+
+    forAll(patchFaces, i)
+    {
+        const label patchi = patchFaces[i].first();
+        const label facei = patchFaces[i].second();
+
+        if (patchi >= 0 && patchi == destPatchi)
+        {
+            dest[facei] = af[i];
+        }
+    }
+}
+
 
+template<class Type>
+void Foam::volSurfaceMapping::mapToVolumePatch
+(
+    const tmp<DimensionedField<Type, areaMesh>>& taf,
+    Field<Type>& dest,
+    const label destPatchi
+) const
+{
+    mapToVolumePatch(taf(), dest, destPatchi);
     taf.clear();
 }
 
 
+template<class Type>
+void Foam::volSurfaceMapping::mapToVolumePatch
+(
+    const tmp<GeometricField<Type, faPatchField, areaMesh>>& taf,
+    Field<Type>& dest,
+    const label destPatchi
+) const
+{
+    mapToVolumePatch(taf().internalField(), dest, destPatchi);
+    taf.clear();
+}
+
+
+// These are fragile/wrong. To be removed (2022-09)
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::volSurfaceMapping::mapToSurface
+(
+    const Field<Type>& f
+) const
+{
+    const labelList& faceLabels = mesh_.faceLabels();
+
+    auto tresult = tmp<Field<Type>>::New(faceLabels.size(), Zero);
+    auto& result = tresult.ref();
+
+    const polyMesh& pMesh = mesh_.mesh();
+    const polyBoundaryMesh& bm = pMesh.boundaryMesh();
+    label patchID, faceID;
+
+    forAll(faceLabels, i)
+    {
+        if (faceLabels[i] < pMesh.nFaces())
+        {
+            patchID = bm.whichPatch(faceLabels[i]);
+            faceID = bm[patchID].whichFace(faceLabels[i]);
+
+            result[i] = f[faceID];
+        }
+    }
+
+    return tresult;
+}
+
+
 template<class Type>
 void Foam::volSurfaceMapping::mapToField
 (
@@ -182,9 +527,7 @@ void Foam::volSurfaceMapping::mapToField
     Field<Type>& f
 ) const
 {
-    const Field<Type>& afi = af.internalField();
-
-    mapToField(afi, f);
+    mapToField(af.primitiveField(), f);
 }
 
 
@@ -197,7 +540,7 @@ void Foam::volSurfaceMapping::mapToField
 {
     const labelList& faceLabels = mesh_.faceLabels();
 
-    const polyMesh& pMesh = mesh_();
+    const polyMesh& pMesh = mesh_.mesh();
     const polyBoundaryMesh& bm = pMesh.boundaryMesh();
     label patchID, faceID;
 
diff --git a/src/finiteArea/interpolation/volSurfaceMapping/volSurfaceMapping.H b/src/finiteArea/interpolation/volSurfaceMapping/volSurfaceMapping.H
index 31382499986..4bcd5c81777 100644
--- a/src/finiteArea/interpolation/volSurfaceMapping/volSurfaceMapping.H
+++ b/src/finiteArea/interpolation/volSurfaceMapping/volSurfaceMapping.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2016-2017 Wikki Ltd
-    Copyright (C) 2019-2021 OpenCFD Ltd.
+    Copyright (C) 2019-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,8 +41,11 @@ SourceFiles
 #ifndef Foam_volSurfaceMapping_H
 #define Foam_volSurfaceMapping_H
 
-#include "faMesh.H"
+#include "areaFields.H"
 #include "volMesh.H"
+#include "surfaceMesh.H"
+#include "PtrList.H"
+#include "PtrMap.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -51,6 +54,7 @@ namespace Foam
 
 // Forward Declarations
 template<class Type> class fvPatchField;
+template<class Type> class fvsPatchField;
 
 /*---------------------------------------------------------------------------*\
                       Class volSurfaceMapping Declaration
@@ -69,7 +73,7 @@ public:
     // Constructors
 
         //- Construct from mesh
-        volSurfaceMapping(const faMesh& mesh)
+        explicit volSurfaceMapping(const faMesh& mesh)
         :
             mesh_(mesh)
         {}
@@ -85,59 +89,274 @@ public:
     ~volSurfaceMapping() = default;
 
 
-    // Member Functions
+    // Mapping to area fields
 
-        //- Map volume boundary field to surface
+        //- Map volume boundary fields as area field
+        template<class Type>
+        void mapToSurface
+        (
+            const GeometricBoundaryField<Type, fvPatchField, volMesh>&,
+            Field<Type>& result
+        ) const;
+
+        //- Map volume boundary fields as area field
         template<class Type>
         tmp<Field<Type>> mapToSurface
         (
-            const GeometricBoundaryField<Type, fvPatchField, volMesh>& df
+            const GeometricBoundaryField<Type, fvPatchField, volMesh>&
         ) const;
 
-        //- Map vol Field to surface Field
+        //- Map volume (boundary) fields to area field
+        template<class Type>
+        void mapToSurface
+        (
+            const GeometricField<Type, fvPatchField, volMesh>&,
+            Field<Type>& result
+        ) const;
+
+        //- Map volume (boundary) fields to area field
+        template<class Type>
+        void mapToSurface
+        (
+            const tmp<GeometricField<Type, fvPatchField, volMesh>>&,
+            Field<Type>& result
+        ) const;
+
+        //- Map volume (boundary) fields to area field
+        template<class Type>
+        tmp<Field<Type>> mapToSurface
+        (
+            const GeometricField<Type, fvPatchField, volMesh>&
+        ) const;
+
+        //- Map volume (boundary) fields to area field
+        template<class Type>
+        tmp<Field<Type>> mapToSurface
+        (
+            const tmp<GeometricField<Type, fvPatchField, volMesh>>&
+        ) const;
+
+
+        //- Map surface fields to area field
+        template<class Type>
+        void mapToSurface
+        (
+            const GeometricField<Type, fvsPatchField, surfaceMesh>&,
+            Field<Type>& result
+        ) const;
+
+        //- Map surface fields to area field
+        template<class Type>
+        tmp<Field<Type>> mapToSurface
+        (
+            const GeometricField<Type, fvsPatchField, surfaceMesh>&
+        ) const;
+
+        //- Map surface fields to area field
+        template<class Type>
+        void mapToSurface
+        (
+            const tmp<GeometricField<Type, fvsPatchField, surfaceMesh>>&,
+            Field<Type>& result
+        ) const;
+
+        //- Map surface fields to area field
+        template<class Type>
+        tmp<Field<Type>> mapToSurface
+        (
+            const tmp<GeometricField<Type, fvsPatchField, surfaceMesh>>&
+        ) const;
+
+
+        //- Map pre-calculated boundary fields to area field
+        template<class Type>
+        void mapToSurface
+        (
+            const UPtrList<Field<Type>>& patchFields,
+            Field<Type>& result
+        ) const;
+
+        //- Map pre-calculated boundary fields to area field
+        template<class Type>
+        void mapToSurface
+        (
+            const PtrMap<Field<Type>>& patchFields,
+            Field<Type>& result
+        ) const;
+
+        //- Map pre-calculated boundary fields to area field
+        template<class Type>
+        tmp<Field<Type>> mapToSurface
+        (
+            const UPtrList<Field<Type>>& patchFields
+        ) const;
+
+        //- Map pre-calculated boundary fields to area field
         template<class Type>
-        tmp<Field<Type>> mapToSurface(const Field<Type>& f) const;
+        tmp<Field<Type>> mapToSurface
+        (
+            const PtrMap<Field<Type>>& patchFields
+        ) const;
+
+
+        //- Map patch internal field to area field
+        template<class Type>
+        void mapInternalToSurface
+        (
+            const GeometricBoundaryField<Type, fvPatchField, volMesh>&,
+            Field<Type>& result
+        ) const;
 
-        //- Map patch internal field to surface
+        //- Map patch internal field to area field
         template<class Type>
         tmp<Field<Type>> mapInternalToSurface
         (
-            const GeometricBoundaryField<Type, fvPatchField, volMesh>& df
+            const GeometricBoundaryField<Type, fvPatchField, volMesh>&
         ) const;
 
-        //- Map surface field to volume boundary field
+        //- Map patch internal field to area field
+        template<class Type>
+        void mapInternalToSurface
+        (
+            const GeometricField<Type, fvPatchField, volMesh>&,
+            Field<Type>& result
+        ) const;
+
+        //- Map patch internal field to area field
+        template<class Type>
+        void mapInternalToSurface
+        (
+            const tmp<GeometricField<Type, fvPatchField, volMesh>>&,
+            Field<Type>& result
+        ) const;
+
+        //- Map patch internal field to area field
+        template<class Type>
+        tmp<Field<Type>> mapInternalToSurface
+        (
+            const GeometricField<Type, fvPatchField, volMesh>&
+        ) const;
+
+        //- Map patch internal field to area field
+        template<class Type>
+        tmp<Field<Type>> mapInternalToSurface
+        (
+            const tmp<GeometricField<Type, fvPatchField, volMesh>>&
+        ) const;
+
+
+    // Mapping from area field to volume (boundary) field
+
+        //- Map area field to volume boundary field,
+        //- optionally restricted to a single destination patch
         template<class Type>
         void mapToVolume
         (
-            const GeometricField<Type, faPatchField, areaMesh>& af,
-            GeometricBoundaryField<Type, fvPatchField, volMesh>& bf
+            const DimensionedField<Type, areaMesh>&,
+            GeometricBoundaryField<Type, fvPatchField, volMesh>& dest,
+            const label destPatchi = -1
+        ) const;
+
+        //- Map area field to volume boundary field,
+        //- optionally restricted to a single destination patch
+        template<class Type>
+        void mapToVolume
+        (
+            const tmp<DimensionedField<Type, areaMesh>>&,
+            GeometricBoundaryField<Type, fvPatchField, volMesh>& dest,
+            const label destPatchi = -1
+        ) const;
+
+        //- Map area field to volume boundary field,
+        //- optionally restricted to a single destination patch
+        template<class Type>
+        void mapToVolume
+        (
+            const DimensionedField<Type, areaMesh>&,
+            GeometricField<Type, fvPatchField, volMesh>& dest,
+            const label destPatchi = -1
         ) const;
 
-        //- Map surface tmp field to volume boundary field
+        //- Map area field to volume boundary field,
+        //- optionally restricted to a single destination patch
         template<class Type>
         void mapToVolume
+        (
+            const tmp<DimensionedField<Type, areaMesh>>&,
+            GeometricField<Type, fvPatchField, volMesh>& dest,
+            const label destPatchi = -1
+        ) const;
+
+        //- Map tmp area field to volume boundary field,
+        //- optionally restricted to a single destination patch
+        template<class Type>
+        void mapToVolume
+        (
+            const tmp<GeometricField<Type, faPatchField, areaMesh>>&,
+            GeometricBoundaryField<Type, fvPatchField, volMesh>& dest,
+            const label destPatchi = -1
+        ) const;
+
+        //- Map tmp area field to volume boundary field,
+        //- optionally restricted to a single destination patch
+        template<class Type>
+        void mapToVolume
+        (
+            const tmp<GeometricField<Type, faPatchField, areaMesh>>&,
+            GeometricField<Type, fvPatchField, volMesh>& dest,
+            const label destPatchi = -1
+        ) const;
+
+
+    // Mapping from area field to volume patch field
+
+        //- Map area field to a volume boundary patch
+        template<class Type>
+        void mapToVolumePatch
+        (
+            const DimensionedField<Type, areaMesh>& af,
+            Field<Type>& dest,
+            const label destPatchi
+        ) const;
+
+        //- Map tmp area field to a volume boundary patch
+        template<class Type>
+        void mapToVolumePatch
+        (
+            const tmp<DimensionedField<Type, areaMesh>>& af,
+            Field<Type>& dest,
+            const label destPatchi
+        ) const;
+
+        //- Map tmp area field to a volume boundary patch
+        template<class Type>
+        void mapToVolumePatch
         (
             const tmp<GeometricField<Type, faPatchField, areaMesh>>& taf,
-            GeometricBoundaryField<Type, fvPatchField, volMesh>& bf
+            Field<Type>& dest,
+            const label destPatchi
         ) const;
 
+
+    // Deprecated (to be removed)
+
+        //- Map vol Field to surface Field
+        template<class Type>
+        tmp<Field<Type>> mapToSurface(const Field<Type>&) const;
+
         //- Map surface field to field
         //  Assumes Field faces in the same order as Boundary
         template<class Type>
         void mapToField
         (
             const GeometricField<Type, faPatchField, areaMesh>& af,
-            Field<Type>& f
+            Field<Type>&
         ) const;
 
         //- Map surface field to volume field
         //  Assumes Field faces in the same order as Boundary
         template<class Type>
-        void mapToField
-        (
-            const Field<Type>& af,
-            Field<Type>& f
-        ) const;
+        void mapToField(const Field<Type>& af, Field<Type>&) const;
 };
 
 
-- 
GitLab