ENH: add probes sampleOnExecute option (#2358)

- supports sampling/probing of values to obtain min/max/average/size
  at execution intervals without writing any output or generating
  output directories.

- 'verbose' option for additional output
This commit is contained in:
Mark Olesen
2022-02-09 13:48:31 +01:00
parent e147ac52e9
commit 0511aebd86
9 changed files with 539 additions and 721 deletions

View File

@ -1,6 +1,5 @@
probes/probes.C
probes/patchProbes.C
probes/probesGrouping.C
sampledSet/circle/circleSet.C
sampledSet/cloud/cloudSet.C

View File

@ -201,13 +201,16 @@ void Foam::patchProbes::findElements(const fvMesh& mesh)
// - faceList_ : faces (now patch faces)
// - patchIDList_ : patch corresponding to faceList
// - processor_ : processor
elementList_.setSize(nearest.size());
elementList_.resize_nocopy(nearest.size());
elementList_ = -1;
faceList_.setSize(nearest.size());
faceList_.resize_nocopy(nearest.size());
faceList_ = -1;
processor_.setSize(nearest.size());
processor_.resize_nocopy(nearest.size());
processor_ = -1;
patchIDList_.setSize(nearest.size());
patchIDList_.resize_nocopy(nearest.size());
patchIDList_ = -1;
forAll(nearest, sampleI)
@ -236,13 +239,13 @@ void Foam::patchProbes::findElements(const fvMesh& mesh)
Foam::patchProbes::patchProbes
(
const word& name,
const Time& t,
const Time& runTime,
const dictionary& dict,
const bool loadFromFiles,
const bool readFields
)
:
probes(name, t, dict, loadFromFiles, false)
probes(name, runTime, dict, loadFromFiles, false)
{
if (readFields)
{
@ -253,27 +256,43 @@ Foam::patchProbes::patchProbes
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::patchProbes::write()
bool Foam::patchProbes::performAction(unsigned request)
{
if (this->size() && prepare())
if (!pointField::empty() && request && prepare(request))
{
sampleAndWrite(scalarFields_);
sampleAndWrite(vectorFields_);
sampleAndWrite(sphericalTensorFields_);
sampleAndWrite(symmTensorFields_);
sampleAndWrite(tensorFields_);
performAction(scalarFields_, request);
performAction(vectorFields_, request);
performAction(sphericalTensorFields_, request);
performAction(symmTensorFields_, request);
performAction(tensorFields_, request);
sampleAndWriteSurfaceFields(surfaceScalarFields_);
sampleAndWriteSurfaceFields(surfaceVectorFields_);
sampleAndWriteSurfaceFields(surfaceSphericalTensorFields_);
sampleAndWriteSurfaceFields(surfaceSymmTensorFields_);
sampleAndWriteSurfaceFields(surfaceTensorFields_);
performAction(surfaceScalarFields_, request);
performAction(surfaceVectorFields_, request);
performAction(surfaceSphericalTensorFields_, request);
performAction(surfaceSymmTensorFields_, request);
performAction(surfaceTensorFields_, request);
}
return true;
}
bool Foam::patchProbes::execute()
{
if (onExecute_)
{
return performAction(ACTION_ALL & ~ACTION_WRITE);
}
return true;
}
bool Foam::patchProbes::write()
{
return performAction(ACTION_ALL);
}
bool Foam::patchProbes::read(const dictionary& dict)
{
if (!dict.readIfPresent("patches", patchNames_))

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2020 OpenCFD Ltd.
Copyright (C) 2016-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -64,14 +64,14 @@ Description
}
\endverbatim
SourceFiles
patchProbes.C
patchProbesTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef patchProbes_H
#define patchProbes_H
#ifndef Foam_patchProbes_H
#define Foam_patchProbes_H
#include "probes.H"
@ -80,12 +80,6 @@ SourceFiles
namespace Foam
{
// Forward declaration of classes
class objectRegistry;
class dictionary;
class fvMesh;
class mapPolyMesh;
/*---------------------------------------------------------------------------*\
Class patchProbes Declaration
\*---------------------------------------------------------------------------*/
@ -96,7 +90,7 @@ class patchProbes
{
protected:
// Protected data
// Protected Data
//- Patches to sample
wordRes patchNames_;
@ -104,52 +98,35 @@ protected:
// Protected Member Functions
//- Sample and write a particular volume field
template<class Type>
void sampleAndWrite
(
const GeometricField<Type, fvPatchField, volMesh>&
);
//- Sample and write a particular surface field
template<class Type>
void sampleAndWrite
(
const GeometricField<Type, fvsPatchField, surfaceMesh>&
);
//- Sample and write all the fields of the given type
template<class Type>
void sampleAndWrite(const fieldGroup<Type>&);
//- Sample and write all the surface fields of the given type
template<class Type>
void sampleAndWriteSurfaceFields(const fieldGroup<Type>&);
//- Sample a volume field at all locations
template<class Type>
tmp<Field<Type>> sample
(
const GeometricField<Type, fvPatchField, volMesh>&
) const;
//- Sample a surface field at all locations
template<class Type>
tmp<Field<Type>> sample
(
const GeometricField<Type, fvsPatchField, surfaceMesh>&
) const;
//- Sample a single field on all sample locations
template<class Type>
tmp<Field<Type>> sample(const word& fieldName) const;
//- Find elements containing patchProbes
virtual void findElements(const fvMesh&);
virtual void findElements(const fvMesh& mesh); // override
private:
// Private Member Functions
//- Write field values
template<class Type>
void writeValues
(
const word& fieldName,
const Field<Type>& values,
const scalar timeValue
);
//- Sample and store/write applicable volume/surface fields
template<class GeoField>
void performAction
(
const fieldGroup<GeoField>& fieldNames, /* must be sorted */
unsigned request
);
//- Perform sampling action with store/write
bool performAction(unsigned request);
//- No copy construct
patchProbes(const patchProbes&) = delete;
@ -169,7 +146,7 @@ public:
patchProbes
(
const word& name,
const Time& time,
const Time& runTime,
const dictionary& dict,
const bool loadFromFiles = false,
const bool readFields = true
@ -179,13 +156,35 @@ public:
virtual ~patchProbes() = default;
//- Public members
// Member Functions
//- Sample and store result if the sampleOnExecute is enabled.
virtual bool execute();
//- Sample and write
virtual bool write();
//- Read
virtual bool read(const dictionary&);
// Sampling
//- Sample a volume field at all locations
template<class Type>
tmp<Field<Type>> sample(const VolumeField<Type>&) const;
//- Sample a surface field at all locations
template<class Type>
tmp<Field<Type>> sample(const SurfaceField<Type>&) const;
//- Sample a single field on all sample locations
template<class Type>
tmp<Field<Type>> sample(const word& fieldName) const;
//- Sample a surface field at all locations
template<class Type>
tmp<Field<Type>> sampleSurfaceField(const word& fieldName) const;
};

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2021 OpenCFD Ltd.
Copyright (C) 2021-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,153 +28,57 @@ License
#include "patchProbes.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "IOmanip.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class Type>
void Foam::patchProbes::sampleAndWrite
void Foam::patchProbes::writeValues
(
const GeometricField<Type, fvPatchField, volMesh>& vField
const word& fieldName,
const Field<Type>& values,
const scalar timeValue
)
{
Field<Type> values(sample(vField));
if (Pstream::master())
{
unsigned int w = IOstream::defaultPrecision() + 7;
OFstream& probeStream = *probeFilePtrs_[vField.name()];
const unsigned int w = IOstream::defaultPrecision() + 7;
OFstream& os = *probeFilePtrs_[fieldName];
probeStream
<< setw(w)
<< vField.time().timeOutputValue();
os << setw(w) << timeValue;
for (const auto& v : values)
{
probeStream << ' ' << setw(w) << v;
os << ' ' << setw(w) << v;
}
probeStream << endl;
os << endl;
}
const word& fieldName = vField.name();
this->setResult("average(" + fieldName + ")", average(values));
this->setResult("min(" + fieldName + ")", min(values));
this->setResult("max(" + fieldName + ")", max(values));
this->setResult("size(" + fieldName + ")", values.size());
}
template<class Type>
void Foam::patchProbes::sampleAndWrite
template<class GeoField>
void Foam::patchProbes::performAction
(
const GeometricField<Type, fvsPatchField, surfaceMesh>& sField
const fieldGroup<GeoField>& fieldNames,
unsigned request
)
{
Field<Type> values(sample(sField));
if (Pstream::master())
for (const word& fieldName : fieldNames)
{
unsigned int w = IOstream::defaultPrecision() + 7;
OFstream& probeStream = *probeFilePtrs_[sField.name()];
tmp<GeoField> tfield = getOrLoadField<GeoField>(fieldName);
probeStream
<< setw(w)
<< sField.time().timeOutputValue();
for (const auto& v : values)
if (tfield)
{
probeStream << ' ' << setw(w) << v;
}
probeStream << endl;
}
const auto& field = tfield();
const scalar timeValue = field.time().timeOutputValue();
const word& fieldName = sField.name();
this->setResult("average(" + fieldName + ")", average(values));
this->setResult("min(" + fieldName + ")", min(values));
this->setResult("max(" + fieldName + ")", max(values));
this->setResult("size(" + fieldName + ")", values.size());
}
Field<typename GeoField::value_type> values(sample(field));
template<class Type>
void Foam::patchProbes::sampleAndWrite
(
const fieldGroup<Type>& fields
)
{
typedef GeometricField<Type, fvPatchField, volMesh> VolFieldType;
for (const auto& fieldName : fields)
{
if (loadFromFiles_)
{
sampleAndWrite
(
VolFieldType
(
IOobject
(
fieldName,
mesh_.time().timeName(),
mesh_,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
),
mesh_
)
);
}
else
{
objectRegistry::const_iterator iter = mesh_.find(fieldName);
if (iter.found() && iter()->type() == VolFieldType::typeName)
this->storeResults(fieldName, values);
if (request & ACTION_WRITE)
{
sampleAndWrite(mesh_.lookupObject<VolFieldType>(fieldName));
}
}
}
}
template<class Type>
void Foam::patchProbes::sampleAndWriteSurfaceFields
(
const fieldGroup<Type>& fields
)
{
typedef GeometricField<Type, fvsPatchField, surfaceMesh> SurfaceFieldType;
for (const auto& fieldName : fields)
{
if (loadFromFiles_)
{
sampleAndWrite
(
SurfaceFieldType
(
IOobject
(
fieldName,
mesh_.time().timeName(),
mesh_,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
),
mesh_
)
);
}
else
{
objectRegistry::const_iterator iter = mesh_.find(fieldName);
if (iter.found() && iter()->type() == SurfaceFieldType::typeName)
{
sampleAndWrite(mesh_.lookupObject<SurfaceFieldType>(fieldName));
this->writeValues(fieldName, values, timeValue);
}
}
}
@ -185,17 +89,15 @@ void Foam::patchProbes::sampleAndWriteSurfaceFields
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::patchProbes::sample
(
const GeometricField<Type, fvPatchField, volMesh>& vField
) const
Foam::patchProbes::sample(const VolumeField<Type>& vField) const
{
const Type unsetVal(-VGREAT*pTraits<Type>::one);
auto tValues = tmp<Field<Type>>::New(Field<Type>(this->size(), unsetVal));
auto& values = tValues.ref();
auto tvalues = tmp<Field<Type>>::New(Field<Type>(this->size(), unsetVal));
auto& values = tvalues.ref();
const polyBoundaryMesh& patches = mesh_.boundaryMesh();
const auto& bField = vField.boundaryField();
forAll(*this, probei)
{
@ -205,14 +107,45 @@ Foam::patchProbes::sample
{
label patchi = patches.whichPatch(facei);
label localFacei = patches[patchi].whichFace(facei);
values[probei] = vField.boundaryField()[patchi][localFacei];
values[probei] = bField[patchi][localFacei];
}
}
Pstream::listCombineGather(values, isNotEqOp<Type>());
Pstream::listCombineScatter(values);
return tValues;
return tvalues;
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::patchProbes::sample(const SurfaceField<Type>& sField) const
{
const Type unsetVal(-VGREAT*pTraits<Type>::one);
auto tvalues = tmp<Field<Type>>::New(Field<Type>(this->size(), unsetVal));
auto& values = tvalues.ref();
const polyBoundaryMesh& patches = mesh_.boundaryMesh();
const auto& bField = sField.boundaryField();
forAll(*this, probei)
{
label facei = faceList_[probei];
if (facei >= 0)
{
label patchi = patches.whichPatch(facei);
label localFacei = patches[patchi].whichFace(facei);
values[probei] = bField[patchi][localFacei];
}
}
Pstream::listCombineGather(values, isNotEqOp<Type>());
Pstream::listCombineScatter(values);
return tvalues;
}
@ -220,46 +153,15 @@ template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::patchProbes::sample(const word& fieldName) const
{
return sample
(
mesh_.lookupObject<GeometricField<Type, fvPatchField, volMesh>>
(
fieldName
)
);
return sample(mesh_.lookupObject<VolumeField<Type>>(fieldName));
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::patchProbes::sample
(
const GeometricField<Type, fvsPatchField, surfaceMesh>& sField
) const
Foam::patchProbes::sampleSurfaceField(const word& fieldName) const
{
const Type unsetVal(-VGREAT*pTraits<Type>::one);
auto tValues = tmp<Field<Type>>::New(Field<Type>(this->size(), unsetVal));
auto& values = tValues.ref();
const polyBoundaryMesh& patches = mesh_.boundaryMesh();
forAll(*this, probei)
{
label facei = faceList_[probei];
if (facei >= 0)
{
label patchi = patches.whichPatch(facei);
label localFacei = patches[patchi].whichFace(facei);
values[probei] = sField.boundaryField()[patchi][localFacei];
}
}
Pstream::listCombineGather(values, isNotEqOp<Type>());
Pstream::listCombineScatter(values);
return tValues;
return sample(mesh_.lookupObject<SurfaceField<Type>>(fieldName));
}

View File

@ -27,8 +27,9 @@ License
\*---------------------------------------------------------------------------*/
#include "probes.H"
#include "volFields.H"
#include "dictionary.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "Time.H"
#include "IOmanip.H"
#include "mapPolyMesh.H"
@ -55,18 +56,14 @@ void Foam::probes::findElements(const fvMesh& mesh)
{
DebugInfo<< "probes: resetting sample locations" << endl;
elementList_.clear();
elementList_.setSize(size());
faceList_.clear();
faceList_.setSize(size());
processor_.setSize(size());
elementList_.resize_nocopy(pointField::size());
faceList_.resize_nocopy(pointField::size());
processor_.resize_nocopy(pointField::size());
processor_ = -1;
forAll(*this, probei)
{
const vector& location = operator[](probei);
const point& location = (*this)[probei];
const label celli = mesh.findCell(location);
@ -107,7 +104,7 @@ void Foam::probes::findElements(const fvMesh& mesh)
// Check if all probes have been found.
forAll(elementList_, probei)
{
const vector& location = operator[](probei);
const point& location = operator[](probei);
label celli = elementList_[probei];
label facei = faceList_[probei];
@ -171,15 +168,53 @@ void Foam::probes::findElements(const fvMesh& mesh)
}
Foam::label Foam::probes::prepare()
Foam::label Foam::probes::prepare(unsigned request)
{
const label nFields = classifyFields();
// Prefilter on selection
HashTable<wordHashSet> selected =
(
loadFromFiles_
? IOobjectList(mesh_, mesh_.time().timeName()).classes(fieldSelection_)
: mesh_.classes(fieldSelection_)
);
// adjust file streams
// Classify and count fields
label nFields = 0;
do
{
#undef doLocalCode
#define doLocalCode(InputType, Target) \
{ \
Target.clear(); /* Remove old values */ \
const auto iter = selected.cfind(InputType::typeName); \
if (iter.found()) \
{ \
/* Add new (current) values */ \
Target.append(iter.val().sortedToc()); \
nFields += Target.size(); \
} \
}
doLocalCode(volScalarField, scalarFields_);
doLocalCode(volVectorField, vectorFields_)
doLocalCode(volSphericalTensorField, sphericalTensorFields_);
doLocalCode(volSymmTensorField, symmTensorFields_);
doLocalCode(volTensorField, tensorFields_);
doLocalCode(surfaceScalarField, surfaceScalarFields_);
doLocalCode(surfaceVectorField, surfaceVectorFields_);
doLocalCode(surfaceSphericalTensorField, surfaceSphericalTensorFields_);
doLocalCode(surfaceSymmTensorField, surfaceSymmTensorFields_);
doLocalCode(surfaceTensorField, surfaceTensorFields_);
#undef doLocalCode
}
while (false);
// Adjust file streams
if (Pstream::master())
{
wordHashSet currentFields;
wordHashSet currentFields(2*nFields);
currentFields.insert(scalarFields_);
currentFields.insert(vectorFields_);
currentFields.insert(sphericalTensorFields_);
@ -197,27 +232,7 @@ Foam::label Foam::probes::prepare()
<< "Probing locations: " << *this << nl
<< endl;
fileName probeSubDir = name();
if (mesh_.name() != polyMesh::defaultRegion)
{
probeSubDir = probeSubDir/mesh_.name();
}
// Put in undecomposed case
// (Note: gives problems for distributed data running)
fileName probeDir
(
mesh_.time().globalPath()
/ functionObject::outputPrefix
/ probeSubDir
/ mesh_.time().timeName()
);
probeDir.clean(); // Remove unneeded ".."
// ignore known fields, close streams for fields that no longer exist
// Close streams for fields that no longer exist
forAllIters(probeFilePtrs_, iter)
{
if (!currentFields.erase(iter.key()))
@ -228,28 +243,59 @@ Foam::label Foam::probes::prepare()
}
}
// currentFields now just has the new fields - open streams for them
for (const word& fieldName : currentFields)
if (!(request & ACTION_WRITE))
{
// Create directory if does not exist.
mkDir(probeDir);
// No writing - can return now
return nFields;
}
else if (currentFields.empty())
{
// No new fields - can return now
return nFields;
}
auto fPtr = autoPtr<OFstream>::New(probeDir/fieldName);
auto& fout = *fPtr;
DebugInfo<< "open probe stream: " << fout.name() << endl;
// Have new fields - open streams for them
probeFilePtrs_.insert(fieldName, fPtr);
// Put in undecomposed case
// (Note: gives problems for distributed data running)
unsigned int w = IOstream::defaultPrecision() + 7;
fileName probeSubDir = name();
if (mesh_.name() != polyMesh::defaultRegion)
{
probeSubDir = probeSubDir/mesh_.name();
}
fileName probeDir
(
mesh_.time().globalPath()
/ functionObject::outputPrefix
/ probeSubDir
/ mesh_.time().timeName()
);
probeDir.clean(); // Remove unneeded ".."
// Create directory if needed
mkDir(probeDir);
for (const word& fieldName : currentFields.sortedToc())
{
auto osPtr = autoPtr<OFstream>::New(probeDir/fieldName);
auto& os = *osPtr;
probeFilePtrs_.insert(fieldName, osPtr);
DebugInfo<< "open probe stream: " << os.name() << endl;
const unsigned int w = IOstream::defaultPrecision() + 7;
forAll(*this, probei)
{
fout<< "# Probe " << probei << ' ' << operator[](probei);
os << "# Probe " << probei << ' ' << operator[](probei);
if (processor_[probei] == -1)
{
fout<< " # Not Found";
os << " # Not Found";
}
// Only for patchProbes
else if (probei < patchIDList_.size())
@ -264,31 +310,31 @@ Foam::label Foam::probes::prepare()
|| processor_[probei] == Pstream::myProcNo()
)
{
fout<< " at patch " << bm[patchi].name();
os << " at patch " << bm[patchi].name();
}
fout<< " with a distance of "
os << " with a distance of "
<< mag(operator[](probei)-oldPoints_[probei])
<< " m to the original point "
<< oldPoints_[probei];
}
}
fout<< endl;
os << nl;
}
fout<< '#' << setw(IOstream::defaultPrecision() + 6)
os << '#' << setw(IOstream::defaultPrecision() + 6)
<< "Probe";
forAll(*this, probei)
{
if (includeOutOfBounds_ || processor_[probei] != -1)
{
fout<< ' ' << setw(w) << probei;
os << ' ' << setw(w) << probei;
}
}
fout<< endl;
os << nl;
fout<< '#' << setw(IOstream::defaultPrecision() + 6)
os << '#' << setw(IOstream::defaultPrecision() + 6)
<< "Time" << endl;
}
}
@ -308,23 +354,15 @@ Foam::probes::probes
const bool readFields
)
:
stateFunctionObject(name, runTime),
pointField(0),
mesh_
(
refCast<const fvMesh>
(
runTime.lookupObject<objectRegistry>
(
dict.getOrDefault("region", polyMesh::defaultRegion)
)
)
),
functionObjects::fvMeshFunctionObject(name, runTime, dict),
pointField(),
loadFromFiles_(loadFromFiles),
fieldSelection_(),
fixedLocations_(true),
interpolationScheme_("cell"),
includeOutOfBounds_(true)
includeOutOfBounds_(true),
verbose_(false),
onExecute_(false),
fieldSelection_(),
samplePointScheme_("cell")
{
if (readFields)
{
@ -335,15 +373,28 @@ Foam::probes::probes
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::probes::verbose(const bool on) noexcept
{
bool old(verbose_);
verbose_ = on;
return old;
}
bool Foam::probes::read(const dictionary& dict)
{
dict.readEntry("probeLocations", static_cast<pointField&>(*this));
dict.readEntry("fields", fieldSelection_);
dict.readIfPresent("fixedLocations", fixedLocations_);
if (dict.readIfPresent("interpolationScheme", interpolationScheme_))
dict.readIfPresent("includeOutOfBounds", includeOutOfBounds_);
verbose_ = dict.getOrDefault("verbose", false);
onExecute_ = dict.getOrDefault("sampleOnExecute", false);
if (dict.readIfPresent("interpolationScheme", samplePointScheme_))
{
if (!fixedLocations_ && interpolationScheme_ != "cell")
if (!fixedLocations_ && samplePointScheme_ != "cell")
{
WarningInFunction
<< "Only cell interpolation can be applied when "
@ -352,41 +403,51 @@ bool Foam::probes::read(const dictionary& dict)
<< endl;
}
}
dict.readIfPresent("includeOutOfBounds", includeOutOfBounds_);
// Initialise cells to sample from supplied locations
findElements(mesh_);
prepare();
// Close old (ununsed) streams
prepare(ACTION_NONE);
return true;
}
bool Foam::probes::performAction(unsigned request)
{
if (!pointField::empty() && request && prepare(request))
{
performAction(scalarFields_, request);
performAction(vectorFields_, request);
performAction(sphericalTensorFields_, request);
performAction(symmTensorFields_, request);
performAction(tensorFields_, request);
performAction(surfaceScalarFields_, request);
performAction(surfaceVectorFields_, request);
performAction(surfaceSphericalTensorFields_, request);
performAction(surfaceSymmTensorFields_, request);
performAction(surfaceTensorFields_, request);
}
return true;
}
bool Foam::probes::execute()
{
if (onExecute_)
{
return performAction(ACTION_ALL & ~ACTION_WRITE);
}
return true;
}
bool Foam::probes::write()
{
if (size() && prepare())
{
sampleAndWrite(scalarFields_);
sampleAndWrite(vectorFields_);
sampleAndWrite(sphericalTensorFields_);
sampleAndWrite(symmTensorFields_);
sampleAndWrite(tensorFields_);
sampleAndWriteSurfaceFields(surfaceScalarFields_);
sampleAndWriteSurfaceFields(surfaceVectorFields_);
sampleAndWriteSurfaceFields(surfaceSphericalTensorFields_);
sampleAndWriteSurfaceFields(surfaceSymmTensorFields_);
sampleAndWriteSurfaceFields(surfaceTensorFields_);
}
return true;
return performAction(ACTION_ALL);
}

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2021 OpenCFD Ltd.
Copyright (C) 2016-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -50,7 +50,7 @@ Description
writeInterval 1;
// Fields to be probed
fields (p U);
fields (U "p.*");
// Optional: do not recalculate cells if mesh moves
fixedLocations false;
@ -72,15 +72,28 @@ Description
}
\endverbatim
Entries:
\table
Property | Description | Required | Default
type | Type-name: probes | yes |
probeLocations | Probe locations | yes |
fields | word/regex list of fields to sample | yes |
interpolationScheme | scheme to obtain values | no | cell
fixedLocations | Do not recalculate cells if mesh moves | no | true
includeOutOfBounds | Include out-of-bounds locations | no | true
sampleOnExecute | Sample on execution and store results | no | false
\endtable
SourceFiles
probes.C
probesTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef probes_H
#define probes_H
#ifndef Foam_probes_H
#define Foam_probes_H
#include "stateFunctionObject.H"
#include "fvMeshFunctionObject.H"
#include "HashPtrTable.H"
#include "OFstream.H"
#include "polyMesh.H"
@ -89,15 +102,14 @@ SourceFiles
#include "surfaceFieldsFwd.H"
#include "surfaceMesh.H"
#include "wordRes.H"
using namespace Foam::functionObjects;
#include "IOobjectList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declaration of classes
// Forward Declarations
class Time;
class objectRegistry;
class dictionary;
@ -110,70 +122,75 @@ class mapPolyMesh;
class probes
:
public stateFunctionObject,
public functionObjects::fvMeshFunctionObject,
public pointField
{
protected:
// Protected classes
// Protected Classes
//- Class used for grouping field types
template<class Type>
class fieldGroup
:
public DynamicList<word>
//- Grouping of field names by GeometricField type
template<class GeoField>
struct fieldGroup : public DynamicList<word> {};
// Data Types
//- Local control for sampling actions
enum sampleActionType : unsigned
{
public:
//- Construct null
fieldGroup()
:
DynamicList<word>(0)
{}
ACTION_NONE = 0,
ACTION_WRITE = 0x1,
ACTION_STORE = 0x2,
ACTION_ALL = 0xF
};
// Protected data
//- Const reference to fvMesh
const fvMesh& mesh_;
// Protected Data
//- Load fields from files (not from objectRegistry)
bool loadFromFiles_;
// Read from dictionary
//- Names of fields to probe
wordRes fieldSelection_;
//- Fixed locations, default = yes
//- Fixed locations (default: true)
// Note: set to false for moving mesh calculations where locations
// should move with the mesh
bool fixedLocations_;
//- Interpolation scheme name
// Note: only possible when fixedLocations_ is true
word interpolationScheme_;
//- Include probes that were not found
//- Include probes that were not found (default: true)
bool includeOutOfBounds_;
//- Output verbosity
bool verbose_;
// Calculated
//- Perform sample actions on execute as well
bool onExecute_;
//- Categorized scalar/vector/tensor vol fields
fieldGroup<scalar> scalarFields_;
fieldGroup<vector> vectorFields_;
fieldGroup<sphericalTensor> sphericalTensorFields_;
fieldGroup<symmTensor> symmTensorFields_;
fieldGroup<tensor> tensorFields_;
//- Requested names of fields to probe
wordRes fieldSelection_;
//- Categorized scalar/vector/tensor surf fields
fieldGroup<scalar> surfaceScalarFields_;
fieldGroup<vector> surfaceVectorFields_;
fieldGroup<sphericalTensor> surfaceSphericalTensorFields_;
fieldGroup<symmTensor> surfaceSymmTensorFields_;
fieldGroup<tensor> surfaceTensorFields_;
//- Interpolation/sample scheme to obtain values at the points
// Note: only possible when fixedLocations_ is true
word samplePointScheme_;
// Calculated
//- Current list of field names selected for sampling
DynamicList<word> selectedFieldNames_;
//- Categorized scalar/vector/tensor volume fields
fieldGroup<volScalarField> scalarFields_;
fieldGroup<volVectorField> vectorFields_;
fieldGroup<volSphericalTensorField> sphericalTensorFields_;
fieldGroup<volSymmTensorField> symmTensorFields_;
fieldGroup<volTensorField> tensorFields_;
//- Categorized scalar/vector/tensor surface fields
fieldGroup<surfaceScalarField> surfaceScalarFields_;
fieldGroup<surfaceVectorField> surfaceVectorFields_;
fieldGroup<surfaceSphericalTensorField> surfaceSphericalTensorFields_;
fieldGroup<surfaceSymmTensorField> surfaceSymmTensorFields_;
fieldGroup<surfaceTensorField> surfaceTensorFields_;
//- Cells to be probed (obtained from the locations)
labelList elementList_;
@ -185,58 +202,57 @@ protected:
// on any processor)
labelList processor_;
//- Current open files
//- Current open files (non-empty on master only)
HashPtrTable<OFstream> probeFilePtrs_;
// Additional fields for patchProbes
//- Patch IDs on which the new probes are located (for patchProbes)
labelList patchIDList_;
//- Patch IDs on which the new probes are located
labelList patchIDList_;
//- Original probes location (only used for patchProbes)
pointField oldPoints_;
//- Original probes location (only used for patchProbes)
pointField oldPoints_;
// Protected Member Functions
//- Clear old field groups
void clearFieldGroups();
//- Classify field types, returns the number of fields
label classifyFields();
//- Find cells and faces containing probes
virtual void findElements(const fvMesh& mesh);
//- Classify field type and open/close file streams,
// returns number of fields to sample
label prepare();
//- Classify field types, close/open file streams
// \return number of fields to sample
label prepare(unsigned request);
//- Get from registry or load from disk
template<class GeoField>
tmp<GeoField> getOrLoadField(const word& fieldName) const;
//- Store results: min/max/average/size
template<class Type>
void storeResults(const word& fieldName, const Field<Type>& values);
private:
//- Sample and write a particular volume field
// Private Member Functions
//- Write field values
template<class Type>
void sampleAndWrite
void writeValues
(
const GeometricField<Type, fvPatchField, volMesh>&
const word& fieldName,
const Field<Type>& values,
const scalar timeValue
);
//- Sample and write a particular surface field
template<class Type>
void sampleAndWrite
//- Sample and store/write all applicable sampled fields
template<class GeoField>
void performAction
(
const GeometricField<Type, fvsPatchField, surfaceMesh>&
const fieldGroup<GeoField>& fieldNames, /* must be sorted */
unsigned request
);
//- Sample and write all the fields of the given type
template<class Type>
void sampleAndWrite(const fieldGroup<Type>&);
//- Sample and write all the surface fields of the given type
template<class Type>
void sampleAndWriteSurfaceFields(const fieldGroup<Type>&);
//- Perform sampling action with store/write
bool performAction(unsigned request);
//- No copy construct
probes(const probes&) = delete;
@ -244,7 +260,6 @@ private:
//- No copy assignment
void operator=(const probes&) = delete;
public:
//- Runtime type information
@ -270,14 +285,18 @@ public:
// Member Functions
//- Enable/disable verbose output
// \return old value
bool verbose(const bool on) noexcept;
//- Return names of fields to probe
virtual const wordRes& fieldNames() const
virtual const wordRes& fieldNames() const noexcept
{
return fieldSelection_;
}
//- Return locations to probe
virtual const pointField& probeLocations() const
virtual const pointField& probeLocations() const noexcept
{
return *this;
}
@ -289,7 +308,7 @@ public:
}
//- Cells to be probed (obtained from the locations)
const labelList& elements() const
const labelList& elements() const noexcept
{
return elementList_;
}
@ -297,7 +316,7 @@ public:
//- Read the probes
virtual bool read(const dictionary&);
//- Execute, currently does nothing
//- Sample and store result if the sampleOnExecute is enabled.
virtual bool execute();
//- Sample and write
@ -313,27 +332,24 @@ public:
virtual void readUpdate(const polyMesh::readUpdateState state)
{}
// Sampling
//- Sample a volume field at all locations
template<class Type>
tmp<Field<Type>> sample
(
const GeometricField<Type, fvPatchField, volMesh>&
) const;
//- Sample a single vol field on all sample locations
template<class Type>
tmp<Field<Type>> sample(const word& fieldName) const;
//- Sample a single scalar field on all sample locations
template<class Type>
tmp<Field<Type>> sampleSurfaceFields(const word& fieldName) const;
tmp<Field<Type>> sample(const VolumeField<Type>&) const;
//- Sample a surface field at all locations
template<class Type>
tmp<Field<Type>> sample
(
const GeometricField<Type, fvsPatchField, surfaceMesh>&
) const;
tmp<Field<Type>> sample(const SurfaceField<Type>&) const;
//- Sample a volume field at all locations
template<class Type>
tmp<Field<Type>> sample(const word& fieldName) const;
//- Sample a surface field at all locations
template<class Type>
tmp<Field<Type>> sampleSurfaceField(const word& fieldName) const;
};

View File

@ -1,127 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
-------------------------------------------------------------------------------
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 "probes.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "IOobjectList.H"
#include "stringListOps.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::probes::clearFieldGroups()
{
scalarFields_.clear();
vectorFields_.clear();
sphericalTensorFields_.clear();
symmTensorFields_.clear();
tensorFields_.clear();
surfaceScalarFields_.clear();
surfaceVectorFields_.clear();
surfaceSphericalTensorFields_.clear();
surfaceSymmTensorFields_.clear();
surfaceTensorFields_.clear();
}
Foam::label Foam::probes::classifyFields()
{
label nFields = 0;
clearFieldGroups();
HashTable<wordHashSet> available =
(
loadFromFiles_
? IOobjectList(mesh_, mesh_.time().timeName()).classes(fieldSelection_)
: mesh_.classes(fieldSelection_)
);
forAllConstIters(available, iter)
{
const word& fieldType = iter.key();
const wordList fieldNames = iter.val().sortedToc();
const label count = fieldNames.size(); // pre-filtered, so non-empty
if (fieldType == volScalarField::typeName)
{
scalarFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == volVectorField::typeName)
{
vectorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == volSphericalTensorField::typeName)
{
sphericalTensorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == volSymmTensorField::typeName)
{
symmTensorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == volTensorField::typeName)
{
tensorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == surfaceScalarField::typeName)
{
surfaceScalarFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == surfaceVectorField::typeName)
{
surfaceVectorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == surfaceSphericalTensorField::typeName)
{
surfaceSphericalTensorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == surfaceSymmTensorField::typeName)
{
surfaceSymmTensorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == surfaceTensorField::typeName)
{
surfaceTensorFields_.append(fieldNames);
nFields += count;
}
}
return nFields;
}
// ************************************************************************* //

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2021 OpenCFD Ltd.
Copyright (C) 2017-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -38,10 +38,8 @@ namespace Foam
{
template<class T>
class isNotEqOp
struct isNotEqOp
{
public:
void operator()(T& x, const T& y) const
{
const T unsetVal(-VGREAT*pTraits<T>::one);
@ -61,25 +59,87 @@ public:
}
};
} // End namespace Foam
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
template<class GeoField>
Foam::tmp<GeoField>
Foam::probes::getOrLoadField(const word& fieldName) const
{
tmp<GeoField> tfield;
if (loadFromFiles_)
{
tfield.reset
(
new GeoField
(
IOobject
(
fieldName,
mesh_.time().timeName(),
mesh_,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
),
mesh_
)
);
}
else
{
// Slightly paranoid here
tfield.cref(mesh_.cfindObject<GeoField>(fieldName));
}
return tfield;
}
template<class Type>
void Foam::probes::storeResults
(
const word& fieldName,
const Field<Type>& values
)
{
const MinMax<Type> limits(values);
const Type avgVal = average(values);
this->setResult("average(" + fieldName + ")", avgVal);
this->setResult("min(" + fieldName + ")", limits.min());
this->setResult("max(" + fieldName + ")", limits.max());
this->setResult("size(" + fieldName + ")", values.size());
if (verbose_)
{
Info<< name() << " : " << fieldName << nl
<< " avg: " << avgVal << nl
<< " min: " << limits.min() << nl
<< " max: " << limits.max() << nl << nl;
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class Type>
void Foam::probes::sampleAndWrite
void Foam::probes::writeValues
(
const GeometricField<Type, fvPatchField, volMesh>& vField
const word& fieldName,
const Field<Type>& values,
const scalar timeValue
)
{
Field<Type> values(sample(vField));
if (Pstream::master())
{
unsigned int w = IOstream::defaultPrecision() + 7;
OFstream& os = *probeFilePtrs_[vField.name()];
const unsigned int w = IOstream::defaultPrecision() + 7;
OFstream& os = *probeFilePtrs_[fieldName];
os << setw(w) << vField.time().timeOutputValue();
os << setw(w) << timeValue;
forAll(values, probei)
{
@ -90,144 +150,53 @@ void Foam::probes::sampleAndWrite
}
os << endl;
}
const word& fieldName = vField.name();
this->setResult("average(" + fieldName + ")", average(values));
this->setResult("min(" + fieldName + ")", min(values));
this->setResult("max(" + fieldName + ")", max(values));
this->setResult("size(" + fieldName + ")", values.size());
}
template<class Type>
void Foam::probes::sampleAndWrite
template<class GeoField>
void Foam::probes::performAction
(
const GeometricField<Type, fvsPatchField, surfaceMesh>& sField
const fieldGroup<GeoField>& fieldNames,
unsigned request
)
{
Field<Type> values(sample(sField));
if (Pstream::master())
for (const word& fieldName : fieldNames)
{
unsigned int w = IOstream::defaultPrecision() + 7;
OFstream& os = *probeFilePtrs_[sField.name()];
tmp<GeoField> tfield = getOrLoadField<GeoField>(fieldName);
os << setw(w) << sField.time().timeOutputValue();
forAll(values, probei)
if (tfield)
{
if (includeOutOfBounds_ || processor_[probei] != -1)
const auto& field = tfield();
const scalar timeValue = field.time().timeOutputValue();
Field<typename GeoField::value_type> values(sample(field));
this->storeResults(fieldName, values);
if (request & ACTION_WRITE)
{
os << ' ' << setw(w) << values[probei];
}
}
os << endl;
}
const word& fieldName = sField.name();
this->setResult("average(" + fieldName + ")", average(values));
this->setResult("min(" + fieldName + ")", min(values));
this->setResult("max(" + fieldName + ")", max(values));
this->setResult("size(" + fieldName + ")", values.size());
}
template<class Type>
void Foam::probes::sampleAndWrite(const fieldGroup<Type>& fields)
{
typedef GeometricField<Type, fvPatchField, volMesh> VolFieldType;
for (const auto& fieldName : fields)
{
if (loadFromFiles_)
{
sampleAndWrite
(
VolFieldType
(
IOobject
(
fieldName,
mesh_.time().timeName(),
mesh_,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
),
mesh_
)
);
}
else
{
objectRegistry::const_iterator iter = mesh_.find(fieldName);
if (iter.found() && iter()->type() == VolFieldType::typeName)
{
sampleAndWrite(mesh_.lookupObject<VolFieldType>(fieldName));
this->writeValues(fieldName, values, timeValue);
}
}
}
}
template<class Type>
void Foam::probes::sampleAndWriteSurfaceFields(const fieldGroup<Type>& fields)
{
typedef GeometricField<Type, fvsPatchField, surfaceMesh> SurfaceFieldType;
for (const auto& fieldName : fields)
{
if (loadFromFiles_)
{
sampleAndWrite
(
SurfaceFieldType
(
IOobject
(
fieldName,
mesh_.time().timeName(),
mesh_,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
),
mesh_
)
);
}
else
{
objectRegistry::const_iterator iter = mesh_.find(fieldName);
if (iter.found() && iter()->type() == SurfaceFieldType::typeName)
{
sampleAndWrite(mesh_.lookupObject<SurfaceFieldType>(fieldName));
}
}
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::probes::sample
(
const GeometricField<Type, fvPatchField, volMesh>& vField
) const
Foam::probes::sample(const VolumeField<Type>& vField) const
{
const Type unsetVal(-VGREAT*pTraits<Type>::one);
auto tValues = tmp<Field<Type>>::New(Field<Type>(this->size(), unsetVal));
auto& values = tValues.ref();
auto tvalues = tmp<Field<Type>>::New(Field<Type>(this->size(), unsetVal));
auto& values = tvalues.ref();
if (fixedLocations_)
{
autoPtr<interpolation<Type>> interpolator
autoPtr<interpolation<Type>> interpPtr
(
interpolation<Type>::New(interpolationScheme_, vField)
interpolation<Type>::New(samplePointScheme_, vField)
);
forAll(*this, probei)
@ -236,7 +205,7 @@ Foam::probes::sample
{
const vector& position = operator[](probei);
values[probei] = interpolator().interpolate
values[probei] = interpPtr().interpolate
(
position,
elementList_[probei],
@ -259,35 +228,18 @@ Foam::probes::sample
Pstream::listCombineGather(values, isNotEqOp<Type>());
Pstream::listCombineScatter(values);
return tValues;
return tvalues;
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::probes::sample(const word& fieldName) const
{
return sample
(
mesh_.lookupObject<GeometricField<Type, fvPatchField, volMesh>>
(
fieldName
)
);
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::probes::sample
(
const GeometricField<Type, fvsPatchField, surfaceMesh>& sField
) const
Foam::probes::sample(const SurfaceField<Type>& sField) const
{
const Type unsetVal(-VGREAT*pTraits<Type>::one);
auto tValues = tmp<Field<Type>>::New(Field<Type>(this->size(), unsetVal));
auto& values = tValues.ref();
auto tvalues = tmp<Field<Type>>::New(Field<Type>(this->size(), unsetVal));
auto& values = tvalues.ref();
forAll(*this, probei)
{
@ -300,21 +252,24 @@ Foam::probes::sample
Pstream::listCombineGather(values, isNotEqOp<Type>());
Pstream::listCombineScatter(values);
return tValues;
return tvalues;
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::probes::sampleSurfaceFields(const word& fieldName) const
Foam::probes::sample(const word& fieldName) const
{
return sample
(
mesh_.lookupObject<GeometricField<Type, fvsPatchField, surfaceMesh>>
(
fieldName
)
);
return sample(mesh_.lookupObject<VolumeField<Type>>(fieldName));
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::probes::sampleSurfaceField(const word& fieldName) const
{
return sample(mesh_.lookupObject<SurfaceField<Type>>(fieldName));
}
// ************************************************************************* //

View File

@ -36,46 +36,40 @@ Volume3_v_fins
probesFins
{
type probes;
type probes;
libs (sampling);
writeControl timeStep;
writeInterval 1;
interpolationScheme cell;
region v_fins;
setFormat raw;
log true;
valueOutput false;
writeFields false;
region v_fins;
fields ( T );
probeLocations
(
(0.118 0.01 -0.125)
(0.118 0.03 -0.125)
);
fields ( T );
}
probesFluid
{
type probes;
type probes;
libs (sampling);
writeControl timeStep;
writeInterval 1;
interpolationScheme cell;
region domain0;
setFormat raw;
region domain0;
log true;
valueOutput false;
writeFields false;
verbose true;
fields (T U);
probeLocations
(
(0.118 0.035 -0.125)
(0.118 0.07 -0.125)
);
fields ( T U);
}
#remove (_volFieldValue _surfaceFieldValue)