ENH: improved handling for clamping

- proper component-wise clamping for MinMax clamp().

- construct clampOp from components

- propagate clamp() method from GeometricField to FieldField and Field

- clamp_min() and clamp_max() for one-sided clamping,
  as explicit alternative to min/max free functions which can
  be less intuitive and often involve additional field copies.

- top-level checks to skip applying invalid min/max ranges
  and bypass the internal checks of MinMax::clamp() etc.
This commit is contained in:
Mark Olesen 2023-01-13 20:45:53 +01:00
parent 3888bfa17f
commit ba153df8db
25 changed files with 501 additions and 164 deletions

View File

@ -110,8 +110,7 @@ unsigned checkDimensions
try try
{ {
// min(a, b); min(a, b);
clip(a, b);
dimsOk = true; dimsOk = true;
} }
catch (const Foam::error& err) catch (const Foam::error& err)

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2019-2022 OpenCFD Ltd. Copyright (C) 2019-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -83,6 +83,9 @@ int main(int argc, char *argv[])
Info<<"Construct zero : "; Info<<"Construct zero : ";
printInfo(MinMax<scalar>(Zero)) << nl; printInfo(MinMax<scalar>(Zero)) << nl;
Info<<"Construct zero_one : ";
printInfo(MinMax<scalar>(zero_one{})) << nl;
Info<<"Construct range : "; Info<<"Construct range : ";
printInfo(MinMax<scalar>(1, 20)) << nl; printInfo(MinMax<scalar>(1, 20)) << nl;
@ -92,6 +95,26 @@ int main(int argc, char *argv[])
Info<<"A 0-1 vector range : "; Info<<"A 0-1 vector range : ";
printInfo(MinMax<vector>::zero_one()) << nl; printInfo(MinMax<vector>::zero_one()) << nl;
{
vector a(0, 1, 20);
vector b(2, 1, 0);
vector c(4, 10, 12);
Info<< "vectors:"
<< " a = " << a
<< " b = " << b
<< " c = " << c << nl;
Info<< "min max = " << min(max(a, b), c) << nl;
Info<< "range clamp= " << MinMax<vector>(b, c).clamp(a) << nl;
Info<< "clamp = " << clamp(a, b, c) << nl;
Info<< "clamp 0/1 = " << clamp(a, vector::zero, vector::one) << nl;
}
// Scalar promotion
Info<< "clamp (scalar) = " << clamp(15.0, -1, 1) << nl;
{ {
scalarMinMax range1(10, 20); scalarMinMax range1(10, 20);
@ -180,6 +203,9 @@ int main(int argc, char *argv[])
{ {
MinMax<scalar> limiter(10, 200); MinMax<scalar> limiter(10, 200);
clampOp<scalar> clipper(limiter);
// clampOp<scalar> clipper(zero_one{});
// clampOp<scalar> clipper(10, 200);
Info<< nl Info<< nl
<< "Test clipping limiter: " << limiter << nl << "Test clipping limiter: " << limiter << nl
@ -196,7 +222,10 @@ int main(int argc, char *argv[])
Info<< nl << "test clip() with limiter: " << limiter << nl; Info<< nl << "test clip() with limiter: " << limiter << nl;
for (const scalar& val : values1) for (const scalar& val : values1)
{ {
Info<< "clipped : " << val << " = " << clip(val, limiter) << nl; Info<< "clipped : " << val << " = "
<< clip(val, limiter)
<< " or " << clip(val, limiter)
<< " or " << clipper(val) << nl;
} }
Info<< nl << "test clip(Field) with limiter: " << limiter << nl; Info<< nl << "test clip(Field) with limiter: " << limiter << nl;
@ -208,9 +237,15 @@ int main(int argc, char *argv[])
Info<< "before " << flatOutput(values2) << nl; Info<< "before " << flatOutput(values2) << nl;
// Too much clutter
// for (scalar& val : values2)
// {
// clampEqOp<scalar>{limiter}(val);
// }
for (scalar& val : values2) for (scalar& val : values2)
{ {
clipEqOp<scalar>()(val, limiter); val = clipper(val);
} }
Info<< "after: " << flatOutput(values2) << nl; Info<< "after: " << flatOutput(values2) << nl;

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2019 OpenCFD Ltd. Copyright (C) 2019-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -24,7 +24,7 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Description Description
Test minMax Test-minMax2
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
@ -37,6 +37,7 @@ Description
#include "MinMax.H" #include "MinMax.H"
#include "dimensionedScalar.H" #include "dimensionedScalar.H"
#include "dimensionedMinMax.H" #include "dimensionedMinMax.H"
#include "Random.H"
using namespace Foam; using namespace Foam;
@ -78,7 +79,6 @@ int main(int argc, char *argv[])
Info<< "Test min/max " << nl; Info<< "Test min/max " << nl;
{ {
scalarMinMax range1(10, 20); scalarMinMax range1(10, 20);
scalarMinMax range2(40, 50); scalarMinMax range2(40, 50);
@ -140,7 +140,34 @@ int main(int argc, char *argv[])
} }
} }
{
scalarField someField(25);
Random rnd(4567);
for (scalar& val : someField)
{
val = rnd.position(-0.2, 1.2);
}
Info<< nl
<< "field: " << flatOutput(someField) << nl;
Info<< "clamp01: "
<< flatOutput(clamp(someField, scalarMinMax(zero_one{}))()) << nl;
Info<< "clamp01: "
<< clamp(tmp<scalarField>(someField), scalarMinMax(zero_one{}))<< nl;
scalarField result(10);
clamp(result, someField, scalarMinMax(zero_one{}));
Info<< "result: " << result << nl;
someField.clamp(zero_one{});
Info<< "inplace: " << someField << nl;
}
Info<< nl << "\nDone\n" << endl;
return 0; return 0;
} }

View File

@ -1279,7 +1279,7 @@ void write_scalarField
continue; continue;
} }
os << limits.clip(fld(cellIdx)) << nl; os << limits.clamp(fld(cellIdx)) << nl;
} }
os << token::END_LIST << token::END_STATEMENT << nl; os << token::END_LIST << token::END_STATEMENT << nl;

View File

@ -58,12 +58,11 @@ namespace Foam
{ {
// Read porosity, change to blockage. Clamp values [0-1] silently // Read porosity, change to blockage. Clamp values [0-1] silently
static const scalarMinMax limits01(scalarMinMax::zero_one());
// Volume porosity -> blockage // Volume porosity -> blockage
inline scalar getPorosity(const dictionary& dict) inline scalar getPorosity(const dictionary& dict)
{ {
return 1 - limits01.clip(dict.getOrDefault<scalar>("porosity", 0)); return 1 - clamp(dict.getOrDefault<scalar>("porosity", 0), 0, 1);
} }
// Direction porosities -> blockage // Direction porosities -> blockage
@ -75,7 +74,7 @@ inline vector getPorosities(const dictionary& dict)
{ {
for (scalar& val : blockage) for (scalar& val : blockage)
{ {
val = 1 - limits01.clip(val); val = 1 - clamp(val, 0, 1);
} }
} }

View File

@ -253,36 +253,38 @@ bool Foam::dimensionSet::operator/=(const dimensionSet& ds)
// * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * * //
Foam::dimensionSet Foam::min(const dimensionSet& ds1, const dimensionSet& ds2) Foam::dimensionSet Foam::min(const dimensionSet& a, const dimensionSet& b)
{ {
if (dimensionSet::checking()) if (dimensionSet::checking())
{ {
checkDims("min(a, b)", ds1, ds2); checkDims("min(a, b)", a, b);
} }
return ds1; return a;
} }
Foam::dimensionSet Foam::max(const dimensionSet& ds1, const dimensionSet& ds2) Foam::dimensionSet Foam::max(const dimensionSet& a, const dimensionSet& b)
{ {
if (dimensionSet::checking()) if (dimensionSet::checking())
{ {
checkDims("max(a, b)", ds1, ds2); checkDims("max(a, b)", a, b);
} }
return ds1; return a;
} }
Foam::dimensionSet Foam::clip(const dimensionSet& ds1, const dimensionSet& ds2) Foam::dimensionSet Foam::clamp(const dimensionSet& a, const dimensionSet& range)
{ {
if (dimensionSet::checking()) // In may cases the min/max range will be created from raw values
// (no dimension) so accept those without error
if (dimensionSet::checking() && !range.dimensionless())
{ {
checkDims("clip(a, b)", ds1, ds2); checkDims("clamp(a, b)", a, range);
} }
return ds1; return a;
} }

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2017-2022 OpenCFD Ltd. Copyright (C) 2017-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -354,9 +354,9 @@ Ostream& operator<<(Ostream& os, const dimensionSet& ds);
// Global Functions // Global Functions
dimensionSet min(const dimensionSet& ds1, const dimensionSet& ds2); dimensionSet min(const dimensionSet& a, const dimensionSet& b);
dimensionSet max(const dimensionSet& ds1, const dimensionSet& ds2); dimensionSet max(const dimensionSet& a, const dimensionSet& b);
dimensionSet clip(const dimensionSet& ds1, const dimensionSet& ds2); dimensionSet clamp(const dimensionSet& a, const dimensionSet& range);
dimensionSet cmptMultiply(const dimensionSet& ds1, const dimensionSet& ds2); dimensionSet cmptMultiply(const dimensionSet& ds1, const dimensionSet& ds2);
dimensionSet cmptDivide(const dimensionSet& ds1, const dimensionSet& ds2); dimensionSet cmptDivide(const dimensionSet& ds1, const dimensionSet& ds2);

View File

@ -421,6 +421,8 @@ public:
const tmp<DimensionedField<cmptType, GeoMesh>>& tdf const tmp<DimensionedField<cmptType, GeoMesh>>& tdf
); );
// Inherits clamp, clamp_min, clamp_max (without dimensions) from Field
//- Return the field transpose (only defined for second rank tensors) //- Return the field transpose (only defined for second rank tensors)
tmp<DimensionedField<Type, GeoMesh>> T() const; tmp<DimensionedField<Type, GeoMesh>> T() const;

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2019-2022 OpenCFD Ltd. Copyright (C) 2019-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -282,6 +282,65 @@ void FieldField<Field, Type>::replace
} }
template<template<class> class Field, class Type>
void FieldField<Field, Type>::clamp
(
const Type& lower,
const Type& upper
)
{
if (lower < upper)
{
for (auto& ff : *this)
{
ff.clamp(lower, upper);
}
}
}
template<template<class> class Field, class Type>
void FieldField<Field, Type>::clamp
(
const MinMax<Type>& range
)
{
if (range.min() < range.max())
{
for (auto& ff : *this)
{
ff.clamp(range.min(), range.max());
}
}
}
template<template<class> class Field, class Type>
void FieldField<Field, Type>::clamp_min
(
const Type& lower
)
{
for (auto& ff : *this)
{
ff.clamp_min(lower);
}
}
template<template<class> class Field, class Type>
void FieldField<Field, Type>::clamp_max
(
const Type& upper
)
{
for (auto& ff : *this)
{
ff.clamp_max(upper);
}
}
template<template<class> class Field, class Type> template<template<class> class Field, class Type>
tmp<FieldField<Field, Type>> FieldField<Field, Type>::T() const tmp<FieldField<Field, Type>> FieldField<Field, Type>::T() const
{ {

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2022 OpenCFD Ltd. Copyright (C) 2022-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -149,6 +149,20 @@ public:
//- Replace a component field of the field //- Replace a component field of the field
void replace(const direction, const cmptType&); void replace(const direction, const cmptType&);
//- Clamp field values (in-place) to the specified range.
// A no-op for an invalid range.
void clamp(const Type& lower, const Type& upper);
//- Clamp field values (in-place) to the specified range.
// A no-op for an invalid range.
void clamp(const MinMax<Type>& range);
//- Impose lower (floor) clamp on the field values (in-place)
void clamp_min(const Type& lower);
//- Impose upper (ceiling) clamp on the field values (in-place)
void clamp_max(const Type& upper);
//- Return the field transpose (only defined for second rank tensors) //- Return the field transpose (only defined for second rank tensors)
tmp<FieldField<Field, Type>> T() const; tmp<FieldField<Field, Type>> T() const;

View File

@ -673,7 +673,7 @@ BINARY_TYPE_FUNCTION(Type, Type, Type, min)
BINARY_TYPE_FUNCTION(Type, Type, Type, cmptMultiply) BINARY_TYPE_FUNCTION(Type, Type, Type, cmptMultiply)
BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide) BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide)
BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax<Type>, clip) BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax<Type>, clamp)
/* * * * * * * * * * * * * * * * Global operators * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * Global operators * * * * * * * * * * * * * */

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2019 OpenCFD Ltd. Copyright (C) 2019-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -287,7 +287,7 @@ BINARY_TYPE_FUNCTION(Type, Type, Type, min)
BINARY_TYPE_FUNCTION(Type, Type, Type, cmptMultiply) BINARY_TYPE_FUNCTION(Type, Type, Type, cmptMultiply)
BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide) BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide)
BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax<Type>, clip) BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax<Type>, clamp)
/* * * * * * * * * * * * * * * * Global operators * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * Global operators * * * * * * * * * * * * * */

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2022 OpenCFD Ltd. Copyright (C) 2015-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -625,6 +625,50 @@ void Foam::Field<Type>::replace
} }
template<class Type>
void Foam::Field<Type>::clamp(const Type& lower, const Type& upper)
{
// Use free functions min(), max() to impose component-wise clamping
if (lower < upper)
{
// std::for_each
for (auto& val : *this)
{
val = min(max(val, lower), upper);
}
}
}
template<class Type>
void Foam::Field<Type>::clamp(const MinMax<Type>& range)
{
clamp(range.min(), range.max());
}
template<class Type>
void Foam::Field<Type>::clamp_min(const Type& lower)
{
// Use free function max() [sic] to impose component-wise clamp_min
// std::for_each
for (auto& val : *this)
{
val = max(val, lower);
}
}
template<class Type>
void Foam::Field<Type>::clamp_max(const Type& upper)
{
// Use free function min() [sic] to impose component-wise clamp_max
// std::for_each
for (auto& val : *this)
{
val = min(val, upper);
}
}
template<class Type> template<class Type>
template<class VSForm> template<class VSForm>
VSForm Foam::Field<Type>::block(const label start) const VSForm Foam::Field<Type>::block(const label start) const

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2022 OpenCFD Ltd. Copyright (C) 2015-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -415,6 +415,20 @@ public:
//- Replace a component field of the field //- Replace a component field of the field
void replace(const direction, const cmptType&); void replace(const direction, const cmptType&);
//- Clamp field values (in-place) to the specified range.
// A no-op for an invalid range.
void clamp(const Type& lower, const Type& upper);
//- Clamp field values (in-place) to the specified range.
// A no-op for an invalid range.
void clamp(const MinMax<Type>& range);
//- Impose lower (floor) clamp on the field values (in-place)
void clamp_min(const Type& lower);
//- Impose upper (ceiling) clamp on the field values (in-place)
void clamp_max(const Type& upper);
template<class VSForm> template<class VSForm>
VSForm block(const label start) const; VSForm block(const label start) const;

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2019 OpenCFD Ltd. Copyright (C) 2019-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -656,6 +656,64 @@ TMP_UNARY_FUNCTION(Type, gAverage)
#undef TMP_UNARY_FUNCTION #undef TMP_UNARY_FUNCTION
template<class Type>
void clamp
(
Field<Type>& result,
const UList<Type>& f1,
const MinMax<Type>& range
)
{
if (result.cdata() == f1.cdata())
{
// Apply in-place
result.clamp(range);
}
else
{
if (range.good())
{
std::transform
(
f1.cbegin(),
f1.cbegin(result.size()),
result.begin(),
clampOp<Type>(range)
);
}
else
{
// No clamping
std::copy(f1.cbegin(), f1.cbegin(result.size()), result.begin());
}
}
}
template<class Type>
tmp<Field<Type>> clamp
(
const UList<Type>& f1,
const MinMax<Type>& range
)
{
auto tres = tmp<Field<Type>>::New(f1.size());
clamp(tres.ref(), f1, range);
return tres;
}
template<class Type>
tmp<Field<Type>> clamp
(
const tmp<Field<Type>>& tf1,
const MinMax<Type>& range
)
{
auto tres = reuseTmp<Type, Type>::New(tf1);
clamp(tres.ref(), tf1(), range);
tf1.clear();
return tres;
}
BINARY_FUNCTION(Type, Type, Type, max) BINARY_FUNCTION(Type, Type, Type, max)
BINARY_FUNCTION(Type, Type, Type, min) BINARY_FUNCTION(Type, Type, Type, min)
@ -667,10 +725,10 @@ BINARY_TYPE_FUNCTION(Type, Type, Type, min)
BINARY_TYPE_FUNCTION(Type, Type, Type, cmptMultiply) BINARY_TYPE_FUNCTION(Type, Type, Type, cmptMultiply)
BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide) BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide)
BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax<Type>, clip) BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax<Type>, clip) // Same as clamp
/* * * * * * * * * * * * * * * * Global operators * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * */
UNARY_OPERATOR(Type, Type, -, negate) UNARY_OPERATOR(Type, Type, -, negate)

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2019 OpenCFD Ltd. Copyright (C) 2019-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -313,10 +313,11 @@ BINARY_TYPE_FUNCTION(Type, Type, Type, min)
BINARY_TYPE_FUNCTION(Type, Type, Type, cmptMultiply) BINARY_TYPE_FUNCTION(Type, Type, Type, cmptMultiply)
BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide) BINARY_TYPE_FUNCTION(Type, Type, Type, cmptDivide)
BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax<Type>, clip) BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax<Type>, clamp)
BINARY_TYPE_FUNCTION_FS(Type, Type, MinMax<Type>, clip) // Same as clamp
// * * * * * * * * * * * * * * * Global operators * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
UNARY_OPERATOR(Type, Type, -, negate) UNARY_OPERATOR(Type, Type, -, negate)

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2015-2022 OpenCFD Ltd. Copyright (C) 2015-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -1250,60 +1250,88 @@ void Foam::GeometricField<Type, PatchField, GeoMesh>::replace
template<class Type, template<class> class PatchField, class GeoMesh> template<class Type, template<class> class PatchField, class GeoMesh>
void Foam::GeometricField<Type, PatchField, GeoMesh>::min void Foam::GeometricField<Type, PatchField, GeoMesh>::clamp
( (
const dimensioned<Type>& dt const Type& lower,
const Type& upper
) )
{ {
Foam::min(primitiveFieldRef(), primitiveField(), dt.value()); primitiveFieldRef().clamp(lower, upper);
Foam::min(boundaryFieldRef(), boundaryField(), dt.value()); boundaryFieldRef().clamp(lower, upper);
} }
template<class Type, template<class> class PatchField, class GeoMesh> template<class Type, template<class> class PatchField, class GeoMesh>
void Foam::GeometricField<Type, PatchField, GeoMesh>::max void Foam::GeometricField<Type, PatchField, GeoMesh>::clamp
( (
const dimensioned<Type>& dt const MinMax<Type>& range
) )
{ {
Foam::max(primitiveFieldRef(), primitiveField(), dt.value()); primitiveFieldRef().clamp(range.min(), range.max());
Foam::max(boundaryFieldRef(), boundaryField(), dt.value()); boundaryFieldRef().clamp(range.min(), range.max());
} }
template<class Type, template<class> class PatchField, class GeoMesh> template<class Type, template<class> class PatchField, class GeoMesh>
void Foam::GeometricField<Type, PatchField, GeoMesh>::clip void Foam::GeometricField<Type, PatchField, GeoMesh>::clamp
(
const dimensioned<Type>& lower,
const dimensioned<Type>& upper
)
{
this->clamp(lower.value(), upper.value());
}
template<class Type, template<class> class PatchField, class GeoMesh>
void Foam::GeometricField<Type, PatchField, GeoMesh>::clamp
( (
const dimensioned<MinMax<Type>>& range const dimensioned<MinMax<Type>>& range
) )
{ {
Foam::clip(primitiveFieldRef(), primitiveField(), range.value()); this->clamp(range.value());
Foam::clip(boundaryFieldRef(), boundaryField(), range.value());
} }
template<class Type, template<class> class PatchField, class GeoMesh> template<class Type, template<class> class PatchField, class GeoMesh>
void Foam::GeometricField<Type, PatchField, GeoMesh>::clip void Foam::GeometricField<Type, PatchField, GeoMesh>::clamp_min
( (
const dimensioned<Type>& minVal, const Type& lower
const dimensioned<Type>& maxVal
) )
{ {
MinMax<Type> range(minVal.value(), maxVal.value()); primitiveFieldRef().clamp_min(lower);
boundaryFieldRef().clamp_min(lower);
Foam::clip(primitiveFieldRef(), primitiveField(), range);
Foam::clip(boundaryFieldRef(), boundaryField(), range);
} }
template<class Type, template<class> class PatchField, class GeoMesh> template<class Type, template<class> class PatchField, class GeoMesh>
void Foam::GeometricField<Type, PatchField, GeoMesh>::maxMin void Foam::GeometricField<Type, PatchField, GeoMesh>::clamp_max
( (
const dimensioned<Type>& minVal, const Type& upper
const dimensioned<Type>& maxVal
) )
{ {
this->clip(minVal, maxVal); primitiveFieldRef().clamp_max(upper);
boundaryFieldRef().clamp_max(upper);
}
template<class Type, template<class> class PatchField, class GeoMesh>
void Foam::GeometricField<Type, PatchField, GeoMesh>::clamp_min
(
const dimensioned<Type>& lower
)
{
this->clamp_min(lower.value());
}
template<class Type, template<class> class PatchField, class GeoMesh>
void Foam::GeometricField<Type, PatchField, GeoMesh>::clamp_max
(
const dimensioned<Type>& upper
)
{
this->clamp_max(upper.value());
} }

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2015-2022 OpenCFD Ltd. Copyright (C) 2015-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -134,7 +134,6 @@ private:
//- Read the field - create the field dictionary on-the-fly //- Read the field - create the field dictionary on-the-fly
void readFields(); void readFields();
public: public:
//- Runtime type information //- Runtime type information
@ -630,32 +629,39 @@ public:
const dimensioned<cmptType>& ds const dimensioned<cmptType>& ds
); );
//- Use the minimum of the field and specified value //- Clamp field values (in-place) to the specified range.
// This sets the \em ceiling on the field values // A no-op for an invalid range.
void min(const dimensioned<Type>& dt); void clamp(const Type& lower, const Type& upper);
//- Use the maximum of the field and specified value //- Clamp field values (in-place) to the specified range.
// This sets the \em floor on the field values // A no-op for an invalid range.
void max(const dimensioned<Type>& dt); void clamp(const MinMax<Type>& range);
//- Clip the field to be bounded within the specified range //- Clamp field values (in-place) to the specified range.
void clip(const dimensioned<MinMax<Type>>& range); // A no-op for an invalid range. No dimension checking.
void clamp
//- Clip the field to be bounded within the specified range
void clip
( (
const dimensioned<Type>& minVal, const dimensioned<Type>& lower,
const dimensioned<Type>& maxVal const dimensioned<Type>& upper
); );
//- Deprecated(2019-01) identical to clip() //- Clamp field values (in-place) to the specified range.
// \deprecated(2019-01) identical to clip() // A no-op for an invalid range. No dimension checking.
FOAM_DEPRECATED_FOR(2019-01, "clip() method") void clamp(const dimensioned<MinMax<Type>>& range);
void maxMin
( //- Impose lower (floor) clamp on the field values (in-place)
const dimensioned<Type>& minVal, void clamp_min(const Type& lower);
const dimensioned<Type>& maxVal
); //- Impose upper (ceiling) clamp on the field values (in-place)
void clamp_max(const Type& upper);
//- Impose lower (floor) clamp on the field values (in-place)
// No dimension checking
void clamp_min(const dimensioned<Type>& lower);
//- Impose upper (ceiling) clamp on the field values (in-place)
// No dimension checking
void clamp_max(const dimensioned<Type>& upper);
// Member Operators // Member Operators
@ -691,7 +697,7 @@ public:
void operator/=(const dimensioned<scalar>&); void operator/=(const dimensioned<scalar>&);
// Ostream operators // Ostream Operators
friend Ostream& operator<< <Type, PatchField, GeoMesh> friend Ostream& operator<< <Type, PatchField, GeoMesh>
( (
@ -704,6 +710,41 @@ public:
Ostream&, Ostream&,
const tmp<GeometricField<Type, PatchField, GeoMesh>>& const tmp<GeometricField<Type, PatchField, GeoMesh>>&
); );
// Housekeeping
//- Clamp field values (in-place) to the specified range.
// \deprecated(2023-01) prefer clamp() naming
void clip(const dimensioned<MinMax<Type>>& range)
{
this->clamp(range);
}
//- Clamp field values (in-place) to the specified range.
// \deprecated(2023-01) prefer clamp() naming
void clip(const dimensioned<Type>& lo, const dimensioned<Type>& hi)
{
this->clamp(lo.value(), hi.value());
}
//- Use minimum of the field and specified value. ie, clamp_max().
// This sets the \em ceiling on the field values
// \deprecated(2023-01) prefer clamp_max()
void min(const dimensioned<Type>& upper) { this->clamp_max(upper); }
//- Use maximum of the field and specified value. ie, clamp_min().
// This sets the \em floor on the field values
// \deprecated(2023-01) prefer clamp_min()
void max(const dimensioned<Type>& lower) { this->clamp_min(lower); }
//- Deprecated(2019-01) identical to clamp()
// \deprecated(2019-01) identical to clamp()
FOAM_DEPRECATED_FOR(2019-01, "clamp() method")
void maxMin(const dimensioned<Type>& lo, const dimensioned<Type>& hi)
{
return this->clamp(lo.value(), hi.value());
}
}; };

View File

@ -294,6 +294,13 @@ inline bool notEqual(const Scalar a, const Scalar b)
} }
// Fast implementation, and with scalar promotion of upper/lower limits
inline Scalar clamp(const Scalar& val, const Scalar& lower, const Scalar& upper)
{
return (val < lower) ? lower : (upper < val) ? upper : val;
}
inline Scalar limit(const Scalar s1, const Scalar s2) inline Scalar limit(const Scalar s1, const Scalar s2)
{ {
return (mag(s1) < mag(s2)) ? s1: 0.0; return (mag(s1) < mag(s2)) ? s1: 0.0;

View File

@ -141,6 +141,15 @@ inline bool equal(const T& a, const T& b)
return (a == b); return (a == b);
} }
//- Return value clamped between upper and lower limits.
// Unlike the std::clamp, which selects between references, this version
// wraps the min/max free functions for component-wise clamping
template<class T>
inline T clamp(const T& val, const T& lower, const T& upper)
{
return min(max(val, lower), upper);
}
/*---------------------------------------------------------------------------*\ /*---------------------------------------------------------------------------*\
Struct labelOp Declaration Struct labelOp Declaration

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2019-2022 OpenCFD Ltd. Copyright (C) 2019-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -67,30 +67,16 @@ Description
Info<< "values values in range " << mask << nl; Info<< "values values in range " << mask << nl;
\endverbatim \endverbatim
One particular advantage offered by MinMax is to clip or limit values One advantage offered by MinMax is to clamp or limit values
to a particular range. For example, to a particular range. For example,
\verbatim \verbatim
scalarMinMax range(lower, upper); scalarMinMax range(lower, upper);
scalar val; scalar val;
val = range.clip(val) .. return clip values val = range.clamp(val) .. return clamped values
// vs. // vs.
val = min(max(value, lower, upper)) 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 \endverbatim
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
@ -112,6 +98,7 @@ namespace Foam
// Forward Declarations // Forward Declarations
template<class T> class MinMax; template<class T> class MinMax;
class zero; class zero;
class zero_one;
// Common min/max types // Common min/max types
typedef MinMax<label> labelMinMax; //!< A label min/max range typedef MinMax<label> labelMinMax; //!< A label min/max range
@ -155,6 +142,9 @@ public:
//- Construct with a single zero value //- Construct with a single zero value
inline explicit MinMax(const Foam::zero); inline explicit MinMax(const Foam::zero);
//- Implicit construct from zero_one as 0-1 range (pTraits zero, one)
inline MinMax(const Foam::zero_one);
//- Construct with a single initial value //- Construct with a single initial value
inline explicit MinMax(const T& val); inline explicit MinMax(const T& val);
@ -239,14 +229,9 @@ public:
//- True if the value is within the range (inclusive check) //- True if the value is within the range (inclusive check)
inline bool contains(const T& val) const; inline bool contains(const T& val) const;
//- If out of range, return the respective min/max limits, otherwise //- Return value clamped component-wise.
//- return the value itself. // If the range is invalid, just returns the value.
// If the range is invalid, always return the value. inline T clamp(const T& val) const;
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 // Manipulate
@ -284,6 +269,15 @@ public:
//- Divide range by scalar factor //- Divide range by scalar factor
inline MinMax<T>& operator/=(const scalar& s); inline MinMax<T>& operator/=(const scalar& s);
// Housekeeping
//- Old method name. Same as clamp (2023-01)
T clip(const T& val) const { return this->clamp(val); }
//- Old method (2023-01)
void inplaceClip(T& val) const { val = this->clamp(val); }
}; };

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2019-2022 OpenCFD Ltd. Copyright (C) 2019-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -44,7 +44,7 @@ inline Foam::MinMax<T> Foam::MinMax<T>::le(const T& maxVal)
template<class T> template<class T>
inline Foam::MinMax<T> Foam::MinMax<T>::zero_one() inline Foam::MinMax<T> Foam::MinMax<T>::zero_one()
{ {
return MinMax<T>(pTraits<T>::zero, pTraits<T>::one); return MinMax<T>(Foam::zero_one{});
} }
@ -85,6 +85,13 @@ inline Foam::MinMax<T>::MinMax(const Foam::zero)
{} {}
template<class T>
inline Foam::MinMax<T>::MinMax(const Foam::zero_one)
:
Tuple2<T,T>(pTraits<T>::zero, pTraits<T>::one)
{}
template<class T> template<class T>
inline Foam::MinMax<T>::MinMax(const T& val) inline Foam::MinMax<T>::MinMax(const T& val)
: :
@ -235,45 +242,17 @@ inline bool Foam::MinMax<T>::contains(const T& val) const
template<class T> template<class T>
inline const T& Foam::MinMax<T>::clip(const T& val) const inline T Foam::MinMax<T>::clamp(const T& val) const
{ {
if (good()) if (good())
{ {
if (val < min()) return Foam::min(Foam::max(val, min()), max());
{
return min();
}
else if (max() < val)
{
return max();
}
} }
return val; // Pass-through return val; // Pass-through
} }
template<class T>
inline bool Foam::MinMax<T>::inplaceClip(T& val) const
{
if (good())
{
if (val < min())
{
val = min();
return true;
}
else if (max() < val)
{
val = max();
return true;
}
}
return false; // No change
}
template<class T> template<class T>
inline Foam::MinMax<T>& Foam::MinMax<T>::add(const MinMax& other) inline Foam::MinMax<T>& Foam::MinMax<T>::add(const MinMax& other)
{ {

View File

@ -54,35 +54,60 @@ inline scalar mag(const MinMax<T>& range)
} }
//- Return the value after clipping by the min/max limiter //- Return the value after clamping by the min/max limiter
template<class T> template<class T>
inline T clip(const T& val, const MinMax<T>& range) inline T clip(const T& val, const MinMax<T>& range)
{ {
return range.clip(val); return range.clamp(val);
} }
//- Unary function for applying component-wise clamping
//- Return the value after clipping by the min/max limiter
template<class T> template<class T>
struct clipOp struct clampOp
{ {
T operator()(T& val, const MinMax<T>& range) const const T lower;
const T upper;
//- Construct from min/max limits. No validity checks
clampOp(const T& min, const T& max)
:
lower(min),
upper(max)
{}
//- Construct from min/max range. No validity checks
clampOp(const MinMax<T>& range)
:
lower(range.min()),
upper(range.max())
{}
//- Construct as 0-1 min/max range
clampOp(const Foam::zero_one)
:
clampOp(MinMax<T>(Foam::zero_one{}))
{}
T operator()(const T& val) const
{ {
return range.clip(val); return min(max(val, lower), upper);
} }
}; };
//- Clip value and assign inplace /// Not really needed
template<class T> /// //- Unary function for applying component-wise clamping (inplace)
struct clipEqOp /// template<class T>
{ /// struct clampEqOp : public clampOp<T>
bool operator()(T& val, const MinMax<T>& range) const /// {
{ /// using clampOp<T>::clampOp;
return range.inplaceClip(val); ///
} /// void operator()(T& val) const
}; /// {
/// val = clampOp<T>::operator()(val);
/// }
/// };
//- Extract the min/max range from a list of values. //- Extract the min/max range from a list of values.
@ -156,14 +181,14 @@ struct minMaxEqOp
}; };
//- The magnitude of an initial single value. //- The magnitude of a single value.
inline scalarMinMax minMaxMag(const scalar val) inline scalarMinMax minMaxMag(const scalar val)
{ {
return scalarMinMax(Foam::mag(val)); return scalarMinMax(Foam::mag(val));
} }
//- The magnitude of from an initial VectorSpace. //- The magnitude of a VectorSpace.
template<class Form, class Cmpt, direction nCmpt> template<class Form, class Cmpt, direction nCmpt>
inline scalarMinMax minMaxMag(const VectorSpace<Form,Cmpt,nCmpt>& vs) inline scalarMinMax minMaxMag(const VectorSpace<Form,Cmpt,nCmpt>& vs)
{ {

View File

@ -257,7 +257,7 @@ void Foam::lumpedPointMovement::readDict(const dictionary& dict)
} }
relax_ = dict.getOrDefault<scalar>("relax", 1); relax_ = dict.getOrDefault<scalar>("relax", 1);
scalarMinMax::zero_one().inplaceClip(relax_); relax_ = clamp(relax_, 0, 1);
forcesDict_.merge(dict.subOrEmptyDict("forces")); forcesDict_.merge(dict.subOrEmptyDict("forces"));

View File

@ -73,7 +73,7 @@ Foam::pointIndexHit Foam::searchableDisk::findNearest
v.normalise(); v.normalise();
// Clip to inner/outer radius // Clip to inner/outer radius
info.setPoint(origin() + radialLimits_.clip(magV)*v); info.setPoint(origin() + radialLimits_.clamp(magV)*v);
if (info.point().distSqr(sample) < nearestDistSqr) if (info.point().distSqr(sample) < nearestDistSqr)
{ {