mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
- shortcut for "clear" + "add". At the end of the operation, only parcels matching that particular selection source will be used.
425 lines
12 KiB
C
425 lines
12 KiB
C
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration |
|
|
\\ / A nd | Copyright (C) 2018 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/>.
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#include "parcelSelectionDetail.H"
|
|
#include "scalarPredicates.H"
|
|
#include "labelField.H"
|
|
#include "scalarField.H"
|
|
#include "pointField.H"
|
|
#include "ListListOps.H"
|
|
|
|
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
|
|
|
const Foam::Enum
|
|
<
|
|
Foam::Detail::parcelSelection::actionType
|
|
>
|
|
Foam::Detail::parcelSelection::actionNames
|
|
({
|
|
{ actionType::ALL, "all" },
|
|
{ actionType::CLEAR, "clear" },
|
|
{ actionType::INVERT, "invert" },
|
|
{ actionType::USE, "use" },
|
|
{ actionType::ADD, "add" },
|
|
{ actionType::SUBTRACT, "subtract" },
|
|
{ actionType::SUBSET, "subset" },
|
|
{ actionType::IGNORE, "ignore" },
|
|
});
|
|
|
|
|
|
const Foam::Enum
|
|
<
|
|
Foam::Detail::parcelSelection::sourceType
|
|
>
|
|
Foam::Detail::parcelSelection::sourceNames
|
|
({
|
|
{ sourceType::FIELD, "field" },
|
|
{ sourceType::STRIDE, "stride" },
|
|
});
|
|
|
|
|
|
const Foam::Enum
|
|
<
|
|
Foam::Detail::parcelSelection::logicType
|
|
> Foam::Detail::parcelSelection::logicNames
|
|
({
|
|
{ logicType::AND, "and" },
|
|
{ logicType::OR, "or" },
|
|
});
|
|
|
|
|
|
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
|
|
|
namespace Foam
|
|
{
|
|
template<class Type, class Predicate, class AccessOp>
|
|
static void apply
|
|
(
|
|
bitSet& selection,
|
|
const Detail::parcelSelection::actionType action,
|
|
const Predicate& accept,
|
|
const UList<Type>& list,
|
|
const AccessOp& aop
|
|
)
|
|
{
|
|
using actionType = Detail::parcelSelection::actionType;
|
|
|
|
const label len = selection.size();
|
|
|
|
switch (action)
|
|
{
|
|
case actionType::ADD:
|
|
case actionType::USE:
|
|
{
|
|
if (actionType::USE == action)
|
|
{
|
|
// USE = CLEAR + ADD (ie, only use this selection)
|
|
selection = false;
|
|
}
|
|
|
|
for (label parceli = 0; parceli < len; ++parceli)
|
|
{
|
|
if (accept(aop(list[parceli])))
|
|
{
|
|
selection.set(parceli);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case actionType::SUBTRACT:
|
|
{
|
|
for (label parceli = 0; parceli < len; ++parceli)
|
|
{
|
|
if (accept(aop(list[parceli])))
|
|
{
|
|
selection.unset(parceli);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case actionType::SUBSET:
|
|
{
|
|
for (const label parceli : selection)
|
|
{
|
|
if (!accept(aop(list[parceli])))
|
|
{
|
|
selection.unset(parceli);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // End namespace Foam
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
|
|
|
Foam::Detail::parcelSelection::parcelSelection()
|
|
:
|
|
parcelSelect_(),
|
|
parcelAddr_()
|
|
{}
|
|
|
|
|
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
|
|
|
bool Foam::Detail::parcelSelection::calculateFilter
|
|
(
|
|
const objectRegistry& obrTmp,
|
|
const bool log
|
|
)
|
|
{
|
|
if (parcelSelect_.empty())
|
|
{
|
|
parcelAddr_.clear();
|
|
return false;
|
|
}
|
|
|
|
// Start with all parcels unselected
|
|
|
|
// Number of parcels (locally)
|
|
const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
|
|
label nParcels = pointsPtr->size();
|
|
|
|
parcelAddr_.reset();
|
|
parcelAddr_.resize(nParcels);
|
|
|
|
reduce(nParcels, sumOp<label>());
|
|
|
|
Log << "Applying parcel filtering to " << nParcels << " parcels" << nl;
|
|
|
|
if (!nParcels)
|
|
{
|
|
parcelAddr_.clear();
|
|
return false;
|
|
}
|
|
|
|
// The unary function type(s) for testing a scalar.
|
|
// Allocate 3 slots.
|
|
// 0 is the test
|
|
// 1,2 are for storage of composite tests (eg, and/or logic)
|
|
predicates::scalars tests(3);
|
|
|
|
for (const entry& dEntry : parcelSelect_)
|
|
{
|
|
if (!dEntry.isDict())
|
|
{
|
|
WarningInFunction
|
|
<< "Ignoring non-dictionary entry "
|
|
<< dEntry << endl;
|
|
continue;
|
|
}
|
|
|
|
const dictionary& dict = dEntry.dict();
|
|
|
|
// A very limited number of sources (stride, field)
|
|
// and actions (all add subtract subset) so handle manually
|
|
|
|
auto action = actionNames.get("action", dict);
|
|
|
|
// These ones we do directly
|
|
switch (action)
|
|
{
|
|
case actionType::ALL:
|
|
Log << "- select all" << nl;
|
|
parcelAddr_ = true;
|
|
continue;
|
|
break;
|
|
|
|
case actionType::CLEAR:
|
|
Log << "- clear" << nl;
|
|
parcelAddr_ = false;
|
|
continue;
|
|
break;
|
|
|
|
case actionType::INVERT:
|
|
Log << "- invert" << nl;
|
|
parcelAddr_.flip();
|
|
continue;
|
|
break;
|
|
|
|
case actionType::IGNORE:
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// The others need a source
|
|
// Need a source
|
|
const auto source = sourceNames.get("source", dict);
|
|
|
|
switch (source)
|
|
{
|
|
case sourceType::STRIDE:
|
|
{
|
|
const label stride = dict.get<label>("stride");
|
|
|
|
const labelField& ids =
|
|
obrTmp.lookupObject<labelField>("origId");
|
|
|
|
Log << "- " << actionNames[action]
|
|
<< " stride " << stride << nl;
|
|
|
|
if (stride <= 0)
|
|
{
|
|
WarningInFunction
|
|
<< nl
|
|
<< "Ignoring bad value for stride=" << stride << nl
|
|
<< endl;
|
|
}
|
|
else if (stride == 1)
|
|
{
|
|
// More efficient handling of stride 1, but should
|
|
// not really be using stride == 1.
|
|
switch (action)
|
|
{
|
|
case actionType::ADD:
|
|
parcelAddr_ = true;
|
|
break;
|
|
|
|
case actionType::SUBTRACT:
|
|
parcelAddr_ = false;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Using stride > 1
|
|
apply
|
|
(
|
|
parcelAddr_,
|
|
action,
|
|
[=](const label id) -> bool { return !(id % stride); },
|
|
ids,
|
|
accessOp<label>() // pass-through
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case sourceType::FIELD:
|
|
{
|
|
const word fieldName(dict.get<word>("field"));
|
|
|
|
const auto* labelFld =
|
|
obrTmp.findObject<labelField>(fieldName);
|
|
|
|
const auto* scalarFld =
|
|
obrTmp.findObject<scalarField>(fieldName);
|
|
|
|
const auto* vectorFld =
|
|
obrTmp.findObject<vectorField>(fieldName);
|
|
|
|
|
|
Log << "- " << actionNames[action] << " field " << fieldName;
|
|
|
|
if (!labelFld && !scalarFld && !vectorFld)
|
|
{
|
|
WarningInFunction
|
|
<< nl
|
|
<< "No scalar/vector parcel field: " << fieldName
|
|
<< " ignoring selection" << nl
|
|
<< endl;
|
|
continue;
|
|
}
|
|
|
|
const entry& e = dict.lookupEntry("accept", keyType::LITERAL);
|
|
|
|
ITstream& is = e.stream();
|
|
|
|
if (4 == is.size())
|
|
{
|
|
// 4 tokens:
|
|
// -> (op val)
|
|
|
|
Tuple2<word,scalar> expr1(is);
|
|
e.checkITstream(is);
|
|
|
|
tests.first() = predicates::scalars::operation(expr1);
|
|
|
|
Log << " : " << expr1;
|
|
}
|
|
else if (9 == is.size())
|
|
{
|
|
// 9 tokens:
|
|
// -> (op val) and (op val)
|
|
// -> (op val) or (op val)
|
|
|
|
Tuple2<word,scalar> expr1(is);
|
|
word logicName(is);
|
|
Tuple2<word,scalar> expr2(is);
|
|
e.checkITstream(is);
|
|
|
|
logicType logic = logicNames[logicName];
|
|
tests[1] = predicates::scalars::operation(expr1);
|
|
tests[2] = predicates::scalars::operation(expr2);
|
|
|
|
switch (logic)
|
|
{
|
|
case logicType::AND:
|
|
tests.first() =
|
|
predicates::scalars::andOp(tests[1], tests[2]);
|
|
break;
|
|
|
|
case logicType::OR:
|
|
tests.first() =
|
|
predicates::scalars::orOp(tests[1], tests[2]);
|
|
break;
|
|
}
|
|
|
|
Log << " : " << expr1 << ' ' << logicName << ' ' << expr2;
|
|
}
|
|
else
|
|
{
|
|
action = actionType::IGNORE;
|
|
|
|
// Use the following to always provoke an error.
|
|
e.checkITstream(is);
|
|
}
|
|
|
|
if (labelFld)
|
|
{
|
|
apply
|
|
(
|
|
parcelAddr_,
|
|
action,
|
|
tests.first(),
|
|
*labelFld,
|
|
accessOp<label>() // pass-through
|
|
);
|
|
}
|
|
else if (scalarFld)
|
|
{
|
|
apply
|
|
(
|
|
parcelAddr_,
|
|
action,
|
|
tests.first(),
|
|
*scalarFld,
|
|
accessOp<scalar>() // pass-through
|
|
);
|
|
}
|
|
else if (vectorFld)
|
|
{
|
|
apply
|
|
(
|
|
parcelAddr_,
|
|
action,
|
|
tests.first(),
|
|
*vectorFld,
|
|
[](const vector& val) -> scalar
|
|
{
|
|
return Foam::mag(val);
|
|
}
|
|
);
|
|
}
|
|
|
|
Log << endl;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|