diff --git a/src/functionObjects/field/Make/files b/src/functionObjects/field/Make/files
index 6746b0b4a1..86eb165e04 100644
--- a/src/functionObjects/field/Make/files
+++ b/src/functionObjects/field/Make/files
@@ -96,4 +96,6 @@ flux/flux.C
ddt2/ddt2.C
zeroGradient/zeroGradient.C
+stabilityBlendingFactor/stabilityBlendingFactor.C
+
LIB = $(FOAM_LIBBIN)/libfieldFunctionObjects
diff --git a/src/functionObjects/field/stabilityBlendingFactor/stabilityBlendingFactor.C b/src/functionObjects/field/stabilityBlendingFactor/stabilityBlendingFactor.C
new file mode 100644
index 0000000000..a67339b00f
--- /dev/null
+++ b/src/functionObjects/field/stabilityBlendingFactor/stabilityBlendingFactor.C
@@ -0,0 +1,647 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
+ \\/ 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 "stabilityBlendingFactor.H"
+#include "addToRunTimeSelectionTable.H"
+#include "zeroGradientFvPatchFields.H"
+#include "fvcGrad.H"
+#include "surfaceInterpolate.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+ defineTypeNameAndDebug(stabilityBlendingFactor, 0);
+ addToRunTimeSelectionTable
+ (
+ functionObject,
+ stabilityBlendingFactor,
+ dictionary
+ );
+}
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
+
+void Foam::functionObjects::stabilityBlendingFactor::writeFileHeader
+(
+ Ostream& os
+) const
+{
+ writeHeader(os, "Stabilization blending factor");
+ writeCommented(os, "Time");
+ writeTabbed(os, "Scheme1");
+ writeTabbed(os, "Scheme2");
+ writeTabbed(os, "Blended");
+ os << endl;
+}
+
+bool Foam::functionObjects::stabilityBlendingFactor::calc()
+{
+ init(true);
+ return true;
+}
+
+
+bool Foam::functionObjects::stabilityBlendingFactor::init(bool first)
+{
+
+ const IOField* residualPtr =
+ mesh_.lookupObjectPtr>(residualName_);
+
+ if (residuals_)
+ {
+ if (!residualPtr)
+ {
+ WarningInFunction
+ << residualName_ << " is not available in the database."
+ << "It won't be considered for the blended field. " << nl
+ << "Add the corresponding FO (residuals). If the FO is already "
+ << "set, you need to wait for the first iteration."
+ << endl;
+
+ }
+ else
+ {
+ scalar meanRes = gAverage(mag(*residualPtr)) + VSMALL;
+
+ if (log)
+ {
+ Log << " Average(mag(residuals)) : " << meanRes << endl;
+ }
+
+ oldError_ = error_;
+ oldErrorIntegral_ = errorIntegral_;
+ error_ = mag(meanRes - mag(*residualPtr));
+ errorIntegral_ = oldErrorIntegral_ + 0.5*(error_ + oldError_);
+ const scalarField errorDifferential = error_ - oldError_;
+
+ const scalarField factorList
+ (
+ + P_*error_
+ + I_*errorIntegral_
+ + D_*errorDifferential
+ );
+
+ const scalarField indicatorResidual
+ (
+ max
+ (
+ min
+ (
+ mag(factorList - meanRes)/(maxResidual_*meanRes),
+ 1.0
+ ),
+ 0.0
+ )
+ );
+
+ forAll (indicator_, i)
+ {
+ indicator_[i] = indicatorResidual[i];
+ }
+ }
+ }
+
+ const volScalarField* nonOrthPtr =
+ mesh_.lookupObjectPtr(nonOrthogonalityName_);
+
+ if (nonOrthogonality_)
+ {
+ indicator_ =
+ max
+ (
+ indicator_,
+ min
+ (
+ max
+ (
+ 0.0,
+ (*nonOrthPtr - maxNonOrthogonality_)
+ /(minNonOrthogonality_ - maxNonOrthogonality_)
+ ),
+ 1.0
+ )
+ );
+
+ if (log)
+ {
+ Log << " Max non-orthogonality : " << max(*nonOrthPtr).value()
+ << endl;
+ }
+ }
+
+ const volScalarField* skewnessPtr =
+ mesh_.lookupObjectPtr(skewnessName_);
+
+ if (skewness_)
+ {
+ indicator_ =
+ max
+ (
+ indicator_,
+ min
+ (
+ max
+ (
+ 0.0,
+ (*skewnessPtr - maxSkewness_)
+ / (minSkewness_ - maxSkewness_)
+ ),
+ 1.0
+ )
+ );
+
+ if (log)
+ {
+ Log << " Max skewness : " << max(*skewnessPtr).value()
+ << endl;
+ }
+ }
+
+ const volScalarField* faceWeightsPtr =
+ mesh_.lookupObjectPtr(faceWeightName_);
+
+ if (faceWeight_)
+ {
+ indicator_ =
+ max
+ (
+ indicator_,
+ min
+ (
+ max
+ (
+ 0.0,
+ (minFaceWeight_ - *faceWeightsPtr)
+ / (minFaceWeight_ - maxFaceWeight_)
+ ),
+ 1.0
+ )
+ );
+
+ if (log)
+ {
+ Log << " Min face weight: " << min(*faceWeightsPtr).value()
+ << endl;
+ }
+ }
+
+
+ if (gradCc_)
+ {
+ tmp magGradCCPtr
+ (
+ new volScalarField
+ (
+ IOobject
+ (
+ "magGradCC",
+ time_.timeName(),
+ mesh_,
+ IOobject::NO_READ,
+ IOobject::NO_WRITE
+ ),
+ mesh_,
+ dimensionedScalar("0", dimless, Zero),
+ zeroGradientFvPatchScalarField::typeName
+ )
+ );
+
+ for (direction i=0; i(UName_);
+
+ if (Co_)
+ {
+ tmp CoPtr
+ (
+ new volScalarField
+ (
+ IOobject
+ (
+ "Co",
+ time_.timeName(),
+ mesh_,
+ IOobject::NO_READ,
+ IOobject::NO_WRITE
+ ),
+ mesh_,
+ dimensionedScalar("0", dimless, Zero),
+ zeroGradientFvPatchScalarField::typeName
+ )
+ );
+
+ volScalarField& Co = CoPtr.ref();
+
+ Co.primitiveFieldRef() =
+ mesh_.time().deltaT()*mag(*UNamePtr)/pow(mesh_.V(), 1.0/3.0);
+
+ indicator_ =
+ max
+ (
+ indicator_,
+ min(max(0.0, (Co - Co1_)/(Co2_ - Co1_)), 1.0)
+ );
+
+ if (log)
+ {
+ Log << " Max Co : " << max(Co).value()
+ << endl;
+ }
+ }
+
+ indicator_.correctBoundaryConditions();
+ indicator_.min(1.0);
+ indicator_.max(0.0);
+
+ // Update the blended surface field
+ surfaceScalarField* surBlendedPtr =
+ (
+ mesh_.lookupObjectRefPtr(resultName_)
+ );
+
+ *surBlendedPtr = fvc::interpolate(indicator_);
+
+ return true;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::functionObjects::stabilityBlendingFactor::stabilityBlendingFactor
+(
+ const word& name,
+ const Time& runTime,
+ const dictionary& dict
+)
+:
+ fieldExpression(name, runTime, dict),
+ writeFile(obr_, name, typeName, dict),
+ indicator_
+ (
+ IOobject
+ (
+ "blendedIndicator",
+ time_.timeName(),
+ mesh_,
+ IOobject::NO_READ,
+ IOobject::NO_WRITE
+ ),
+ mesh_,
+ dimensionedScalar("0", dimless, 0.0),
+ zeroGradientFvPatchScalarField::typeName
+ ),
+ nonOrthogonality_(dict.lookupOrDefault("switchNonOrtho", false)),
+ gradCc_(dict.lookupOrDefault("switchGradCc", false)),
+ residuals_(dict.lookupOrDefault("switchResiduals", false)),
+ faceWeight_(dict.lookupOrDefault("switchFaceWeight", false)),
+ skewness_(dict.lookupOrDefault("switchSkewness", false)),
+ Co_(dict.lookupOrDefault("switchCo", false)),
+
+ maxNonOrthogonality_
+ (
+ dict.lookupOrDefault("maxNonOrthogonality", 20.0)
+ ),
+ minNonOrthogonality_
+ (
+ dict.lookupOrDefault("minNonOrthogonality", 60.0)
+ ),
+ maxGradCc_(dict.lookupOrDefault("maxGradCc", 3.0)),
+ minGradCc_(dict.lookupOrDefault("minGradCc", 4.0)),
+ maxResidual_(dict.lookupOrDefault("maxResidual", 10.0)),
+ minFaceWeight_(dict.lookupOrDefault("minFaceWeight", 0.3)),
+ maxFaceWeight_(dict.lookupOrDefault("maxFaceWeight", 0.2)),
+ maxSkewness_(dict.lookupOrDefault("maxSkewness", 2.0)),
+ minSkewness_(dict.lookupOrDefault("minSkewness", 3.0)),
+ Co1_(dict.lookupOrDefault("Co1", 1.0)),
+ Co2_(dict.lookupOrDefault("Co2", 10.0)),
+
+ nonOrthogonalityName_
+ (
+ dict.lookupOrDefault("nonOrthogonality", "nonOrthoAngle")
+ ),
+ faceWeightName_
+ (
+ dict.lookupOrDefault("faceWeight", "faceWeight")
+ ),
+ skewnessName_
+ (
+ dict.lookupOrDefault("skewness", "skewness")
+ ),
+ residualName_
+ (
+ dict.lookupOrDefault("residual", "initialResidual:p")
+ ),
+ UName_
+ (
+ dict.lookupOrDefault("U", "U")
+ ),
+
+ tolerance_(0.001),
+ error_(mesh_.nCells(), 0.0),
+ errorIntegral_(mesh_.nCells(), 0.0),
+ oldError_(mesh_.nCells(), 0.0),
+ oldErrorIntegral_(mesh_.nCells(), 0.0),
+ P_(dict.lookupOrDefault("P", 3)),
+ I_(dict.lookupOrDefault("I", 0.0)),
+ D_(dict.lookupOrDefault("D", 0.25))
+{
+ read(dict);
+ setResultName(typeName, "");
+
+ tmp faceBlendedPtr
+ (
+ new surfaceScalarField
+ (
+ IOobject
+ (
+ resultName_,
+ time_.timeName(),
+ mesh_,
+ IOobject::NO_READ,
+ IOobject::NO_WRITE
+ ),
+ mesh_,
+ dimensionedScalar("0", dimless, 0.0)
+ )
+ );
+ store(resultName_, faceBlendedPtr);
+
+ const volScalarField* nonOrthPtr =
+ mesh_.lookupObjectPtr(nonOrthogonalityName_);
+
+ if (nonOrthogonality_)
+ {
+ if (!nonOrthPtr)
+ {
+ IOobject fieldHeader
+ (
+ nonOrthogonalityName_,
+ mesh_.time().constant(),
+ mesh_,
+ IOobject::MUST_READ,
+ IOobject::NO_WRITE
+ );
+
+ if (fieldHeader.typeHeaderOk(true, true, false))
+ {
+ volScalarField* vfPtr(new volScalarField(fieldHeader, mesh_));
+ mesh_.objectRegistry::store(vfPtr);
+ }
+ else
+ {
+ FatalErrorInFunction
+ << "Field : " << nonOrthogonalityName_ << " not found."
+ << exit(FatalError);
+ }
+ }
+ }
+
+
+ const volScalarField* faceWeightsPtr =
+ mesh_.lookupObjectPtr(faceWeightName_);
+
+ if (faceWeight_)
+ {
+ if (!faceWeightsPtr)
+ {
+ IOobject fieldHeader
+ (
+ faceWeightName_,
+ mesh_.time().constant(),
+ mesh_,
+ IOobject::MUST_READ,
+ IOobject::NO_WRITE
+ );
+
+ if (fieldHeader.typeHeaderOk(true, true, false))
+ {
+ volScalarField* vfPtr(new volScalarField(fieldHeader, mesh_));
+ mesh_.objectRegistry::store(vfPtr);
+ }
+ else
+ {
+ FatalErrorInFunction
+ << "Field : " << faceWeightName_ << " not found."
+ << exit(FatalError);
+ }
+ }
+ }
+
+ const volScalarField* skewnessPtr =
+ mesh_.lookupObjectPtr(skewnessName_);
+
+ if (skewness_)
+ {
+ if (!skewnessPtr)
+ {
+ IOobject fieldHeader
+ (
+ skewnessName_,
+ mesh_.time().constant(),
+ mesh_,
+ IOobject::MUST_READ,
+ IOobject::NO_WRITE
+ );
+
+ if (fieldHeader.typeHeaderOk(true, true, false))
+ {
+ volScalarField* vfPtr(new volScalarField(fieldHeader, mesh_));
+ mesh_.objectRegistry::store(vfPtr);
+ }
+ else
+ {
+ FatalErrorInFunction
+ << "Field : " << skewnessName_ << " not found."
+ << exit(FatalError);
+ }
+ }
+ }
+
+ if (log)
+ {
+ indicator_.writeOpt() = IOobject::AUTO_WRITE;
+ }
+
+ init(true);
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
+
+Foam::functionObjects::stabilityBlendingFactor::~stabilityBlendingFactor()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
+
+bool Foam::functionObjects::stabilityBlendingFactor::read
+(
+ const dictionary& dict
+)
+{
+ if (fieldExpression::read(dict) && writeFile::read(dict))
+ {
+ dict.lookup("switchNonOrtho") >> nonOrthogonality_;
+ dict.lookup("switchGradCc") >> gradCc_;
+ dict.lookup("switchResiduals") >> residuals_;
+ dict.lookup("switchFaceWeight") >> faceWeight_;
+ dict.lookup("switchSkewness") >> skewness_;
+ dict.lookup("switchCo") >> Co_;
+
+ dict.readIfPresent("maxNonOrthogonality", maxNonOrthogonality_);
+ dict.readIfPresent("maxGradCc", maxGradCc_);
+ dict.readIfPresent("maxResidual", maxResidual_);
+ dict.readIfPresent("maxSkewness", maxSkewness_);
+ dict.readIfPresent("maxFaceWeight", maxFaceWeight_);
+ dict.readIfPresent("Co2", Co2_);
+
+ dict.readIfPresent("minFaceWeight", minFaceWeight_);
+ dict.readIfPresent("minNonOrthogonality", minNonOrthogonality_);
+ dict.readIfPresent("minGradCc", minGradCc_);
+ dict.readIfPresent("minSkewness", minSkewness_);
+ dict.readIfPresent("Co1", Co1_);
+
+
+ dict.readIfPresent("P", P_);
+ dict.readIfPresent("I", I_);
+ dict.readIfPresent("D", D_);
+
+ tolerance_ = 0.001;
+ if
+ (
+ dict.readIfPresent("tolerance", tolerance_)
+ && (tolerance_ < 0 || tolerance_ > 1)
+ )
+ {
+ FatalErrorInFunction
+ << "tolerance must be in the range 0 to 1. Supplied value: "
+ << tolerance_ << exit(FatalError);
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+bool Foam::functionObjects::stabilityBlendingFactor::write()
+{
+ // Generate scheme statistics
+ label nCellsScheme1 = 0;
+ label nCellsScheme2 = 0;
+ label nCellsBlended = 0;
+ forAll(indicator_, celli)
+ {
+ scalar i = indicator_[celli];
+
+ if (i < tolerance_)
+ {
+ nCellsScheme2++;
+ }
+ else if (i > (1 - tolerance_))
+ {
+ nCellsScheme1++;
+ }
+ else
+ {
+ nCellsBlended++;
+ }
+ }
+
+ reduce(nCellsScheme1, sumOp