diff --git a/applications/test/minMax1/Make/files b/applications/test/minMax1/Make/files
new file mode 100644
index 0000000000..3b5fddb9fc
--- /dev/null
+++ b/applications/test/minMax1/Make/files
@@ -0,0 +1,3 @@
+Test-minMax1.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-minMax1
diff --git a/applications/test/minMax1/Make/options b/applications/test/minMax1/Make/options
new file mode 100644
index 0000000000..d27c95d033
--- /dev/null
+++ b/applications/test/minMax1/Make/options
@@ -0,0 +1,7 @@
+EXE_INC = \
+ -I$(LIB_SRC)/finiteVolume/lnInclude \
+ -I$(LIB_SRC)/meshTools/lnInclude
+
+EXE_LIBS = \
+ -lfiniteVolume \
+ -lmeshTools
diff --git a/applications/test/minMax1/Test-minMax1.C b/applications/test/minMax1/Test-minMax1.C
new file mode 100644
index 0000000000..cb5f94725e
--- /dev/null
+++ b/applications/test/minMax1/Test-minMax1.C
@@ -0,0 +1,213 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | Copyright (C) 2019 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 .
+
+Description
+ Test minMax
+
+\*---------------------------------------------------------------------------*/
+
+#include "fvCFD.H"
+#include "Time.H"
+#include "BitOps.H"
+#include "HashOps.H"
+#include "ListOps.H"
+#include "scalarField.H"
+#include "MinMax.H"
+#include "dimensionedScalar.H"
+
+using namespace Foam;
+
+
+template
+Ostream& printInfo(const MinMax& range)
+{
+ Info<< range << " valid=" << range.valid();
+
+ return Info;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// Main program:
+
+int main(int argc, char *argv[])
+{
+ argList::noCheckProcessorDirectories();
+
+ #include "setRootCase.H"
+
+ Info<< "Test min/max " << nl;
+
+ Info<<"Construct null: ";
+ printInfo(MinMax()) << nl;
+
+ Info<<"Construct single value : ";
+ printInfo(MinMax(15)) << nl;
+
+ Info<<"Construct zero : ";
+ printInfo(MinMax(Zero)) << nl;
+
+ Info<<"Construct range : ";
+ printInfo(MinMax(1, 20)) << nl;
+
+
+ {
+ scalarMinMax range1(10, 20);
+ scalarMinMax range2(40, 50);
+ Info<< range1 << " + " << range2 << " = " << (range1 + range2) <("velrange", dimVelocity, {1, 20})
+ << nl;
+
+ dimensioned range1("a", dimVelocity, {10, 20});
+ dimensioned range2("b", dimVelocity, {40, 50});
+
+ Info<<"Dimensioned range : " << (range1 + range2) << endl;
+ }
+
+ Info<<"Centre value for (10 250) = "
+ << scalarMinMax(10, 250).centre() << nl;
+
+
+ {
+ Info<<"compare - similar definition as per std::string compare" << nl;
+
+ string str1("abc");
+ Info<< "For string=" << str1 << nl
+ << " compare(\"aaa\") = " << str1.compare("aaa") << nl
+ << " compare(\"abz\") = " << str1.compare("abz") << nl;
+
+
+ MinMax range(10, 20);
+
+ Info<< "For range=" << range << nl
+ << " compare(5) = " << range.compare(5) << nl
+ << " compare(15) = " << range.compare(15) << nl
+ << " compare(25) = " << range.compare(25) << nl;
+
+
+ Info<< "Binary comparisons" << nl;
+
+ Info<< "(5 < range) = " << (5 < range) << nl
+ << "(25 > range) = " << (25 >= range) << nl
+ << "(12 <= range) = " << (12 <= range) << nl
+ << "(12 >= range) = " << (12 >= range) << nl;
+ }
+
+
+ scalarField values1
+ (
+ List({3, 10, -11, 85, 300})
+ );
+
+ values1 *= (Pstream::myProcNo()+1);
+
+ Pout<<"min-max of " << flatOutput(values1) << " = "
+ << minMax(values1) << endl;
+
+ // Construct from values
+ MinMax minmax1(values1);
+ printInfo(minmax1) << nl;
+
+ // Reset and add values
+ minmax1.clear();
+
+ minmax1 += values1;
+ Pout<<"range: " << minmax1 << endl;
+
+ Info<< "Reduced: "<< returnReduce(minmax1, plusOp()) << nl;
+
+ {
+ MinMax limiter(10, 200);
+
+ Info<< nl
+ << "Test clipping limiter: " << limiter << nl
+ << "values : " << flatOutput(values1) << nl;
+
+ Info<< "Subset mask: "
+ << ListOps::create(values1, limiter)
+ << nl;
+
+ Info<< "Subset = " << subsetList(values1, limiter) << nl;
+
+ Info<< nl << "test clip() with limiter: " << limiter << nl;
+ for (const scalar& val : values1)
+ {
+ Info<< "clipped : " << val << " = " << clip(val, limiter) << nl;
+ }
+
+ Info<< nl << "inplace clip" << nl;
+
+ scalarField values2(values1);
+
+ Info<< "before: " << flatOutput(values2) << nl;
+
+ Info<< "before: " << flatOutput(values2) << nl;
+
+ for (scalar& val : values2)
+ {
+ clipEqOp()(val, limiter);
+ }
+
+ Info<< "after: " << flatOutput(values2) << nl;
+
+ Info<< nl << "For list: " << flatOutput(values1) << nl
+ << " minMax : " << minMax(values1) << nl
+ << " minMaxMag : " << minMaxMag(values1) << nl;
+ }
+
+
+ // Hashed values and reduction
+ {
+ HashTable hashed;
+
+ hashed.insert("zero", scalarMinMax(Zero));
+ hashed("null");
+ hashed("U") += values1;
+
+
+ Pout<< "hashed: " << hashed << nl;
+
+ Pstream::mapCombineGather
+ (
+ hashed,
+ plusEqOp()
+ );
+
+ Info<< "reduced: " << hashed << nl;
+
+
+ // Prune invalid
+ hashed.filterValues(emptyOp(), true);
+
+ Info<< "filtered: " << hashed << nl;
+ }
+
+ return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/ranges/MinMax/MinMax.H b/src/OpenFOAM/primitives/ranges/MinMax/MinMax.H
new file mode 100644
index 0000000000..4d71b4ae4d
--- /dev/null
+++ b/src/OpenFOAM/primitives/ranges/MinMax/MinMax.H
@@ -0,0 +1,412 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | Copyright (C) 2019 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 .
+
+Class
+ Foam::MinMax
+
+Description
+ A min/max value pair with additional methods.
+ In addition to conveniently storing values, it can be used for logic
+ operations or to modify data. A few global functions and functors are
+ also provided.
+
+ Examples of use.
+
+ Determine min/max limits from a List of values:
+ \verbatim
+ List values = ...;
+
+ // on construction
+ MinMax range(values);
+
+ range.clear();
+
+ range += val;
+
+ // global minMax() function
+ Info<< minMax(values) << nl;
+ \endverbatim
+
+ General comparsion operations
+ \verbatim
+ scalar val;
+ if (val < range) ... value is below range min
+ if (range.contains(val)) ... value within range
+ if (range.compare(val) > 0) ... value is above range max
+ if (range(val)) ... value within range - as predicate
+ \endverbatim
+
+ Since the range has a predicate form, it can be used as a filter method.
+ For example,
+ \verbatim
+ Info<< "values in range: " << subsetList(values, range) << nl;
+
+ boolList mask = ListOps::create(values, range);
+ Info<< "values values in range " << mask << nl;
+ \endverbatim
+
+ One particular advantage offered by MinMax is to clip or limit values
+ to a particular range. For example,
+ \verbatim
+ scalarMinMax range(lower, upper);
+
+ scalar val;
+ val = range.clip(val) .. return clip values
+
+ // vs.
+ val = min(max(value, lower, upper))
+ \endverbatim
+
+ Or when working on lists, the values can be limited in a single pass
+ of the data without intermediate memory allocation.
+ \verbatim
+ scalarField values = ...;
+
+ for (scalar& val : values)
+ {
+ range.inplaceClip(val);
+ }
+
+ // vs.
+ values = min(max(values, lower, upper))
+ \endverbatim
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef MinMax_H
+#define MinMax_H
+
+#include "scalar.H"
+#include "Pair.H"
+#include "Tuple2.H"
+#include "ListListOps.H"
+#include
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declarations
+template class MinMax;
+
+// Common min/max types
+typedef MinMax scalarMinMax;
+
+
+/*---------------------------------------------------------------------------*\
+ Class MinMax Declaration
+\*---------------------------------------------------------------------------*/
+
+template
+class MinMax
+:
+ public Tuple2
+{
+public:
+
+ // Typedefs
+
+ //- The value type the MinMax represents
+ typedef T value_type;
+
+ typedef typename pTraits::cmptType cmptType;
+
+
+ // Constructors
+
+ //- Construct inverted range
+ inline MinMax();
+
+ //- Copy construct from components
+ inline MinMax(const T& minVal, const T& maxVal);
+
+ //- Copy construct from components
+ inline MinMax(const std::pair& range);
+
+ //- Copy construct from components
+ inline MinMax(const Pair& range);
+
+ //- Construct with a single initial value
+ inline explicit MinMax(const T& val);
+
+ //- Construct from list
+ inline explicit MinMax(const UList& list);
+
+
+ // Member Functions
+
+ // Access
+
+ //- The min value (first)
+ inline const T& min() const;
+
+ //- The min value (first)
+ inline T& min();
+
+ //- The max value (second)
+ inline const T& max() const;
+
+ //- The max value (second)
+ inline T& max();
+
+ //- The min/max average value
+ inline T centre() const;
+
+
+ //- Range is empty if it is inverted
+ inline bool empty() const;
+
+ //- Range is valid if it is not inverted
+ inline bool valid() const;
+
+ //- Reset to an invalid, inverted range
+ inline void clear();
+
+
+ // Testing / Query
+
+ //- Intersect (union) with the second range.
+ // \return True if the resulting intersection is non-empty.
+ bool intersect(const MinMax& b);
+
+ //- Test if the ranges overlap
+ bool overlaps(const MinMax& b) const;
+
+ //- Compares the min/max range with the specified value.
+ // \return
+ // - 0: value is within the range, or range is invalid
+ // - -1: range (max) is less than the value
+ // - +1: range (min) is greater than value
+ inline int compare(const T& val) const;
+
+ //- True if the value is within the range
+ inline bool contains(const T& val) const;
+
+ //- If out of range, return the respective min/max limits, otherwise
+ //- return the value itself.
+ // If the range is invalid, always return the value.
+ inline const T& clip(const T& val) const;
+
+ //- Inplace clip value by the min/max limits
+ // \return True if clipping was applied.
+ inline bool inplaceClip(T& val) const;
+
+
+ // Manipulate
+
+ //- Extend the range to include the other min/max range
+ inline MinMax& add(const MinMax& other);
+
+ //- Include the value into the range
+ inline MinMax& add(const T& val);
+
+ //- Include the values into the range
+ inline MinMax& add(const UList& vals);
+
+
+ // Member Operators
+
+ //- Identical to contains(), for use as a predicate.
+ inline bool operator()(const T& val) const;
+
+ //- Extend min/max range to include other range
+ // Can be used in a reduction operation.
+ inline MinMax& operator+=(const MinMax& b);
+
+ //- Extend min/max range to include value
+ inline MinMax& operator+=(const T& val);
+
+ //- Extend min/max range to include all values
+ inline MinMax& operator+=(const UList& vals);
+
+ //- Multiply range by scalar factor
+ inline MinMax& operator*=(const scalar& s);
+
+ //- Divide range by scalar factor
+ inline MinMax& operator/=(const scalar& s);
+};
+
+
+// Global Functions
+
+//- Min/max range as a string
+template
+word name(const MinMax& range)
+{
+ return '(' + Foam::name(range.min()) + ',' + Foam::name(range.max()) + ')';
+}
+
+
+//- Return the value after clipping by the min/max limiter
+template
+T clip(const T& val, const MinMax& range)
+{
+ return range.clip(val);
+}
+
+
+
+//- Return the value after clipping by the min/max limiter
+template
+struct clipOp
+{
+ T operator()(T& val, const MinMax& range) const
+ {
+ return range.clip(val);
+ }
+};
+
+
+//- Clip value and assign inplace
+template
+struct clipEqOp
+{
+ bool operator()(T& val, const MinMax& range) const
+ {
+ return range.inplaceClip(val);
+ }
+};
+
+
+//- Extract the min/max range from a list of values
+template
+MinMax minMax(const UList& list)
+{
+ return MinMax(list);
+}
+
+
+//- Extract the min/max magnitudes from a list of values
+template
+MinMax minMaxMag(const UList& list)
+{
+ MinMax result;
+ for (const T& val : list)
+ {
+ result += Foam::mag(val);
+ }
+
+ return result;
+}
+
+
+// * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
+
+//- Combine two ranges
+template
+inline MinMax operator+(const MinMax& x, const MinMax& y)
+{
+ return MinMax(x).add(y);
+}
+
+
+//- Multiply range by scalar factor
+template
+inline MinMax operator*(const MinMax& x, const scalar& s)
+{
+ return MinMax(x.min()*s, x.max()*s);
+}
+
+
+//- Divide range by scalar factor
+template
+inline MinMax operator/(const MinMax& x, const scalar& s)
+{
+ return MinMax(x.min()/s, x.max()/s);
+}
+
+
+// Comparison
+
+template
+inline typename std::enable_if::value, bool>::type
+operator<(const MinMax& range, const U& val)
+{
+ return (range.compare(val) < 0);
+}
+
+template
+inline typename std::enable_if::value, bool>::type
+operator<=(const MinMax& range, const U& val)
+{
+ return (range.compare(val) <= 0);
+}
+
+template
+inline typename std::enable_if::value, bool>::type
+operator>(const MinMax& range, const U& val)
+{
+ return (range.compare(val) > 0);
+}
+
+template
+inline typename std::enable_if::value, bool>::type
+operator>=(const MinMax& range, const U& val)
+{
+ return (range.compare(val) >= 0);
+}
+
+
+template
+inline typename std::enable_if::value, bool>::type
+operator<(const U& val, const MinMax& range)
+{
+ return (range.compare(val) > 0);
+}
+
+template
+inline typename std::enable_if::value, bool>::type
+operator<=(const U& val, const MinMax& range)
+{
+ return (range.compare(val) >= 0);
+}
+
+template
+inline typename std::enable_if::value, bool>::type
+operator>(const U& val, const MinMax& range)
+{
+ return (range.compare(val) < 0);
+}
+
+template
+inline typename std::enable_if::value, bool>::type
+operator>=(const U& val, const MinMax& range)
+{
+ return (range.compare(val) <= 0);
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "MinMaxI.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/ranges/MinMax/MinMaxI.H b/src/OpenFOAM/primitives/ranges/MinMax/MinMaxI.H
new file mode 100644
index 0000000000..2e06a8d260
--- /dev/null
+++ b/src/OpenFOAM/primitives/ranges/MinMax/MinMaxI.H
@@ -0,0 +1,330 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | Copyright (C) 2019 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 .
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+template
+inline Foam::MinMax::MinMax()
+:
+ Tuple2(pTraits::max, pTraits::min)
+{}
+
+
+template
+inline Foam::MinMax::MinMax(const T& minVal, const T& maxVal)
+:
+ Tuple2(minVal, maxVal)
+{}
+
+
+template
+inline Foam::MinMax::MinMax(const std::pair& range)
+:
+ Tuple2(range.first, range.second)
+{}
+
+
+template
+inline Foam::MinMax::MinMax(const Pair& range)
+:
+ Tuple2(range.first(), range.second())
+{}
+
+
+template
+inline Foam::MinMax::MinMax(const T& val)
+:
+ Tuple2(val, val)
+{}
+
+
+template
+inline Foam::MinMax::MinMax(const UList& list)
+:
+ MinMax()
+{
+ add(list);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
+
+template
+inline const T& Foam::MinMax::min() const
+{
+ return this->first();
+}
+
+
+template
+inline T& Foam::MinMax::min()
+{
+ return this->first();
+}
+
+
+template
+inline const T& Foam::MinMax::max() const
+{
+ return this->second();
+}
+
+
+template
+inline T& Foam::MinMax::max()
+{
+ return this->second();
+}
+
+
+template
+inline T Foam::MinMax::centre() const
+{
+ // Multiply before adding to avoid possible overflow
+ return (0.5 * min()) + (0.5 * max());
+}
+
+
+template
+inline bool Foam::MinMax::empty() const
+{
+ return (max() < min());
+}
+
+
+template
+inline bool Foam::MinMax::valid() const
+{
+ return !(max() < min());
+}
+
+
+template
+inline void Foam::MinMax::clear()
+{
+ min() = pTraits::max;
+ max() = pTraits::min;
+}
+
+
+template
+inline bool Foam::MinMax::intersect(const MinMax& b)
+{
+ min() = ::Foam::max(min(), b.min());
+ max() = ::Foam::min(max(), b.max());
+
+ return valid();
+}
+
+
+template
+inline bool Foam::MinMax::overlaps(const MinMax& b) const
+{
+ const MinMax& a = *this;
+ return (a.min() <= b.max() && b.min() <= a.max());
+}
+
+
+template
+inline int Foam::MinMax::compare(const T& val) const
+{
+ if (valid())
+ {
+ if (max() < val)
+ {
+ return -1;
+ }
+ else if (val < min())
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+template
+inline bool Foam::MinMax::contains(const T& val) const
+{
+ return (valid() && !compare(val));
+}
+
+
+template
+inline const T& Foam::MinMax::clip(const T& val) const
+{
+ if (valid())
+ {
+ if (val < min())
+ {
+ return min();
+ }
+ else if (max() < val)
+ {
+ return max();
+ }
+ }
+
+ return val; // Pass-through
+}
+
+
+template
+inline bool Foam::MinMax::inplaceClip(T& val) const
+{
+ if (valid())
+ {
+ if (val < min())
+ {
+ val = min();
+ return true;
+ }
+ else if (max() < val)
+ {
+ val = max();
+ return true;
+ }
+ }
+
+ return false; // No change
+}
+
+
+template
+inline Foam::MinMax& Foam::MinMax::add
+(
+ const MinMax& other
+)
+{
+ min() = Foam::min(min(), other.min());
+ max() = Foam::max(max(), other.max());
+ return *this;
+}
+
+
+template
+inline Foam::MinMax& Foam::MinMax::add(const T& val)
+{
+ min() = Foam::min(min(), val);
+ max() = Foam::max(max(), val);
+ return *this;
+}
+
+
+template
+inline Foam::MinMax& Foam::MinMax::add(const UList& vals)
+{
+ for (const T& val : vals)
+ {
+ add(val);
+ }
+ return *this;
+}
+
+
+// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
+
+template
+inline bool Foam::MinMax::operator()(const T& val) const
+{
+ return contains(val);
+}
+
+
+// Perhaps not entirely useful
+//
+// template
+// inline Foam::MinMax& Foam::MinMax::operator-=
+// (
+// const MinMax& b
+// )
+// {
+// MinMax& a = *this;
+//
+// // Remove overlapping portion, but cannot create a 'hole' in the middle
+//
+// if (!a.valid() || !b.valid())
+// {
+// // Invalid range(s): no-op
+// }
+// else if (a.min() < b.max() && b.min() <= a.min())
+// {
+// // Overlap on the left
+// min() = ::Foam::max(a.min(), b.max());
+// }
+// else if (b.min() < a.max() && a.max() <= b.max())
+// {
+// // Overlap on the right
+// max() = ::Foam::min(a.max(), b.min());
+// }
+//
+// return *this;
+// }
+
+
+template
+inline Foam::MinMax& Foam::MinMax::operator+=
+(
+ const MinMax& b
+)
+{
+ return add(b);
+}
+
+
+template
+inline Foam::MinMax& Foam::MinMax::operator+=(const T& val)
+{
+ return add(val);
+}
+
+
+template
+inline Foam::MinMax& Foam::MinMax::operator+=(const UList& vals)
+{
+ return add(vals);
+}
+
+
+template
+inline Foam::MinMax& Foam::MinMax::operator*=(const scalar& s)
+{
+ min() *= s;
+ max() *= s;
+ return *this;
+}
+
+
+template
+inline Foam::MinMax& Foam::MinMax::operator/=(const scalar& s)
+{
+ min() /= s;
+ max() /= s;
+ return *this;
+}
+
+
+// ************************************************************************* //