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; +} + + +// ************************************************************************* //