diff --git a/applications/test/speed/vectorSpeed/Test-vectorSpeed.C b/applications/test/speed/vectorSpeed/Test-vectorSpeed.C
index 2ab430d34a..0acdc5d84c 100644
--- a/applications/test/speed/vectorSpeed/Test-vectorSpeed.C
+++ b/applications/test/speed/vectorSpeed/Test-vectorSpeed.C
@@ -1,3 +1,38 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2011 OpenFOAM Foundation
+ Copyright (C) 2023 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 .
+
+Application
+ Test-vectorSpeed
+
+Description
+ Test speeds, usability of some field operations
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
#include "primitiveFields.H"
#include "cpuTime.H"
#include "IOstreams.H"
@@ -5,12 +40,22 @@
using namespace Foam;
-int main()
-{
- const label nIter = 100;
- const label size = 1000000;
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
- Info<< "Initialising fields" << endl;
+int main(int argc, char *argv[])
+{
+ argList::noParallel();
+ argList::addBoolOption("lerp");
+
+ argList args(argc, argv);
+
+ const label nIter = 1000;
+ const label size = (1000000);
+
+ Info<< "Initialising fields. size:" << size
+ << " max:" << labelMax << endl;
+
+ scalarField onet(size);
vectorField
vf1(size, vector::one),
@@ -18,11 +63,14 @@ int main()
vf3(size, vector::one),
vf4(size);
- Info<< "Done\n" << endl;
+ Info<< "Start loop: " << nIter << endl;
+ cpuTime timing;
+
+ // Timing is mostly malloc anyhow...
+
+ if (!args.found("lerp"))
{
- cpuTime executionTime;
-
Info<< "vectorField algebra" << endl;
for (int j=0; j, clamp)
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+TERNARY_FUNCTION(Type, Type, Type, scalar, lerp)
+TERNARY_TYPE_FUNCTION_FFS(Type, Type, Type, scalar, lerp)
+
+
// * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
UNARY_OPERATOR(Type, Type, -, negate, transform)
diff --git a/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctions.H b/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctions.H
index 537c736624..e444690399 100644
--- a/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctions.H
+++ b/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctions.H
@@ -169,6 +169,12 @@ clamp
BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax, clamp)
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+TERNARY_FUNCTION(Type, Type, Type, scalar, lerp)
+TERNARY_TYPE_FUNCTION_FFS(Type, Type, Type, scalar, lerp)
+
+
// * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
UNARY_OPERATOR(Type, Type, -, negate, transform)
diff --git a/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctionsM.C b/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctionsM.C
index e8ee293203..c748949715 100644
--- a/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctionsM.C
+++ b/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctionsM.C
@@ -694,4 +694,378 @@ tmp> operator Op \
BINARY_TYPE_OPERATOR_FS(ReturnType, Type1, Type2, Op, OpName, OpFunc)
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#define TERNARY_FUNCTION(ReturnType, Type1, Type2, Type3, Func) \
+ \
+TEMPLATE \
+void Func \
+( \
+ DimensionedField& result, \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const DimensionedField& f3 \
+) \
+{ \
+ /* TBD: reset dimensions? */ \
+ Func(result.field(), f1.field(), f2.field(), f3.field()); \
+ result.oriented() = Func(f1.oriented(), f2.oriented()); \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const DimensionedField& f3 \
+) \
+{ \
+ auto tres = \
+ reuseTmpDimensionedField::New \
+ ( \
+ f1, \
+ #Func "(" + f1.name() + ',' + f2.name() + ',' + f3.name() + ')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, f3); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const DimensionedField& f2, \
+ const DimensionedField& f3 \
+) \
+{ \
+ const auto& f1 = tf1(); \
+ \
+ auto tres = \
+ reuseTmpDimensionedField::New \
+ ( \
+ tf1, \
+ #Func "(" + f1.name() + ',' + f2.name() + ',' + f3.name() + ')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, f3); \
+ tf1.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const tmp>& tf2, \
+ const DimensionedField& f3 \
+) \
+{ \
+ const auto& f2 = tf2(); \
+ \
+ auto tres = \
+ reuseTmpDimensionedField::New \
+ ( \
+ tf2, \
+ #Func "(" + f1.name() +','+ f2.name() +','+ f3.name() +')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, f3); \
+ tf2.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const tmp>& tf3 \
+) \
+{ \
+ const auto& f3 = tf3(); \
+ \
+ auto tres = \
+ reuseTmpDimensionedField::New \
+ ( \
+ tf3, \
+ #Func "(" + f1.name() +','+ f2.name() +','+ f3.name() +')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, f3); \
+ tf3.clear(); \
+ return tres; \
+} \
+ \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const DimensionedField& f3 \
+) \
+{ \
+ const auto& f1 = tf1(); \
+ const auto& f2 = tf2(); \
+ \
+ auto tres = \
+ reuseTmpTmpDimensionedField \
+ ::New \
+ ( \
+ tf1, \
+ tf2, \
+ #Func "(" + f1.name() +','+ f2.name() +','+ f3.name() +')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, f3); \
+ tf1.clear(); \
+ tf2.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const DimensionedField& f2, \
+ const tmp>& tf3 \
+) \
+{ \
+ const auto& f1 = tf1(); \
+ const auto& f3 = tf3(); \
+ \
+ auto tres = \
+ reuseTmpTmpDimensionedField \
+ ::New \
+ ( \
+ tf1, \
+ tf3, \
+ #Func "(" + f1.name() +','+ f2.name() +','+ f3.name() +')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, f3); \
+ tf1.clear(); \
+ tf3.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const tmp>& tf2, \
+ const tmp>& tf3 \
+) \
+{ \
+ const auto& f2 = tf2(); \
+ const auto& f3 = tf3(); \
+ \
+ auto tres = \
+ reuseTmpTmpDimensionedField \
+ ::New \
+ ( \
+ tf2, \
+ tf3, \
+ #Func "(" + f1.name() +','+ f2.name() +','+ f3.name() +')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, f3); \
+ tf2.clear(); \
+ tf3.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const tmp>& tf3 \
+) \
+{ \
+ const auto& f1 = tf1(); \
+ const auto& f2 = tf2(); \
+ const auto& f3 = tf3(); \
+ \
+ /* TBD: check all three types? */ \
+ auto tres = \
+ reuseTmpTmpDimensionedField \
+ ::New \
+ ( \
+ tf1, \
+ tf2, \
+ #Func "(" + f1.name() +','+ f2.name() +','+ f3.name() +')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, f3); \
+ tf1.clear(); \
+ tf2.clear(); \
+ tf3.clear(); \
+ return tres; \
+} \
+
+
+#define TERNARY_TYPE_FUNCTION_FFS(ReturnType, Type1, Type2, Type3, Func) \
+ \
+TEMPLATE \
+void Func \
+( \
+ DimensionedField& result, \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const dimensioned& dt3 \
+) \
+{ \
+ /* TBD: reset dimensions? */ \
+ Func(result.field(), f1.field(), f2.field(), dt3.value()); \
+ result.oriented() = Func(f1.oriented(), f2.oriented()); \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const dimensioned& dt3 \
+) \
+{ \
+ auto tres = \
+ reuseTmpDimensionedField::New \
+ ( \
+ f1, \
+ #Func "(" + f1.name() + ',' + f2.name() + ',' + dt3.name() + ')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, dt3); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const Type3& s3 \
+) \
+{ \
+ return Func(f1, f2, dimensioned(s3)); \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const DimensionedField& f2, \
+ const dimensioned& dt3 \
+) \
+{ \
+ const auto& f1 = tf1(); \
+ \
+ auto tres = \
+ reuseTmpDimensionedField::New \
+ ( \
+ tf1, \
+ #Func "(" + f1.name() + ',' + f2.name() + ',' + dt3.name() + ')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, dt3); \
+ tf1.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const DimensionedField& f2, \
+ const Type3& s3 \
+) \
+{ \
+ return Func(tf1, f2, dimensioned(s3)); \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const tmp>& tf2, \
+ const dimensioned& dt3 \
+) \
+{ \
+ const auto& f2 = tf2(); \
+ \
+ auto tres = \
+ reuseTmpDimensionedField::New \
+ ( \
+ tf2, \
+ #Func "(" + f1.name() + ',' + f2.name() + ',' + dt3.name() + ')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, dt3); \
+ tf2.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const tmp>& tf2, \
+ const Type3& s3 \
+) \
+{ \
+ return Func(f1, tf2, dimensioned(s3)); \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const dimensioned& dt3 \
+) \
+{ \
+ const auto& f1 = tf1(); \
+ const auto& f2 = tf2(); \
+ \
+ auto tres = \
+ reuseTmpTmpDimensionedField \
+ ::New \
+ ( \
+ tf1, \
+ tf2, \
+ #Func "(" + f1.name() + ',' + f2.name() + ',' + dt3.name() + ')', \
+ Func(f1.dimensions(), f2.dimensions()) \
+ ); \
+ \
+ Func(tres.ref(), f1, f2, dt3); \
+ tf1.clear(); \
+ tf2.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const Type3& s3 \
+) \
+{ \
+ return Func(tf1, tf2, dimensioned(s3)); \
+}
+
+
// ************************************************************************* //
diff --git a/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctionsM.H b/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctionsM.H
index a3157242f2..7c1d81c6df 100644
--- a/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctionsM.H
+++ b/src/OpenFOAM/fields/DimensionedFields/DimensionedField/DimensionedFieldFunctionsM.H
@@ -326,4 +326,158 @@ tmp> operator Op \
BINARY_TYPE_OPERATOR_FS(ReturnType, Type1, Type2, Op, OpName, OpFunc)
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#define TERNARY_FUNCTION(ReturnType, Type1, Type2, Type3, Func) \
+ \
+TEMPLATE \
+void Func \
+( \
+ DimensionedField& result, \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const DimensionedField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const DimensionedField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const DimensionedField& f2, \
+ const DimensionedField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const tmp>& tf2, \
+ const DimensionedField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const tmp>& tf3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const DimensionedField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const DimensionedField& f2, \
+ const tmp>& tf3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const tmp>& tf2, \
+ const tmp>& tf3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const tmp>& tf3 \
+);
+
+
+#define TERNARY_TYPE_FUNCTION_FFS(ReturnType, Type1, Type2, Type3, Func) \
+ \
+TEMPLATE \
+void Func \
+( \
+ DimensionedField& result, \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const dimensioned& dt3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const dimensioned& dt3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const DimensionedField& f2, \
+ const Type3& s3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const DimensionedField& f2, \
+ const dimensioned& dt3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const DimensionedField& f2, \
+ const Type3& s3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const tmp>& tf2, \
+ const dimensioned& dt3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const DimensionedField& f1, \
+ const tmp>& tf2, \
+ const Type3& s3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const dimensioned& dt3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const Type3& s3 \
+);
+
+
// ************************************************************************* //
diff --git a/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctions.C b/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctions.C
index dfda7e3602..54a489deac 100644
--- a/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctions.C
+++ b/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctions.C
@@ -634,6 +634,12 @@ BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide)
BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax, clamp)
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+TERNARY_FUNCTION(Type, Type, Type, scalar, lerp)
+TERNARY_TYPE_FUNCTION_FFS(Type, Type, Type, scalar, lerp)
+
+
/* * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * */
UNARY_OPERATOR(Type, Type, -, negate)
diff --git a/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctions.H b/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctions.H
index 9776c63c37..843bf67c58 100644
--- a/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctions.H
+++ b/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctions.H
@@ -290,6 +290,11 @@ BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide)
// Note: works with zero_one through implicit conversion to MinMax
BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax, clamp)
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+TERNARY_FUNCTION(Type, Type, Type, scalar, lerp)
+TERNARY_TYPE_FUNCTION_FFS(Type, Type, Type, scalar, lerp)
+
/* * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * */
diff --git a/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctionsM.C b/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctionsM.C
index d2dc88ce0b..5becaa4753 100644
--- a/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctionsM.C
+++ b/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctionsM.C
@@ -459,4 +459,239 @@ tmp> operator Op \
BINARY_TYPE_OPERATOR_FS(ReturnType, Type1, Type2, Op, OpFunc)
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#define TERNARY_FUNCTION(ReturnType, Type1, Type2, Type3, Func) \
+ \
+TEMPLATE \
+void Func \
+( \
+ FieldField& result, \
+ const FieldField& f1, \
+ const FieldField& f2, \
+ const FieldField& f3 \
+) \
+{ \
+ const label loopLen = (result).size(); \
+ \
+ for (label i = 0; i < loopLen; ++i) \
+ { \
+ Func(result[i], f1[i], f2[i], f3[i]); \
+ } \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const FieldField& f1, \
+ const FieldField& f2, \
+ const FieldField& f3 \
+) \
+{ \
+ auto tres = FieldField::NewCalculatedType(f1); \
+ Func(tres.ref(), f1, f2, f3); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const FieldField& f2, \
+ const FieldField& f3 \
+) \
+{ \
+ auto tres = reuseTmpFieldField::New(tf1); \
+ Func(tres.ref(), tf1(), f2, f3); \
+ tf1.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const FieldField& f1, \
+ const tmp>& tf2, \
+ const FieldField& f3 \
+) \
+{ \
+ auto tres = reuseTmpFieldField::New(tf2); \
+ Func(tres.ref(), f1, tf2(), f3); \
+ tf2.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const FieldField& f1, \
+ const FieldField& f2, \
+ const tmp>& tf3 \
+) \
+{ \
+ auto tres = reuseTmpFieldField::New(tf3); \
+ Func(tres.ref(), f1, f2, tf3()); \
+ tf3.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const FieldField& f3 \
+) \
+{ \
+ tmp> tres \
+ ( \
+ reuseTmpTmpFieldField:: \
+ New(tf1, tf2) \
+ ); \
+ Func(tres.ref(), tf1(), tf2(), f3); \
+ tf1.clear(); \
+ tf2.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const FieldField& f2, \
+ const tmp>& tf3 \
+) \
+{ \
+ tmp> tres \
+ ( \
+ reuseTmpTmpFieldField:: \
+ New(tf1, tf3) \
+ ); \
+ Func(tres.ref(), tf1(), f2, tf3()); \
+ tf1.clear(); \
+ tf3.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const FieldField& f1, \
+ const tmp>& tf2, \
+ const tmp>& tf3 \
+) \
+{ \
+ tmp> tres \
+ ( \
+ reuseTmpTmpFieldField:: \
+ New(tf2, tf3) \
+ ); \
+ Func(tres.ref(), f1, tf2(), tf3()); \
+ tf2.clear(); \
+ tf3.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const tmp>& tf3 \
+) \
+{ \
+ /* TBD: check all three types? */ \
+ tmp> tres \
+ ( \
+ reuseTmpTmpFieldField:: \
+ New(tf1, tf2) \
+ ); \
+ Func(tres.ref(), tf1(), tf2(), tf3()); \
+ tf1.clear(); \
+ tf2.clear(); \
+ tf3.clear(); \
+ return tres; \
+}
+
+
+#define TERNARY_TYPE_FUNCTION_FFS(ReturnType, Type1, Type2, Type3, Func) \
+ \
+TEMPLATE \
+void Func \
+( \
+ FieldField& result, \
+ const FieldField& f1, \
+ const FieldField& f2, \
+ const Type3& s3 \
+) \
+{ \
+ const label loopLen = (result).size(); \
+ \
+ for (label i = 0; i < loopLen; ++i) \
+ { \
+ Func(result[i], f1[i], f2[i], s3); \
+ } \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const FieldField& f1, \
+ const FieldField& f2, \
+ const Type3& s3 \
+) \
+{ \
+ auto tres = FieldField::NewCalculatedType(f1); \
+ Func(tres.ref(), f1, f2, s3); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const FieldField& f2, \
+ const Type3& s3 \
+) \
+{ \
+ auto tres = reuseTmpFieldField::New(tf1); \
+ Func(tres.ref(), tf1(), f2, s3); \
+ tf1.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const FieldField& f1, \
+ const tmp>& tf2, \
+ const Type3& s3 \
+) \
+{ \
+ auto tres = reuseTmpFieldField::New(tf2); \
+ Func(tres.ref(), f1, tf2(), s3); \
+ tf2.clear(); \
+ return tres; \
+} \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const Type3& s3 \
+) \
+{ \
+ tmp> tres \
+ ( \
+ reuseTmpTmpFieldField:: \
+ New(tf1, tf2) \
+ ); \
+ Func(tres.ref(), tf1(), tf2(), s3); \
+ tf1.clear(); \
+ tf2.clear(); \
+ return tres; \
+}
+
+
// ************************************************************************* //
diff --git a/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctionsM.H b/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctionsM.H
index 1e3b0501e8..81aac62600 100644
--- a/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctionsM.H
+++ b/src/OpenFOAM/fields/FieldFields/FieldField/FieldFieldFunctionsM.H
@@ -273,4 +273,126 @@ tmp> operator Op \
BINARY_TYPE_OPERATOR_FS(ReturnType, Type1, Type2, Op, OpFunc)
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#define TERNARY_FUNCTION(ReturnType, Type1, Type2, Type3, Func) \
+ \
+TEMPLATE \
+void Func \
+( \
+ FieldField& result, \
+ const FieldField& f1, \
+ const FieldField& f2, \
+ const FieldField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const FieldField& f1, \
+ const FieldField& f2, \
+ const FieldField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const FieldField& f2, \
+ const FieldField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const FieldField& f1, \
+ const tmp>& tf2, \
+ const FieldField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const FieldField& f1, \
+ const FieldField& f2, \
+ const tmp>& tf3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const tmp>& tf2, \
+ const FieldField& f3 \
+); \
+ \
+TEMPLATE \
+tmp> Func \
+( \
+ const tmp>& tf1, \
+ const FieldField