diff --git a/src/finiteVolume/Make/files b/src/finiteVolume/Make/files
index 36b69f77f0..b0cb2bc25b 100644
--- a/src/finiteVolume/Make/files
+++ b/src/finiteVolume/Make/files
@@ -363,6 +363,7 @@ $(schemes)/localBlended/localBlended.C
$(schemes)/limiterBlended/limiterBlended.C
$(schemes)/CoBlended/CoBlended.C
$(schemes)/cellCoBlended/cellCoBlended.C
+$(schemes)/zoneBlended/zoneBlended.C
$(schemes)/localMax/localMax.C
$(schemes)/localMin/localMin.C
diff --git a/src/finiteVolume/interpolation/surfaceInterpolation/schemes/zoneBlended/zoneBlended.C b/src/finiteVolume/interpolation/surfaceInterpolation/schemes/zoneBlended/zoneBlended.C
new file mode 100644
index 0000000000..f8ac1722f7
--- /dev/null
+++ b/src/finiteVolume/interpolation/surfaceInterpolation/schemes/zoneBlended/zoneBlended.C
@@ -0,0 +1,39 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2024 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+ This file is part of OpenFOAM.
+
+ OpenFOAM is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OpenFOAM. If not, see .
+
+\*---------------------------------------------------------------------------*/
+
+#include "fvMesh.H"
+#include "zoneBlended.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+ makeSurfaceInterpolationScheme(zoneBlended);
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/interpolation/surfaceInterpolation/schemes/zoneBlended/zoneBlended.H b/src/finiteVolume/interpolation/surfaceInterpolation/schemes/zoneBlended/zoneBlended.H
new file mode 100644
index 0000000000..49a2cb6c5e
--- /dev/null
+++ b/src/finiteVolume/interpolation/surfaceInterpolation/schemes/zoneBlended/zoneBlended.H
@@ -0,0 +1,370 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2024 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+ This file is part of OpenFOAM.
+
+ OpenFOAM is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OpenFOAM. If not, see .
+
+Class
+ Foam::zoneBlended
+
+Group
+ grpFvSurfaceInterpolationSchemes
+
+Description
+ Multi-faceZone based blending differencing scheme.
+
+ Schemes are set in dictonary format according to:
+
+ \verbatim
+ divSchemes
+ {
+ .
+ .
+ div(phi,U) Gauss zoneBlended
+ {
+ default defaultScheme;
+ faceZone1 scheme1;
+ faceZone2 scheme2;
+ ...
+ faceZoneN schemeN;
+ }
+ .
+ .
+ }
+ \endverbatim
+
+ The \c default entry specifies the background scheme; additional schemes
+ can be set per \c faceZone, e.g. \c scheme1 is applied to \c facZone1,
+ \c scheme2 is applied to \c facZone2 etc.
+
+
+Usage
+ Example of the \c zoneBlended scheme to use \c linearUpwind as the
+ background scheme and \c upwind in \c faceZone1:
+
+ \verbatim
+ divSchemes
+ {
+ .
+ .
+ div(phi,U) Gauss zoneBlended
+ {
+ default linearUpwind grad(U);
+ faceZone1 upwind;
+ };
+ .
+ .
+ }
+ \endverbatim
+
+SourceFiles
+ zoneBlended.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_zoneBlended_H
+#define Foam_zoneBlended_H
+
+#include "surfaceInterpolationScheme.H"
+#include "blendedSchemeBase.H"
+#include "surfaceInterpolate.H"
+#include "UIndirectList.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+ Class zoneBlended Declaration
+\*---------------------------------------------------------------------------*/
+
+template
+class zoneBlended
+:
+ public surfaceInterpolationScheme,
+ public blendedSchemeBase
+{
+ using SurfaceField = GeometricField;
+ using VolumeField = GeometricField;
+
+ // Private data
+
+ //- Face zones
+ wordList zoneNames_;
+
+ //- Schemes
+ // Note: 0 index used to describe default/background scheme
+ List>> schemePtrs_;
+
+ //- Corrected flag - true if any of the schemes has corrected() set
+ bool corrected_;
+
+
+ // Private Member Functions
+
+ //- Set the lists of face zone names and schemes
+ void setSchemes(const fvMesh& mesh, const dictionary& dict);
+
+ //- Set the lists of face zone names and schemes
+ void setSchemes
+ (
+ const fvMesh& mesh,
+ const surfaceScalarField& faceFlux,
+ const dictionary& dict
+ );
+
+ //- Retrieve a scheme from the list
+ const surfaceInterpolationScheme& scheme
+ (
+ const label schemei
+ ) const
+ {
+ return schemePtrs_[schemei]();
+ }
+
+ //- Set destination values from source values for a face zone
+ template
+ void setFaceZoneValues
+ (
+ FieldType& dest,
+ const FieldType& src,
+ const faceZone& fz
+ ) const;
+
+ //- Set destination values to zero for a face zone
+ template
+ void zeroFaceZoneValues(FieldType& dest, const faceZone& fz) const;
+
+ //- No copy construct
+ zoneBlended(const zoneBlended&) = delete;
+
+ //- No copy assignment
+ void operator=(const zoneBlended&) = delete;
+
+
+public:
+
+ //- Runtime type information
+ TypeName("zoneBlended");
+
+
+ // Constructors
+
+ //- Construct from mesh and Istream.
+ // The name of the flux field is read from the Istream and looked-up
+ // from the mesh objectRegistry
+ zoneBlended(const fvMesh& mesh, Istream& is)
+ :
+ surfaceInterpolationScheme(mesh),
+ zoneNames_(),
+ schemePtrs_(),
+ corrected_(false)
+ {
+ const dictionary dict(is);
+
+ setSchemes(mesh, dict);
+ }
+
+
+ //- Construct from mesh, faceFlux and Istream
+ zoneBlended
+ (
+ const fvMesh& mesh,
+ const surfaceScalarField& faceFlux,
+ Istream& is
+ )
+ :
+ surfaceInterpolationScheme(mesh),
+ zoneNames_(),
+ schemePtrs_(),
+ corrected_(false)
+ {
+ const dictionary dict(is);
+
+ setSchemes(mesh, faceFlux, dict);
+ }
+
+
+ // Member Functions
+
+ //- Return the face-based blending factor
+ virtual tmp blendingFactor
+ (
+ const VolumeField& vf
+ ) const
+ {
+ const auto& mesh = vf.mesh();
+ auto tbf = surfaceScalarField::New("blendingFactor", mesh, dimless);
+ auto& bf = tbf.ref();
+ auto& bbf = bf.boundaryFieldRef();
+ bf = 0.0;
+
+ const auto& pbm = mesh.boundaryMesh();
+ const auto& zones = mesh.faceZones();
+
+ // Use blending factor to show different zones
+ for (label zonei=1; zonei weights(const VolumeField& vf) const
+ {
+ const auto& mesh = vf.mesh();
+ auto tweights =
+ surfaceScalarField::New("weights", vf.mesh(), dimless);
+ auto& weights = tweights.ref();
+
+ // Set default scheme weights
+ weights = this->scheme(0).weights(vf);
+
+ // Set face zone weights
+ const auto& zones = mesh.faceZones();
+
+ for (label schemei=1; schemeischeme(schemei);
+
+ auto tschemeWeights = scheme.weights(vf);
+ const auto& schemeWeights = tschemeWeights();
+ const auto& fz = zones[zoneNames_[schemei]];
+
+ setFaceZoneValues(weights, schemeWeights, fz);
+ }
+
+ return tweights;
+ }
+
+
+ //- Return the face-interpolate of the given cell field
+ // with explicit correction
+ tmp interpolate(const VolumeField& vf) const
+ {
+ return
+ surfaceInterpolationScheme::interpolate
+ (
+ vf,
+ weights(vf)
+ );
+ }
+
+
+ //- Return true if this scheme uses an explicit correction
+ virtual bool corrected() const
+ {
+ return corrected_;
+ }
+
+
+ //- Return the explicit correction to the face-interpolate
+ //- for the given field
+ virtual tmp correction
+ (
+ const VolumeField& vf
+ ) const
+ {
+ const auto& mesh = vf.mesh();
+ auto tcorr =
+ SurfaceField::New("correction", vf.mesh(), vf.dimensions());
+ auto& corr = tcorr.ref();
+ corr = dimensioned(vf.dimensions(), Zero);
+
+ // Set default scheme correction
+ const auto& scheme0 = this->scheme(0);
+ if (scheme0.corrected())
+ {
+ corr = scheme0.correction(vf);
+ }
+
+ // Only the default scheme exists - can exit early
+ if (schemePtrs_.size() == 1) return tcorr;
+
+ // Set correction field in faceZones
+ const auto& zones = mesh.faceZones();
+
+ for (label schemei=1; schemeischeme(schemei);
+
+ if (scheme.corrected())
+ {
+ auto tschemeCorr = scheme.correction(vf);
+ const auto& schemeCorr = tschemeCorr();
+ const auto& fz = zones[zoneNames_[schemei]];
+
+ setFaceZoneValues(corr, schemeCorr, fz);
+ }
+ else
+ {
+ if (scheme0.corrected())
+ {
+ // Remove correction from base scheme face zone faces
+ const auto& fz = zones[zoneNames_[schemei]];
+ zeroFaceZoneValues(corr, fz);
+ }
+ }
+ }
+
+ return tcorr;
+ }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+ #include "zoneBlendedTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/interpolation/surfaceInterpolation/schemes/zoneBlended/zoneBlendedTemplates.C b/src/finiteVolume/interpolation/surfaceInterpolation/schemes/zoneBlended/zoneBlendedTemplates.C
new file mode 100644
index 0000000000..687d01847e
--- /dev/null
+++ b/src/finiteVolume/interpolation/surfaceInterpolation/schemes/zoneBlended/zoneBlendedTemplates.C
@@ -0,0 +1,203 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2024 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+ This file is part of OpenFOAM.
+
+ OpenFOAM is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OpenFOAM. If not, see .
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template
+void Foam::zoneBlended::setSchemes
+(
+ const fvMesh& mesh,
+ const dictionary& dict
+)
+{
+ zoneNames_.resize(dict.size());
+ schemePtrs_.resize(dict.size());
+
+ schemePtrs_[0] =
+ surfaceInterpolationScheme::New(mesh, dict.lookup("default"));
+
+ zoneNames_[0] = "default";
+
+ corrected_ = scheme(0).corrected();
+
+ label schemei = 1;
+
+ for (const auto& e : dict)
+ {
+ if (e.isDict())
+ {
+ FatalIOErrorInFunction(dict)
+ << "Entries must be given in zoneName-scheme pairs"
+ << abort(FatalIOError);
+ }
+
+ const word& key = e.keyword();
+
+ if (key == "default")
+ {
+ // Background scheme - already handled at index 0
+ }
+ else
+ {
+ zoneNames_[schemei] = key;
+
+ schemePtrs_[schemei] =
+ surfaceInterpolationScheme::New(mesh, e.stream());
+
+ corrected_ = corrected_ || scheme(schemei).corrected();
+
+ ++schemei;
+ }
+ }
+}
+
+
+template
+void Foam::zoneBlended::setSchemes
+(
+ const fvMesh& mesh,
+ const surfaceScalarField& faceFlux,
+ const dictionary& dict
+)
+{
+ zoneNames_.resize(dict.size());
+ schemePtrs_.resize(dict.size());
+
+ schemePtrs_[0] =
+ surfaceInterpolationScheme::New
+ (
+ mesh,
+ faceFlux,
+ dict.lookup("default")
+ );
+
+ zoneNames_[0] = "default";
+
+ corrected_ = scheme(0).corrected();
+
+ label schemei = 1;
+
+ for (const auto& e : dict)
+ {
+ if (e.isDict())
+ {
+ FatalIOErrorInFunction(dict)
+ << "Entries must be given in faceZoneName-scheme pairs"
+ << abort(FatalIOError);
+ }
+
+ const word& key = e.keyword();
+
+ if (key == "default")
+ {
+ // Background scheme - already handled at index 0
+ }
+ else
+ {
+ zoneNames_[schemei] = key;
+
+ schemePtrs_[schemei] =
+ surfaceInterpolationScheme::New
+ (
+ mesh,
+ faceFlux,
+ e.stream()
+ );
+
+ corrected_ = corrected_ || scheme(schemei).corrected();
+
+ ++schemei;
+ }
+ }
+}
+
+
+template
+template
+void Foam::zoneBlended::setFaceZoneValues
+(
+ FieldType& dest,
+ const FieldType& src,
+ const faceZone& fz
+) const
+{
+ const auto& mesh = dest.mesh();
+ const auto& pbm = mesh.boundaryMesh();
+ const auto& srcBf = src.boundaryField();
+ auto& destBf = dest.boundaryFieldRef();
+
+ for (const label facei : fz)
+ {
+ if (mesh.isInternalFace(facei))
+ {
+ dest[facei] = src[facei];
+ }
+ else
+ {
+ const labelPair pf = pbm.whichPatchFace(facei);
+ auto& pdest = destBf[pf.first()];
+ if (pdest.size())
+ {
+ pdest[pf.second()] = srcBf[pf.first()][pf.second()];
+ }
+ }
+ }
+}
+
+
+template
+template
+void Foam::zoneBlended::zeroFaceZoneValues
+(
+ FieldType& dest,
+ const faceZone& fz
+) const
+{
+ const auto& mesh = dest.mesh();
+ const auto& pbm = mesh.boundaryMesh();
+ auto& destBf = dest.boundaryFieldRef();
+
+ for (const label facei : fz)
+ {
+ if (mesh.isInternalFace(facei))
+ {
+ dest[facei] = pTraits::zero;
+ }
+ else
+ {
+ const labelPair pf = pbm.whichPatchFace(facei);
+ auto& pdest = destBf[pf.first()];
+ if (pdest.size())
+ {
+ pdest[pf.second()] = pTraits::zero;
+ }
+ }
+ }
+}
+
+
+// ************************************************************************* //