Commit 8a391b79 authored by Mark Olesen's avatar Mark Olesen
Browse files

ENH: MinMax class for handling value ranges

parent a174f13f
Test-minMax1.C
EXE = $(FOAM_USER_APPBIN)/Test-minMax1
EXE_INC = \
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude
EXE_LIBS = \
-lfiniteVolume \
-lmeshTools
/*---------------------------------------------------------------------------*\
========= |
\\ / 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 <http://www.gnu.org/licenses/>.
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<class T>
Ostream& printInfo(const MinMax<T>& 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<scalar>()) << nl;
Info<<"Construct single value : ";
printInfo(MinMax<scalar>(15)) << nl;
Info<<"Construct zero : ";
printInfo(MinMax<scalar>(Zero)) << nl;
Info<<"Construct range : ";
printInfo(MinMax<scalar>(1, 20)) << nl;
{
scalarMinMax range1(10, 20);
scalarMinMax range2(40, 50);
Info<< range1 << " + " << range2 << " = " << (range1 + range2) <<nl;
}
{
Info<<"Dimensioned range : "
<< dimensioned<scalarMinMax>("velrange", dimVelocity, {1, 20})
<< nl;
dimensioned<scalarMinMax> range1("a", dimVelocity, {10, 20});
dimensioned<scalarMinMax> 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<scalar> 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<scalar>({3, 10, -11, 85, 300})
);
values1 *= (Pstream::myProcNo()+1);
Pout<<"min-max of " << flatOutput(values1) << " = "
<< minMax(values1) << endl;
// Construct from values
MinMax<scalar> minmax1(values1);
printInfo(minmax1) << nl;
// Reset and add values
minmax1.clear();
minmax1 += values1;
Pout<<"range: " << minmax1 << endl;
Info<< "Reduced: "<< returnReduce(minmax1, plusOp<scalarMinMax>()) << nl;
{
MinMax<scalar> limiter(10, 200);
Info<< nl
<< "Test clipping limiter: " << limiter << nl
<< "values : " << flatOutput(values1) << nl;
Info<< "Subset mask: "
<< ListOps::create<bool, scalar>(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<scalar>()(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<scalarMinMax> hashed;
hashed.insert("zero", scalarMinMax(Zero));
hashed("null");
hashed("U") += values1;
Pout<< "hashed: " << hashed << nl;
Pstream::mapCombineGather
(
hashed,
plusEqOp<scalarMinMax>()
);
Info<< "reduced: " << hashed << nl;
// Prune invalid
hashed.filterValues(emptyOp<scalarMinMax>(), true);
Info<< "filtered: " << hashed << nl;
}
return 0;
}
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / 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 <http://www.gnu.org/licenses/>.
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<scalar> values = ...;
// on construction
MinMax<scalar> 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<bool>(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 <type_traits>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declarations
template<class T> class MinMax;
// Common min/max types
typedef MinMax<scalar> scalarMinMax;
/*---------------------------------------------------------------------------*\
Class MinMax Declaration
\*---------------------------------------------------------------------------*/
template<class T>
class MinMax
:
public Tuple2<T,T>
{
public:
// Typedefs
//- The value type the MinMax represents
typedef T value_type;
typedef typename pTraits<T>::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<T,T>& range);
//- Copy construct from components
inline MinMax(const Pair<T>& range);
//- Construct with a single initial value
inline explicit MinMax(const T& val);
//- Construct from list
inline explicit MinMax(const UList<T>& 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<T>& b);
//- Test if the ranges overlap
bool overlaps(const MinMax<T>& 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<T>& add(const MinMax& other);
//- Include the value into the range
inline MinMax<T>& add(const T& val);
//- Include the values into the range
inline MinMax<T>& add(const UList<T>& 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<T>& operator+=(const MinMax<T>& b);
//- Extend min/max range to include value
inline MinMax<T>& operator+=(const T& val);
//- Extend min/max range to include all values
inline MinMax<T>& operator+=(const UList<T>& vals);
//- Multiply range by scalar factor
inline MinMax<T>& operator*=(const scalar& s);
//- Divide range by scalar factor
inline MinMax<T>& operator/=(const scalar& s);
};
// Global Functions
//- Min/max range as a string
template<class T>
word name(const MinMax<T>& range)
{
return '(' + Foam::name(range.min()) + ',' + Foam::name(range.max()) + ')';
}
//- Return the value after clipping by the min/max limiter
template<class T>
T clip(const T& val, const MinMax<T>& range)
{
return range.clip(val);
}
//- Return the value after clipping by the min/max limiter
template<class T>
struct clipOp
{
T operator()(T& val, const MinMax<T>& range) const
{
return range.clip(val);
}
};
//- Clip value and assign inplace
template<class T>
struct clipEqOp
{
bool operator()(T& val, const MinMax<T>& range) const
{
return range.inplaceClip(val);
}
};
//- Extract the min/max range from a list of values
template<class T>
MinMax<T> minMax(const UList<T>& list)
{
return MinMax<T>(list);
}
//- Extract the min/max magnitudes from a list of values
template<class T>
MinMax<scalar> minMaxMag(const UList<T>& list)
{
MinMax<scalar> result;
for (const T& val : list)
{
result += Foam::mag(val);
}
return result;
}
// * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
//- Combine two ranges
template<class T>
inline MinMax<T> operator+(const MinMax<T>& x, const MinMax<T>& y)
{
return MinMax<T>(x).add(y);
}
//- Multiply range by scalar factor
template<class T>
inline MinMax<T> operator*(const MinMax<T>& x, const scalar& s)
{
return MinMax<T>(x.min()*s, x.max()*s);
}
//- Divide range by scalar factor
template<class T>
inline MinMax<T> operator/(const MinMax<T>& x, const scalar& s)
{
return MinMax<T>(x.min()/s, x.max()/s);
}
// Comparison
template<class T, class U>
inline typename std::enable_if<std::is_convertible<U, T>::value, bool>::type
operator<(const MinMax<T>& range, const U& val)
{
return (range.compare(val) < 0);
}
template<class T, class U>
inline typename std::enable_if<std::is_convertible<U, T>::value, bool>::type
operator<=(const MinMax<T>& range, const U& val)
{
return (range.compare(val) <= 0);
}
template<class T, class U>
inline typename std::enable_if<std::is_convertible<U, T>::value, bool>::type
operator>(const MinMax<T>& range, const U& val)
{
return (range.compare(val) > 0);
}
template<class T, class U>
inline typename std::enable_if<std::is_convertible<U, T>::value, bool>::type
operator>=(const MinMax<T>& range, const U& val)
{
return (range.compare(val) >= 0);
}
template<class T, class U>
inline typename std::enable_if<std::is_convertible<U, T>::value, bool>::type
operator<(const U& val, const MinMax<T>& range)
{
return (range.compare(val) > 0);
}
template<class T, class U>
inline typename std::enable_if<std::is_convertible<U, T>::value, bool>::type
operator<=(const U& val, const MinMax<T>& range)
{
return (range.compare(val) >= 0);
}
template<class T, class U>
inline typename std::enable_if<std::is_convertible<U, T>::value, bool>::type
operator>(const U& val, const MinMax<T>& range)
{