Compare commits

...

2 Commits

20 changed files with 1979 additions and 1333 deletions

View File

@ -76,7 +76,7 @@ Foam::functionObjects::thermoCoupleProbes::thermoCoupleProbes
}
else
{
Ttc_ = probes::sample(thermo_.T());
Ttc_ = prober().sample(thermo_.T());
}
// Note: can only create the solver once all samples have been found
@ -89,7 +89,7 @@ Foam::functionObjects::thermoCoupleProbes::thermoCoupleProbes
Foam::label Foam::functionObjects::thermoCoupleProbes::nEqns() const
{
return this->size();
return prober().size();
}
@ -108,19 +108,21 @@ void Foam::functionObjects::thermoCoupleProbes::derivatives
scalarField Cpc(y.size(), Zero);
scalarField kappac(y.size(), Zero);
const auto& p = prober();
if (radiationFieldName_ != "none")
{
G = sample(mesh_.lookupObject<volScalarField>(radiationFieldName_));
G = p.sample(mesh_.lookupObject<volScalarField>(radiationFieldName_));
}
Tc = probes::sample(thermo_.T());
Tc = p.sample(thermo_.T());
Uc = mag(this->sample(mesh_.lookupObject<volVectorField>(UName_)));
Uc = mag(p.sample(mesh_.lookupObject<volVectorField>(UName_)));
rhoc = this->sample(thermo_.rho()());
kappac = this->sample(thermo_.kappa()());
muc = this->sample(thermo_.mu()());
Cpc = this->sample(thermo_.Cp()());
rhoc = p.sample(thermo_.rho()());
kappac = p.sample(thermo_.kappa()());
muc = p.sample(thermo_.mu()());
Cpc = p.sample(thermo_.Cp()());
scalarField Re(rhoc*Uc*d_/muc);
scalarField Pr(Cpc*muc/kappac);
@ -163,7 +165,7 @@ void Foam::functionObjects::thermoCoupleProbes::jacobian
bool Foam::functionObjects::thermoCoupleProbes::write()
{
if (!pointField::empty())
if (!prober().empty())
{
(void) prepare(ACTION_WRITE);
@ -182,7 +184,7 @@ bool Foam::functionObjects::thermoCoupleProbes::write()
bool Foam::functionObjects::thermoCoupleProbes::execute()
{
if (!pointField::empty())
if (!prober().empty())
{
scalar dt = mesh_.time().deltaTValue();
scalar t = mesh_.time().value();

View File

@ -42,7 +42,8 @@ void Foam::functionObjects::thermoCoupleProbes::writeValues
os << setw(w) << timeValue;
forAll(*this, probei)
const pointField& probes = prober().probeLocations();
forAll(probes, probei)
{
// if (includeOutOfBounds_ || processor_[probei] != -1)
{

View File

@ -1,3 +1,6 @@
probes/pointProbers/pointProberBase.C
probes/pointProbers/internalPointProber.C
probes/pointProbers/patchPointProber.C
probes/probes.C
probes/patchProbes.C

View File

@ -0,0 +1,328 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2015-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
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 "ProbesBase.H"
#include "IOmanip.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class Prober>
void Foam::ProbesBase<Prober>::createProbeFiles(const wordList& fieldNames)
{
// Open new output streams
bool needsNewFiles = false;
for (const word& fieldName : fieldNames)
{
if (!probeFilePtrs_.found(fieldName))
{
needsNewFiles = true;
break;
}
}
if (needsNewFiles && Pstream::master())
{
DebugInfo
<< "Probing fields: " << fieldNames << nl
<< "Probing locations: " << prober_.probeLocations() << nl
<< endl;
// Put in undecomposed case
// (Note: gives problems for distributed data running)
fileName probeDir
(
mesh_.time().globalPath()
/ functionObject::outputPrefix
/ name()/mesh_.regionName()
// Use startTime as the instance for output files
/ mesh_.time().timeName(mesh_.time().startTime().value())
);
probeDir.clean(); // Remove unneeded ".."
// Create directory if needed
Foam::mkDir(probeDir);
for (const word& fieldName : fieldNames)
{
if (probeFilePtrs_.found(fieldName))
{
// Safety
continue;
}
auto osPtr = autoPtr<OFstream>::New(probeDir/fieldName);
auto& os = *osPtr;
probeFilePtrs_.insert(fieldName, osPtr);
DebugInfo<< "open probe stream: " << os.name() << endl;
const unsigned int width(IOstream::defaultPrecision() + 7);
const pointField& probeLocs = prober_.probeLocations();
const labelList& processors = prober_.processors();
const labelList& patchIDList = prober_.patchIDList();
const pointField& oldPoints = prober_.oldPoints();
forAll(probeLocs, probei)
{
os << "# Probe " << probei << ' ' << probeLocs[probei];
if (processors[probei] == -1)
{
os << " # Not Found";
}
else if (probei < patchIDList.size())
{
const label patchi = patchIDList[probei];
if (patchi != -1)
{
const polyBoundaryMesh& bm = mesh_.boundaryMesh();
if
(
patchi < bm.nNonProcessor()
|| processors[probei] == Pstream::myProcNo()
)
{
os << " at patch " << bm[patchi].name();
}
os << " with a distance of "
<< mag(probeLocs[probei]-oldPoints[probei])
<< " m to the original point "
<< oldPoints[probei];
}
}
os << nl;
}
os << '#' << setw(IOstream::defaultPrecision() + 6)
<< "Probe";
forAll(probeLocs, probei)
{
if (prober_.includeOutOfBounds() || processors[probei] != -1)
{
os << ' ' << setw(width) << probei;
}
}
os << nl;
os << '#' << setw(IOstream::defaultPrecision() + 6)
<< "Time" << endl;
}
}
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
template<class Prober>
Foam::label Foam::ProbesBase<Prober>::prepare(unsigned request)
{
// Prefilter on selection
HashTable<wordHashSet> selected =
(
loadFromFiles_
? IOobjectList(mesh_, mesh_.time().timeName()).classes(fieldSelection_)
: mesh_.classes(fieldSelection_)
);
// 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.good()) \
{ \
/* 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(2*nFields);
currentFields.insert(scalarFields_);
currentFields.insert(vectorFields_);
currentFields.insert(sphericalTensorFields_);
currentFields.insert(symmTensorFields_);
currentFields.insert(tensorFields_);
currentFields.insert(surfaceScalarFields_);
currentFields.insert(surfaceVectorFields_);
currentFields.insert(surfaceSphericalTensorFields_);
currentFields.insert(surfaceSymmTensorFields_);
currentFields.insert(surfaceTensorFields_);
DebugInfo
<< "Probing fields: " << currentFields << nl
<< "Probing locations: " << prober_.probeLocations() << nl
<< endl;
// Close streams for fields that no longer exist
forAllIters(probeFilePtrs_, iter)
{
if (!currentFields.erase(iter.key()))
{
DebugInfo<< "close probe stream: " << iter()->name() << endl;
probeFilePtrs_.remove(iter);
}
}
if ((request & ACTION_WRITE) && !currentFields.empty())
{
createProbeFiles(currentFields.sortedToc());
}
}
return nFields;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class Prober>
Foam::ProbesBase<Prober>::ProbesBase
(
const word& name,
const Time& runTime,
const dictionary& dict,
const bool loadFromFiles,
const bool readFields
)
:
functionObjects::fvMeshFunctionObject(name, runTime, dict),
prober_(mesh_, dict),
loadFromFiles_(loadFromFiles),
verbose_(false),
onExecute_(false),
fieldSelection_()
{
if (readFields)
{
read(dict);
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class Prober>
bool Foam::ProbesBase<Prober>::verbose(const bool on) noexcept
{
bool old(verbose_);
verbose_ = on;
return old;
}
template<class Prober>
bool Foam::ProbesBase<Prober>::read(const dictionary& dict)
{
dict.readEntry("fields", fieldSelection_);
verbose_ = dict.getOrDefault("verbose", false);
onExecute_ = dict.getOrDefault("sampleOnExecute", false);
// Close old (ununsed) streams
prepare(ACTION_NONE);
return true;
}
template<class Prober>
bool Foam::ProbesBase<Prober>::performAction(unsigned request)
{
if (!prober_.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;
}
template<class Prober>
bool Foam::ProbesBase<Prober>::execute()
{
if (onExecute_)
{
return performAction(ACTION_ALL & ~ACTION_WRITE);
}
return true;
}
template<class Prober>
bool Foam::ProbesBase<Prober>::write()
{
return performAction(ACTION_ALL);
}
// ************************************************************************* //

View File

@ -0,0 +1,239 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::ProbesBase
Description
Base class for sampling fields at specified locations and writing to file.
The locations are specified and determined in the derived class. The
sampling is done using the specified point prober class.
SourceFiles
ProbesBase.C
ProbesBaseTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_ProbesBase_H
#define Foam_ProbesBase_H
#include "fvMeshFunctionObject.H"
#include "polyMesh.H"
#include "HashPtrTable.H"
#include "OFstream.H"
#include "volFieldsFwd.H"
#include "surfaceFieldsFwd.H"
#include "pointProberBase.H"
#include "IOobjectList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class ProbesBase Declaration
\*---------------------------------------------------------------------------*/
template<class Prober>
class ProbesBase
:
public functionObjects::fvMeshFunctionObject
{
protected:
// Protected Data
//- The specified point prober
Prober prober_;
// Protected Classes
//- 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
{
ACTION_NONE = 0,
ACTION_WRITE = 0x1,
ACTION_STORE = 0x2,
ACTION_ALL = 0xF
};
// Protected Data
//- Load fields from files (not from objectRegistry)
bool loadFromFiles_;
//- Output verbosity
bool verbose_;
//- Perform sample actions on execute as well
bool onExecute_;
//- Requested names of fields to probe
wordRes fieldSelection_;
// 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_;
//- Current open files (non-empty on master only)
HashPtrTable<OFstream> probeFilePtrs_;
// Protected Member Functions
//- 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:
// Private Member Functions
//- Create new streams as required
void createProbeFiles(const wordList& fieldNames);
//- Write field values
template<class Type>
void writeValues
(
const word& fieldName,
const Field<Type>& values,
const scalar timeValue
);
//- Sample and store/write all applicable sampled 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);
public:
// Constructors
//- Construct from Time and dictionary
ProbesBase
(
const word& name,
const Time& runTime,
const dictionary& dict,
const bool loadFromFiles = false,
const bool readFields = true
);
//- Destructor
virtual ~ProbesBase() = default;
// 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 noexcept
{
return fieldSelection_;
}
//- Return const reference to the point prober
const Prober& prober() const noexcept { return prober_; }
//- Read the settings from the dictionary
virtual bool read(const dictionary&);
//- Sample and store result if the sampleOnExecute is enabled.
virtual bool execute();
//- Sample and write
virtual bool write();
//- Update for changes of mesh due to readUpdate
virtual void readUpdate(const polyMesh::readUpdateState state)
{}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "ProbesBase.C"
#include "ProbesBaseTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2022 OpenCFD Ltd.
Copyright (C) 2017-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -26,47 +26,14 @@ License
\*---------------------------------------------------------------------------*/
#include "probes.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "IOmanip.H"
#include "interpolation.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
template<class T>
struct isNotEqOp
{
void operator()(T& x, const T& y) const
{
const T unsetVal(-VGREAT*pTraits<T>::one);
if (x != unsetVal)
{
// Keep x.
// Note: should check for y != unsetVal but multiple sample cells
// already handled in read().
}
else
{
// x is not set. y might be.
x = y;
}
}
};
} // End namespace Foam
#include "ProbesBase.H"
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
template<class Prober>
template<class GeoField>
Foam::tmp<GeoField>
Foam::probes::getOrLoadField(const word& fieldName) const
Foam::ProbesBase<Prober>::getOrLoadField(const word& fieldName) const
{
tmp<GeoField> tfield;
@ -99,8 +66,9 @@ Foam::probes::getOrLoadField(const word& fieldName) const
}
template<class Prober>
template<class Type>
void Foam::probes::storeResults
void Foam::ProbesBase<Prober>::storeResults
(
const word& fieldName,
const Field<Type>& values
@ -126,8 +94,9 @@ void Foam::probes::storeResults
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class Prober>
template<class Type>
void Foam::probes::writeValues
void Foam::ProbesBase<Prober>::writeValues
(
const word& fieldName,
const Field<Type>& values,
@ -141,9 +110,11 @@ void Foam::probes::writeValues
os << setw(width) << timeValue;
const bool includeOutOfBounds = prober_.includeOutOfBounds();
const labelList& procs = prober_.processors();
forAll(values, probei)
{
if (includeOutOfBounds_ || processor_[probei] != -1)
if (includeOutOfBounds || procs[probei] != -1)
{
os << ' ' << setw(width) << values[probei];
}
@ -153,8 +124,9 @@ void Foam::probes::writeValues
}
template<class Prober>
template<class GeoField>
void Foam::probes::performAction
void Foam::ProbesBase<Prober>::performAction
(
const fieldGroup<GeoField>& fieldNames,
unsigned request
@ -169,7 +141,7 @@ void Foam::probes::performAction
const auto& field = tfield();
const scalar timeValue = field.time().timeOutputValue();
Field<typename GeoField::value_type> values(sample(field));
Field<typename GeoField::value_type> values(prober_.sample(field));
this->storeResults(fieldName, values);
if (request & ACTION_WRITE)
@ -181,93 +153,4 @@ void Foam::probes::performAction
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class Type>
Foam::tmp<Foam::Field<Type>>
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();
if (fixedLocations_)
{
autoPtr<interpolation<Type>> interpPtr
(
interpolation<Type>::New(samplePointScheme_, vField)
);
forAll(*this, probei)
{
if (elementList_[probei] >= 0)
{
const vector& position = operator[](probei);
values[probei] = interpPtr().interpolate
(
position,
elementList_[probei],
-1
);
}
}
}
else
{
forAll(*this, probei)
{
if (elementList_[probei] >= 0)
{
values[probei] = vField[elementList_[probei]];
}
}
}
Pstream::listCombineReduce(values, isNotEqOp<Type>());
return tvalues;
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
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();
forAll(*this, probei)
{
if (faceList_[probei] >= 0)
{
values[probei] = sField[faceList_[probei]];
}
}
Pstream::listCombineReduce(values, isNotEqOp<Type>());
return tvalues;
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::probes::sample(const word& fieldName) const
{
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

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -27,11 +27,6 @@ License
\*---------------------------------------------------------------------------*/
#include "patchProbes.H"
#include "volFields.H"
#include "IOmanip.H"
#include "mappedPatchBase.H"
#include "treeBoundBox.H"
#include "treeDataFace.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -39,7 +34,6 @@ License
namespace Foam
{
defineTypeNameAndDebug(patchProbes, 0);
addToRunTimeSelectionTable
(
functionObject,
@ -48,179 +42,6 @@ namespace Foam
);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::patchProbes::findElements(const fvMesh& mesh)
{
(void)mesh.tetBasePtIs();
const polyBoundaryMesh& bm = mesh.boundaryMesh();
// All the info for nearest. Construct to miss
List<mappedPatchBase::nearInfo> nearest(this->size());
const labelList patchIDs(bm.patchSet(patchNames_).sortedToc());
label nFaces = 0;
forAll(patchIDs, i)
{
nFaces += bm[patchIDs[i]].size();
}
if (nFaces > 0)
{
// Collect mesh faces and bounding box
labelList bndFaces(nFaces);
treeBoundBox overallBb;
nFaces = 0;
forAll(patchIDs, i)
{
const polyPatch& pp = bm[patchIDs[i]];
forAll(pp, i)
{
bndFaces[nFaces++] = pp.start()+i;
const face& f = pp[i];
// Without reduction.
overallBb.add(pp.points(), f);
}
}
Random rndGen(123456);
overallBb.inflate(rndGen, 1e-4, ROOTVSMALL);
const indexedOctree<treeDataFace> boundaryTree
(
treeDataFace(mesh, bndFaces), // patch faces only
overallBb, // overall search domain
8, // maxLevel
10, // leafsize
3.0 // duplicity
);
forAll(probeLocations(), probei)
{
const auto& treeData = boundaryTree.shapes();
const point sample = probeLocations()[probei];
pointIndexHit info = boundaryTree.findNearest
(
sample,
Foam::sqr(boundaryTree.bb().mag())
);
if (!info.hit())
{
info = boundaryTree.findNearest(sample, Foam::sqr(GREAT));
}
const label facei = treeData.objectIndex(info.index());
const label patchi = bm.whichPatch(facei);
if (isA<emptyPolyPatch>(bm[patchi]))
{
WarningInFunction
<< " The sample point: " << sample
<< " belongs to " << patchi
<< " which is an empty patch. This is not permitted. "
<< " This sample will not be included "
<< endl;
}
else if (info.hit())
{
// Note: do we store the face centre or the actual nearest?
// We interpolate using the faceI only though (no
// interpolation) so it does not actually matter much, just for
// the location written to the header.
//const point& facePt = mesh.faceCentres()[faceI];
const point& facePt = info.point();
mappedPatchBase::nearInfo sampleInfo;
sampleInfo.first() = pointIndexHit(true, facePt, facei);
sampleInfo.second().first() = facePt.distSqr(sample);
sampleInfo.second().second() = Pstream::myProcNo();
nearest[probei] = sampleInfo;
}
}
}
// Find nearest - globally consistent
Pstream::listCombineReduce(nearest, mappedPatchBase::nearestEqOp());
oldPoints_.resize(this->size());
// Update actual probe locations and store old ones
forAll(nearest, samplei)
{
oldPoints_[samplei] = operator[](samplei);
operator[](samplei) = nearest[samplei].first().point();
}
if (debug)
{
InfoInFunction << nl;
forAll(nearest, samplei)
{
label proci = nearest[samplei].second().second();
label locali = nearest[samplei].first().index();
Info<< " " << samplei << " coord:"<< operator[](samplei)
<< " found on processor:" << proci
<< " in local face:" << locali
<< " with location:" << nearest[samplei].first().point()
<< endl;
}
}
// Extract any local faces to sample:
// - operator[] : actual point to sample (=nearest point on patch)
// - oldPoints_ : original provided point (might be anywhere in the mesh)
// - elementList_ : cells, not used
// - faceList_ : faces (now patch faces)
// - patchIDList_ : patch corresponding to faceList
// - processor_ : processor
elementList_.resize_nocopy(nearest.size());
elementList_ = -1;
faceList_.resize_nocopy(nearest.size());
faceList_ = -1;
processor_.resize_nocopy(nearest.size());
processor_ = -1;
patchIDList_.resize_nocopy(nearest.size());
patchIDList_ = -1;
forAll(nearest, sampleI)
{
processor_[sampleI] = nearest[sampleI].second().second();
if (nearest[sampleI].second().second() == Pstream::myProcNo())
{
// Store the face to sample
faceList_[sampleI] = nearest[sampleI].first().index();
const label facei = faceList_[sampleI];
if (facei != -1)
{
processor_[sampleI] = Pstream::myProcNo();
patchIDList_[sampleI] = bm.whichPatch(facei);
}
}
reduce(processor_[sampleI], maxOp<label>());
reduce(patchIDList_[sampleI], maxOp<label>());
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::patchProbes::patchProbes
@ -232,7 +53,14 @@ Foam::patchProbes::patchProbes
const bool readFields
)
:
probes(name, runTime, dict, loadFromFiles, false)
Base
(
name,
runTime,
dict,
loadFromFiles,
readFields
)
{
if (readFields)
{
@ -243,52 +71,13 @@ Foam::patchProbes::patchProbes
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::patchProbes::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::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_))
if (!(ProbesBase::read(dict) && prober_.read(dict)))
{
patchNames_.resize(1);
patchNames_.first() = dict.get<word>("patch");
return false;
}
return probes::read(dict);
return true;
}

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,52 +28,71 @@ Class
Foam::patchProbes
Description
Set of locations to sample at patches
Probes the specified points on specified patches. The points get snapped
onto the nearest point on the nearest face of the specified patch, and the
sampling is actioned on the snapped locations.
Call write() to sample and write files.
- find nearest location on nearest face
- update *this with location (so header contains 'snapped' locations
- use *this as the sampling location
Example of function object specification:
Usage
Minimal example by using \c system/controlDict.functions:
\verbatim
patchProbes
{
// Mandatory entries
type patchProbes;
libs (sampling);
// Name of the directory for probe data
name patchProbes;
fields (<wordRes>);
probeLocations (<vectorList>);
patches (<wordRes>); // or patch <word>;
// Patches to sample (wildcards allowed)
patches (".*inl.*");
// Optional entries
verbose <bool>;
sampleOnExecute <bool>;
fixedLocations <bool>;
includeOutOfBounds <bool>;
interpolationScheme <word>;
// Write at same frequency as fields
writeControl writeTime;
writeInterval 1;
// Fields to be probed
fields (p U);
// Locations to probe. These get snapped onto the nearest point
// on the selected patches
probeLocations
(
( -100 0 0.01 ) // at inlet
);
...
}
\endverbatim
where the entries mean:
\table
Property | Description | Type | Reqd | Deflt
type | Type name: patchProbes | word | yes | -
libs | Library name: sampling | word | yes | -
fields | Names of the fields to be probed | wordRes | yes | -
probeLocations | Locations of the probes | vectorField | yes | -
patches | Patches to sample (wildcards allowed) | wordRes | yes | -
verbose | Enable/disable verbose output | bool | no | false
sampleOnExecute | Sample on execution and store results | bool | no <!--
--> | false
fixedLocations | Do not recalculate cells if mesh moves | bool | no | true
includeOutOfBounds | Include out-of-bounds locations | bool | no | true
interpolationScheme | Scheme to obtain values at the points | word <!--
--> | no | cell
\endtable
The inherited entries are elaborated in:
- \link fvMeshFunctionObject.H \endlink
- \link ProbesBase.H \endlink
- \link patchPointProber.H \endlink
- \link pointProberBase.H \endlink
Note
- The \c includeOutOfBounds filters out points that haven't been found.
Default is to include them (with value \c -VGREAT).
SourceFiles
patchProbes.C
patchProbesTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_patchProbes_H
#define Foam_patchProbes_H
#include "probes.H"
#include "ProbesBase.H"
#include "patchPointProber.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -81,57 +100,17 @@ namespace Foam
{
/*---------------------------------------------------------------------------*\
Class patchProbes Declaration
Class patchProbes Declaration
\*---------------------------------------------------------------------------*/
class patchProbes
:
public probes
public ProbesBase<patchPointProber>
{
protected:
// Private Data
// Protected Data
//- Patches to sample
wordRes patchNames_;
// Protected Member Functions
//- Find elements containing patchProbes
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;
//- No copy assignment
void operator=(const patchProbes&) = delete;
//- Use simpler synonym for the base type
using Base = ProbesBase<patchPointProber>;
public:
@ -142,7 +121,7 @@ public:
// Constructors
//- Construct from Time and dictionary
//- Construct from name, Time and dictionary
patchProbes
(
const word& name,
@ -152,54 +131,26 @@ public:
const bool readFields = true
);
//- Destructor
virtual ~patchProbes() = default;
// Member Functions
//- Sample and store result if the sampleOnExecute is enabled.
virtual bool execute();
//- Bring Base::prober into this class's public scope.
using Base::prober;
//- Sample and write
virtual bool write();
//- Read
//- Read the settings from the dictionary
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;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "patchProbesTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,186 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
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 "internalPointProber.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(internalPointProber, 0);
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
void Foam::internalPointProber::findElements(const fvMesh& mesh)
{
DebugInfo<< "internalPointProber: resetting sample locations" << endl;
elementList_.resize_nocopy(pointField::size());
faceList_.resize_nocopy(pointField::size());
processor_.resize_nocopy(pointField::size());
processor_ = -1;
forAll(*this, probei)
{
const point& location = (*this)[probei];
const label celli = mesh.findCell(location);
elementList_[probei] = celli;
if (celli != -1)
{
const labelList& cellFaces = mesh.cells()[celli];
const vector& cellCentre = mesh.cellCentres()[celli];
scalar minDistance = GREAT;
label minFaceID = -1;
forAll(cellFaces, i)
{
label facei = cellFaces[i];
vector dist = mesh.faceCentres()[facei] - cellCentre;
if (mag(dist) < minDistance)
{
minDistance = mag(dist);
minFaceID = facei;
}
}
faceList_[probei] = minFaceID;
}
else
{
faceList_[probei] = -1;
}
if (debug && (elementList_[probei] != -1 || faceList_[probei] != -1))
{
Pout<< "internalPointProber : found point " << location
<< " in cell " << elementList_[probei]
<< " and face " << faceList_[probei] << endl;
}
}
// Check if all probes have been found.
forAll(elementList_, probei)
{
const point& location = operator[](probei);
label celli = elementList_[probei];
label facei = faceList_[probei];
processor_[probei] = (celli != -1 ? Pstream::myProcNo() : -1);
// Check at least one processor with cell.
reduce(celli, maxOp<label>());
reduce(facei, maxOp<label>());
reduce(processor_[probei], maxOp<label>());
if (celli == -1)
{
if (Pstream::master())
{
WarningInFunction
<< "Did not find location " << location
<< " in any cell. Skipping location." << endl;
}
}
else if (facei == -1)
{
if (Pstream::master())
{
WarningInFunction
<< "Did not find location " << location
<< " in any face. Skipping location." << endl;
}
}
else
{
// Make sure location not on two domains.
if (elementList_[probei] != -1 && elementList_[probei] != celli)
{
WarningInFunction
<< "Location " << location
<< " seems to be on multiple domains:"
<< " cell " << elementList_[probei]
<< " on my domain " << Pstream::myProcNo()
<< " and cell " << celli << " on some other domain."
<< nl
<< "This might happen if the probe location is on"
<< " a processor patch. Change the location slightly"
<< " to prevent this." << endl;
}
if (faceList_[probei] != -1 && faceList_[probei] != facei)
{
WarningInFunction
<< "Location " << location
<< " seems to be on multiple domains:"
<< " cell " << faceList_[probei]
<< " on my domain " << Pstream::myProcNo()
<< " and face " << facei << " on some other domain."
<< nl
<< "This might happen if the probe location is on"
<< " a processor patch. Change the location slightly"
<< " to prevent this." << endl;
}
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::internalPointProber::internalPointProber
(
const fvMesh& mesh,
const dictionary& dict
)
:
pointProberBase(mesh, dict)
{
read(dict);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::internalPointProber::read(const dictionary& dict)
{
if (!pointProberBase::read(dict))
{
return false;
}
// Initialise cells to sample from supplied locations
findElements(mesh_);
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,140 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::internalPointProber
Description
A utility class for probing field values at specified point locations
within an \c fvMesh.
The \c internalPointProber stores a list of 3D point coordinates and
determines the corresponding mesh elements (cells or faces) that contain
these points. It provides methods to sample volume or surface fields at
the stored locations, with support for fixed or mesh-moving point
coordinates.
Features include:
- Reading probe locations and settings from a dictionary
- Support for fixed or moving locations (for dynamic mesh cases)
- Optional inclusion of points that lie outside of the mesh domain
- Selection of interpolation/sampling schemes for fixed locations
- Sampling of volume and surface fields by name or by direct reference
- Automatic update of element mapping when the mesh changes or moves
SourceFiles
internalPointProber.C
internalPointProberTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_internalPointProber_H
#define Foam_internalPointProber_H
#include "pointProberBase.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class internalPointProber Declaration
\*---------------------------------------------------------------------------*/
class internalPointProber
:
public pointProberBase
{
protected:
// Protected Member Functions
//- Find cells and faces containing probes
virtual void findElements(const fvMesh& mesh);
public:
//- Runtime type information
TypeName("internalPointProber");
// Constructors
//- Construct from Time and dictionary
internalPointProber
(
const fvMesh& mesh,
const dictionary& dict
);
//- Destructor
virtual ~internalPointProber() = default;
// Member Functions
// 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 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;
// I-O
//- Read the settings dictionary
virtual bool read(const dictionary&);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "internalPointProberTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
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 "internalPointProber.H"
#include "interpolation.H"
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::internalPointProber::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();
if (fixedLocations_)
{
autoPtr<interpolation<Type>> interpPtr
(
interpolation<Type>::New(samplePointScheme_, vField)
);
forAll(*this, probei)
{
if (elementList_[probei] >= 0)
{
const vector& position = operator[](probei);
values[probei] = interpPtr().interpolate
(
position,
elementList_[probei],
-1
);
}
}
}
else
{
forAll(*this, probei)
{
if (elementList_[probei] >= 0)
{
values[probei] = vField[elementList_[probei]];
}
}
}
Pstream::listCombineReduce(values, isNotEqOp<Type>());
return tvalues;
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::internalPointProber::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();
forAll(*this, probei)
{
if (faceList_[probei] >= 0)
{
values[probei] = sField[faceList_[probei]];
}
}
Pstream::listCombineReduce(values, isNotEqOp<Type>());
return tvalues;
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::internalPointProber::sample(const word& fieldName) const
{
return sample(mesh_.lookupObject<VolumeField<Type>>(fieldName));
}
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::internalPointProber::sampleSurfaceField(const word& fieldName) const
{
return sample(mesh_.lookupObject<SurfaceField<Type>>(fieldName));
}
// ************************************************************************* //

View File

@ -0,0 +1,251 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
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 "patchPointProber.H"
#include "mappedPatchBase.H"
#include "treeBoundBox.H"
#include "treeDataFace.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(patchPointProber, 0);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::patchPointProber::findElements(const fvMesh& mesh)
{
(void)mesh.tetBasePtIs();
const polyBoundaryMesh& bm = mesh.boundaryMesh();
// All the info for nearest. Construct to miss
List<mappedPatchBase::nearInfo> nearest(this->size());
patchIDs_ = bm.patchSet(patchNames_).sortedToc();
label nFaces = 0;
forAll(patchIDs_, i)
{
nFaces += bm[patchIDs_[i]].size();
}
if (nFaces > 0)
{
// Collect mesh faces and bounding box
labelList bndFaces(nFaces);
treeBoundBox overallBb;
nFaces = 0;
forAll(patchIDs_, i)
{
const polyPatch& pp = bm[patchIDs_[i]];
forAll(pp, i)
{
bndFaces[nFaces++] = pp.start()+i;
const face& f = pp[i];
// Without reduction.
overallBb.add(pp.points(), f);
}
}
Random rndGen(123456);
overallBb.inflate(rndGen, 1e-4, ROOTVSMALL);
const indexedOctree<treeDataFace> boundaryTree
(
treeDataFace(mesh, bndFaces), // patch faces only
overallBb, // overall search domain
8, // maxLevel
10, // leafsize
3.0 // duplicity
);
forAll(probeLocations(), probei)
{
const auto& treeData = boundaryTree.shapes();
const point sample = probeLocations()[probei];
pointIndexHit info = boundaryTree.findNearest
(
sample,
Foam::sqr(boundaryTree.bb().mag())
);
if (!info.hit())
{
info = boundaryTree.findNearest(sample, Foam::sqr(GREAT));
}
const label facei = treeData.objectIndex(info.index());
const label patchi = bm.whichPatch(facei);
if (isA<emptyPolyPatch>(bm[patchi]))
{
WarningInFunction
<< " The sample point: " << sample
<< " belongs to " << patchi
<< " which is an empty patch. This is not permitted. "
<< " This sample will not be included "
<< endl;
}
else if (info.hit())
{
// Note: do we store the face centre or the actual nearest?
// We interpolate using the faceI only though (no
// interpolation) so it does not actually matter much, just for
// the location written to the header.
//const point& facePt = mesh.faceCentres()[faceI];
const point& facePt = info.point();
mappedPatchBase::nearInfo sampleInfo;
sampleInfo.first() = pointIndexHit(true, facePt, facei);
sampleInfo.second().first() = facePt.distSqr(sample);
sampleInfo.second().second() = Pstream::myProcNo();
nearest[probei] = sampleInfo;
}
}
}
// Find nearest - globally consistent
Pstream::listCombineReduce(nearest, mappedPatchBase::nearestEqOp());
oldPoints_.resize(this->size());
// Update actual probe locations and store old ones
forAll(nearest, samplei)
{
oldPoints_[samplei] = operator[](samplei);
operator[](samplei) = nearest[samplei].first().point();
}
if (debug)
{
InfoInFunction << nl;
forAll(nearest, samplei)
{
label proci = nearest[samplei].second().second();
label locali = nearest[samplei].first().index();
Info<< " " << samplei << " coord:"<< operator[](samplei)
<< " found on processor:" << proci
<< " in local face:" << locali
<< " with location:" << nearest[samplei].first().point()
<< endl;
}
}
// Extract any local faces to sample:
// - operator[] : actual point to sample (=nearest point on patch)
// - oldPoints_ : original provided point (might be anywhere in the mesh)
// - elementList_ : cells, not used
// - faceList_ : faces (now patch faces)
// - patchIDList_ : patch corresponding to faceList
// - processor_ : processor
elementList_.resize_nocopy(nearest.size());
elementList_ = -1;
faceList_.resize_nocopy(nearest.size());
faceList_ = -1;
processor_.resize_nocopy(nearest.size());
processor_ = -1;
patchIDList_.resize_nocopy(nearest.size());
patchIDList_ = -1;
forAll(nearest, sampleI)
{
processor_[sampleI] = nearest[sampleI].second().second();
if (nearest[sampleI].second().second() == Pstream::myProcNo())
{
// Store the face to sample
faceList_[sampleI] = nearest[sampleI].first().index();
const label facei = faceList_[sampleI];
if (facei != -1)
{
processor_[sampleI] = Pstream::myProcNo();
patchIDList_[sampleI] = bm.whichPatch(facei);
}
}
reduce(processor_[sampleI], maxOp<label>());
reduce(patchIDList_[sampleI], maxOp<label>());
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::patchPointProber::patchPointProber
(
const fvMesh& mesh,
const dictionary& dict
)
:
pointProberBase(mesh, dict)
{
read(dict);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::patchPointProber::read(const dictionary& dict)
{
if (!pointProberBase::read(dict))
{
return false;
}
if (!dict.readIfPresent("patches", patchNames_))
{
patchNames_.resize(1);
patchNames_.first() = dict.get<word>("patch");
}
// Initialise cells to sample from supplied locations
findElements(mesh_);
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,152 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::patchPointProber
Description
Utility class for probing specified points on user-selected boundary
patches. The input points are projected onto the nearest point of the
nearest face on the specified patch, ensuring sampling occurs at valid
patch locations.
The patchPointProber enables sampling of both volume and surface fields
at these snapped locations. Patch selection is controlled via patch names or
indices, and the class provides runtime selection and dictionary-driven
configuration.
Typical usage involves specifying patch names and probe locations in a
dictionary, after which the class manages the mapping and sampling
operations.
SourceFiles
patchPointProber.C
patchPointProberTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_patchPointProber_H
#define Foam_patchPointProber_H
#include "pointProberBase.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class patchPointProber Declaration
\*---------------------------------------------------------------------------*/
class patchPointProber
:
public pointProberBase
{
protected:
// Protected Data
//- Names of the patches to sample
wordRes patchNames_;
//- Index of the patches to sample
labelList patchIDs_;
// Protected Member Functions
//- Find elements containing patchPointProber
virtual void findElements(const fvMesh& mesh);
public:
//- Runtime type information
TypeName("patchPointProber");
// Constructors
//- Construct from Time and dictionary
patchPointProber
(
const fvMesh& mesh,
const dictionary& dict
);
//- Destructor
virtual ~patchPointProber() = default;
// Member Functions
// Access
//- Return the index of the patches to sample
const labelList& patchIDs() const noexcept { return patchIDs_; }
// 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 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;
// I-O
//- Read the settings dictionary
virtual bool read(const dictionary&);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "patchPointProberTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -5,8 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2021-2022 OpenCFD Ltd.
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -26,70 +25,15 @@ License
\*---------------------------------------------------------------------------*/
#include "patchProbes.H"
#include "patchPointProber.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "IOmanip.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class Type>
void Foam::patchProbes::writeValues
(
const word& fieldName,
const Field<Type>& values,
const scalar timeValue
)
{
if (Pstream::master())
{
const unsigned int w = IOstream::defaultPrecision() + 7;
OFstream& os = *probeFilePtrs_[fieldName];
os << setw(w) << timeValue;
for (const auto& v : values)
{
os << ' ' << setw(w) << v;
}
os << endl;
}
}
template<class GeoField>
void Foam::patchProbes::performAction
(
const fieldGroup<GeoField>& fieldNames,
unsigned request
)
{
for (const word& fieldName : fieldNames)
{
tmp<GeoField> tfield = getOrLoadField<GeoField>(fieldName);
if (tfield)
{
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)
{
this->writeValues(fieldName, values, timeValue);
}
}
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::patchProbes::sample(const VolumeField<Type>& vField) const
Foam::patchPointProber::sample(const VolumeField<Type>& vField) const
{
const Type unsetVal(-VGREAT*pTraits<Type>::one);
@ -119,7 +63,7 @@ Foam::patchProbes::sample(const VolumeField<Type>& vField) const
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::patchProbes::sample(const SurfaceField<Type>& sField) const
Foam::patchPointProber::sample(const SurfaceField<Type>& sField) const
{
const Type unsetVal(-VGREAT*pTraits<Type>::one);
@ -149,7 +93,7 @@ Foam::patchProbes::sample(const SurfaceField<Type>& sField) const
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::patchProbes::sample(const word& fieldName) const
Foam::patchPointProber::sample(const word& fieldName) const
{
return sample(mesh_.lookupObject<VolumeField<Type>>(fieldName));
}
@ -157,7 +101,7 @@ Foam::patchProbes::sample(const word& fieldName) const
template<class Type>
Foam::tmp<Foam::Field<Type>>
Foam::patchProbes::sampleSurfaceField(const word& fieldName) const
Foam::patchPointProber::sampleSurfaceField(const word& fieldName) const
{
return sample(mesh_.lookupObject<SurfaceField<Type>>(fieldName));
}

View File

@ -0,0 +1,182 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
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 "pointProberBase.H"
#include "mapPolyMesh.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(pointProberBase, 0);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::pointProberBase::pointProberBase
(
const fvMesh& mesh,
const dictionary& dict
)
:
pointField(),
mesh_(mesh),
samplePointScheme_("cell")
{
read(dict);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::pointProberBase::read(const dictionary& dict)
{
dict.readEntry("probeLocations", static_cast<pointField&>(*this));
fixedLocations_ = dict.getOrDefault<bool>("fixedLocations", true);
includeOutOfBounds_ = dict.getOrDefault<bool>("includeOutOfBounds", true);
if (dict.readIfPresent("interpolationScheme", samplePointScheme_))
{
if (!fixedLocations_ && samplePointScheme_ != "cell")
{
WarningInFunction
<< "Only cell interpolation can be applied when "
<< "not using fixedLocations. InterpolationScheme "
<< "entry will be ignored"
<< endl;
}
}
return true;
}
void Foam::pointProberBase::updateMesh(const mapPolyMesh& mpm)
{
DebugInfo<< "probes: updateMesh" << endl;
if (&mpm.mesh() != &mesh_)
{
return;
}
if (fixedLocations_)
{
this->findElements(mesh_);
}
else
{
DebugInfo<< "probes: remapping sample locations" << endl;
// 1. Update cells
{
DynamicList<label> elems(elementList_.size());
const labelList& reverseMap = mpm.reverseCellMap();
forAll(elementList_, i)
{
label celli = elementList_[i];
if (celli != -1)
{
label newCelli = reverseMap[celli];
if (newCelli == -1)
{
// cell removed
}
else if (newCelli < -1)
{
// cell merged
elems.append(-newCelli - 2);
}
else
{
// valid new cell
elems.append(newCelli);
}
}
else
{
// Keep -1 elements so the size stays the same
elems.append(-1);
}
}
elementList_.transfer(elems);
}
// 2. Update faces
{
DynamicList<label> elems(faceList_.size());
const labelList& reverseMap = mpm.reverseFaceMap();
for (const label facei : faceList_)
{
if (facei != -1)
{
label newFacei = reverseMap[facei];
if (newFacei == -1)
{
// face removed
}
else if (newFacei < -1)
{
// face merged
elems.append(-newFacei - 2);
}
else
{
// valid new face
elems.append(newFacei);
}
}
else
{
// Keep -1 elements
elems.append(-1);
}
}
faceList_.transfer(elems);
}
}
}
void Foam::pointProberBase::movePoints(const polyMesh& mesh)
{
DebugInfo<< "probes: movePoints" << endl;
if (fixedLocations_ && &mesh == &mesh_)
{
this->findElements(mesh_);
}
}
// ************************************************************************* //

View File

@ -0,0 +1,213 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::patchPointProber
Description
Base class for sampling fields at specified internal and boundary locations.
SourceFiles
pointProberBase.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_pointProberBase_H
#define Foam_pointProberBase_H
#include "fvMesh.H"
#include "pointField.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class pointProberBase Declaration
\*---------------------------------------------------------------------------*/
class pointProberBase
:
public pointField
{
protected:
template<class T>
struct isNotEqOp
{
void operator()(T& x, const T& y) const
{
const T unsetVal(-VGREAT*pTraits<T>::one);
if (x != unsetVal)
{
// Keep x.
// Note: should check for y != unsetVal but multiple sample cells
// already handled in read().
}
else
{
// x is not set. y might be.
x = y;
}
}
};
// Protected Data
//- Const reference to the mesh
const fvMesh& mesh_;
//- Fixed locations (default: true)
// Note: set to false for moving mesh calculations where locations
// should move with the mesh
bool fixedLocations_;
//- Include probes that were not found (default: true)
bool includeOutOfBounds_;
//- Interpolation/sample scheme to obtain values at the points
// Note: only possible when fixedLocations_ is true
word samplePointScheme_;
// Calculated
//- Cells to be probed (obtained from the locations)
labelList elementList_;
//- Faces to be probed
labelList faceList_;
//- Processor holding the cell or face (-1 if point not found
//- on any processor)
labelList processor_;
//- Patch IDs on which the new probes are located
labelList patchIDList_;
//- Original probes location
pointField oldPoints_;
// Protected Member Functions
//- Find cells and faces containing probes
virtual void findElements(const fvMesh& mesh) = 0;
public:
//- Runtime type information
TypeName("pointProberBase");
// Generated Methods
//- No copy construct
pointProberBase(const pointProberBase&) = delete;
//- No copy assignment
void operator=(const pointProberBase&) = delete;
// Constructors
//- Construct from Time and dictionary
pointProberBase
(
const fvMesh& mesh,
const dictionary& dict
);
//- Destructor
virtual ~pointProberBase() = default;
// Member Functions
// Access
//- Return true if no probe locations
bool empty() const { return pointField::empty(); }
//- Return true if fixed locations
bool fixedLocations() const { return fixedLocations_; }
//- Return true if include out of bounds probes
bool includeOutOfBounds() const { return includeOutOfBounds_; }
//- Return the interpolation scheme to obtain values at the points
// Note: only possible when fixedLocations_ is true
const word& samplePointScheme() const { return samplePointScheme_; }
//- Return const reference to the probe locations
const pointField& probeLocations() const { return *this; }
//- Return the location of probe i
const point& probe(const label i) const { return operator[](i); }
//- Cells to be probed (obtained from the locations)
const labelList& elements() const { return elementList_; }
//- Return const reference to the faces to be probed
const labelList& faces() const { return faceList_; }
//- Return const reference to the processor list
const labelList& processors() const { return processor_; }
//- Return const reference to the patch ID list
const labelList& patchIDList() const noexcept { return patchIDList_; }
//- Return const reference to the original probe locations
const pointField& oldPoints() const noexcept { return oldPoints_; }
// I-O
//- Read the settings dictionary
virtual bool read(const dictionary&);
//- Update for changes of mesh
virtual void updateMesh(const mapPolyMesh&);
//- Update for changes of mesh
virtual void movePoints(const polyMesh&);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2015-2022 OpenCFD Ltd.
Copyright (C) 2015-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -27,12 +27,6 @@ License
\*---------------------------------------------------------------------------*/
#include "probes.H"
#include "dictionary.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "Time.H"
#include "IOmanip.H"
#include "mapPolyMesh.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -40,7 +34,6 @@ License
namespace Foam
{
defineTypeNameAndDebug(probes, 0);
addToRunTimeSelectionTable
(
functionObject,
@ -49,318 +42,6 @@ namespace Foam
);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::probes::createProbeFiles(const wordList& fieldNames)
{
// Open new output streams
bool needsNewFiles = false;
for (const word& fieldName : fieldNames)
{
if (!probeFilePtrs_.found(fieldName))
{
needsNewFiles = true;
break;
}
}
if (needsNewFiles && Pstream::master())
{
DebugInfo
<< "Probing fields: " << fieldNames << nl
<< "Probing locations: " << *this << nl
<< endl;
// Put in undecomposed case
// (Note: gives problems for distributed data running)
fileName probeDir
(
mesh_.time().globalPath()
/ functionObject::outputPrefix
/ name()/mesh_.regionName()
// Use startTime as the instance for output files
/ mesh_.time().timeName(mesh_.time().startTime().value())
);
probeDir.clean(); // Remove unneeded ".."
// Create directory if needed
Foam::mkDir(probeDir);
for (const word& fieldName : fieldNames)
{
if (probeFilePtrs_.found(fieldName))
{
// Safety
continue;
}
auto osPtr = autoPtr<OFstream>::New(probeDir/fieldName);
auto& os = *osPtr;
probeFilePtrs_.insert(fieldName, osPtr);
DebugInfo<< "open probe stream: " << os.name() << endl;
const unsigned int width(IOstream::defaultPrecision() + 7);
forAll(*this, probei)
{
os << "# Probe " << probei << ' ' << operator[](probei);
if (processor_[probei] == -1)
{
os << " # Not Found";
}
// Only for patchProbes
else if (probei < patchIDList_.size())
{
const label patchi = patchIDList_[probei];
if (patchi != -1)
{
const polyBoundaryMesh& bm = mesh_.boundaryMesh();
if
(
patchi < bm.nNonProcessor()
|| processor_[probei] == Pstream::myProcNo()
)
{
os << " at patch " << bm[patchi].name();
}
os << " with a distance of "
<< mag(operator[](probei)-oldPoints_[probei])
<< " m to the original point "
<< oldPoints_[probei];
}
}
os << nl;
}
os << '#' << setw(IOstream::defaultPrecision() + 6)
<< "Probe";
forAll(*this, probei)
{
if (includeOutOfBounds_ || processor_[probei] != -1)
{
os << ' ' << setw(width) << probei;
}
}
os << nl;
os << '#' << setw(IOstream::defaultPrecision() + 6)
<< "Time" << endl;
}
}
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
void Foam::probes::findElements(const fvMesh& mesh)
{
DebugInfo<< "probes: resetting sample locations" << endl;
elementList_.resize_nocopy(pointField::size());
faceList_.resize_nocopy(pointField::size());
processor_.resize_nocopy(pointField::size());
processor_ = -1;
forAll(*this, probei)
{
const point& location = (*this)[probei];
const label celli = mesh.findCell(location);
elementList_[probei] = celli;
if (celli != -1)
{
const labelList& cellFaces = mesh.cells()[celli];
const vector& cellCentre = mesh.cellCentres()[celli];
scalar minDistance = GREAT;
label minFaceID = -1;
forAll(cellFaces, i)
{
label facei = cellFaces[i];
vector dist = mesh.faceCentres()[facei] - cellCentre;
if (mag(dist) < minDistance)
{
minDistance = mag(dist);
minFaceID = facei;
}
}
faceList_[probei] = minFaceID;
}
else
{
faceList_[probei] = -1;
}
if (debug && (elementList_[probei] != -1 || faceList_[probei] != -1))
{
Pout<< "probes : found point " << location
<< " in cell " << elementList_[probei]
<< " and face " << faceList_[probei] << endl;
}
}
// Check if all probes have been found.
forAll(elementList_, probei)
{
const point& location = operator[](probei);
label celli = elementList_[probei];
label facei = faceList_[probei];
processor_[probei] = (celli != -1 ? Pstream::myProcNo() : -1);
// Check at least one processor with cell.
reduce(celli, maxOp<label>());
reduce(facei, maxOp<label>());
reduce(processor_[probei], maxOp<label>());
if (celli == -1)
{
if (Pstream::master())
{
WarningInFunction
<< "Did not find location " << location
<< " in any cell. Skipping location." << endl;
}
}
else if (facei == -1)
{
if (Pstream::master())
{
WarningInFunction
<< "Did not find location " << location
<< " in any face. Skipping location." << endl;
}
}
else
{
// Make sure location not on two domains.
if (elementList_[probei] != -1 && elementList_[probei] != celli)
{
WarningInFunction
<< "Location " << location
<< " seems to be on multiple domains:"
<< " cell " << elementList_[probei]
<< " on my domain " << Pstream::myProcNo()
<< " and cell " << celli << " on some other domain."
<< nl
<< "This might happen if the probe location is on"
<< " a processor patch. Change the location slightly"
<< " to prevent this." << endl;
}
if (faceList_[probei] != -1 && faceList_[probei] != facei)
{
WarningInFunction
<< "Location " << location
<< " seems to be on multiple domains:"
<< " cell " << faceList_[probei]
<< " on my domain " << Pstream::myProcNo()
<< " and face " << facei << " on some other domain."
<< nl
<< "This might happen if the probe location is on"
<< " a processor patch. Change the location slightly"
<< " to prevent this." << endl;
}
}
}
}
Foam::label Foam::probes::prepare(unsigned request)
{
// Prefilter on selection
HashTable<wordHashSet> selected =
(
loadFromFiles_
? IOobjectList(mesh_, mesh_.time().timeName()).classes(fieldSelection_)
: mesh_.classes(fieldSelection_)
);
// 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.good()) \
{ \
/* 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(2*nFields);
currentFields.insert(scalarFields_);
currentFields.insert(vectorFields_);
currentFields.insert(sphericalTensorFields_);
currentFields.insert(symmTensorFields_);
currentFields.insert(tensorFields_);
currentFields.insert(surfaceScalarFields_);
currentFields.insert(surfaceVectorFields_);
currentFields.insert(surfaceSphericalTensorFields_);
currentFields.insert(surfaceSymmTensorFields_);
currentFields.insert(surfaceTensorFields_);
DebugInfo
<< "Probing fields: " << currentFields << nl
<< "Probing locations: " << *this << nl
<< endl;
// Close streams for fields that no longer exist
forAllIters(probeFilePtrs_, iter)
{
if (!currentFields.erase(iter.key()))
{
DebugInfo<< "close probe stream: " << iter()->name() << endl;
probeFilePtrs_.remove(iter);
}
}
if ((request & ACTION_WRITE) && !currentFields.empty())
{
createProbeFiles(currentFields.sortedToc());
}
}
return nFields;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::probes::probes
@ -372,15 +53,14 @@ Foam::probes::probes
const bool readFields
)
:
functionObjects::fvMeshFunctionObject(name, runTime, dict),
pointField(),
loadFromFiles_(loadFromFiles),
fixedLocations_(true),
includeOutOfBounds_(true),
verbose_(false),
onExecute_(false),
fieldSelection_(),
samplePointScheme_("cell")
Base
(
name,
runTime,
dict,
loadFromFiles,
readFields
)
{
if (readFields)
{
@ -391,184 +71,14 @@ 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_);
dict.readIfPresent("includeOutOfBounds", includeOutOfBounds_);
verbose_ = dict.getOrDefault("verbose", false);
onExecute_ = dict.getOrDefault("sampleOnExecute", false);
if (dict.readIfPresent("interpolationScheme", samplePointScheme_))
if (!(ProbesBase::read(dict) && prober_.read(dict)))
{
if (!fixedLocations_ && samplePointScheme_ != "cell")
{
WarningInFunction
<< "Only cell interpolation can be applied when "
<< "not using fixedLocations. InterpolationScheme "
<< "entry will be ignored"
<< endl;
}
}
// Initialise cells to sample from supplied locations
findElements(mesh_);
// 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 false;
}
return true;
}
bool Foam::probes::execute()
{
if (onExecute_)
{
return performAction(ACTION_ALL & ~ACTION_WRITE);
}
return true;
}
bool Foam::probes::write()
{
return performAction(ACTION_ALL);
}
void Foam::probes::updateMesh(const mapPolyMesh& mpm)
{
DebugInfo<< "probes: updateMesh" << endl;
if (&mpm.mesh() != &mesh_)
{
return;
}
if (fixedLocations_)
{
findElements(mesh_);
}
else
{
DebugInfo<< "probes: remapping sample locations" << endl;
// 1. Update cells
{
DynamicList<label> elems(elementList_.size());
const labelList& reverseMap = mpm.reverseCellMap();
forAll(elementList_, i)
{
label celli = elementList_[i];
if (celli != -1)
{
label newCelli = reverseMap[celli];
if (newCelli == -1)
{
// cell removed
}
else if (newCelli < -1)
{
// cell merged
elems.append(-newCelli - 2);
}
else
{
// valid new cell
elems.append(newCelli);
}
}
else
{
// Keep -1 elements so the size stays the same
elems.append(-1);
}
}
elementList_.transfer(elems);
}
// 2. Update faces
{
DynamicList<label> elems(faceList_.size());
const labelList& reverseMap = mpm.reverseFaceMap();
for (const label facei : faceList_)
{
if (facei != -1)
{
label newFacei = reverseMap[facei];
if (newFacei == -1)
{
// face removed
}
else if (newFacei < -1)
{
// face merged
elems.append(-newFacei - 2);
}
else
{
// valid new face
elems.append(newFacei);
}
}
else
{
// Keep -1 elements
elems.append(-1);
}
}
faceList_.transfer(elems);
}
}
}
void Foam::probes::movePoints(const polyMesh& mesh)
{
DebugInfo<< "probes: movePoints" << endl;
if (fixedLocations_ && &mesh == &mesh_)
{
findElements(mesh_);
}
}
// ************************************************************************* //

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -31,237 +31,88 @@ Group
grpUtilitiesFunctionObjects
Description
Set of locations to sample.
Function object to sample fields at specified internal-mesh locations and
write to file.
Call write() to sample and write files.
Example of function object specification:
Usage
Minimal example by using \c system/controlDict.functions:
\verbatim
probes
{
// Mandatory entries
type probes;
libs (sampling);
// Name of the directory for probe data
name probes;
fields (<wordRes>);
probeLocations (<vectorList>);
// Write at same frequency as fields
writeControl outputTime;
writeInterval 1;
// Optional entries
verbose <bool>;
sampleOnExecute <bool>;
fixedLocations <bool>;
includeOutOfBounds <bool>;
interpolationScheme <word>;
// Fields to be probed
fields (U "p.*");
// Optional: do not recalculate cells if mesh moves
fixedLocations false;
// Optional: interpolation scheme to use (default is cell)
interpolationScheme cellPoint;
probeLocations
(
( 1e-06 0 0.01 ) // at inlet
(0.21 -0.20999 0.01) // at outlet1
(0.21 0.20999 0.01) // at outlet2
(0.21 0 0.01) // at central block
);
// Optional: filter out points that haven't been found. Default
// is to include them (with value -VGREAT)
includeOutOfBounds true;
// Inherited entries
...
}
\endverbatim
Entries:
where the entries mean:
\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
Property | Description | Type | Reqd | Deflt
type | Type name: probes | word | yes | -
libs | Library name: sampling | word | yes | -
fields | Names of fields to probe | wordRes | yes | -
probeLocations | Locations of probes | vectorList | yes | -
verbose | Enable/disable verbose output | bool | no | false
sampleOnExecute | Sample on execution and store results | bool | no <!--
--> | false
fixedLocations | Do not recalculate cells if mesh moves | bool | no | true
includeOutOfBounds | Include out-of-bounds locations | bool | no | true
interpolationScheme | Scheme to obtain values at the points | word <!--
--> | no | cell
\endtable
The inherited entries are elaborated in:
- \link fvMeshFunctionObject.H \endlink
- \link ProbesBase.H \endlink
- \link internalPointProber.H \endlink
- \link pointProberBase.H \endlink
Note
- The \c includeOutOfBounds filters out points that haven't been found.
Default is to include them (with value \c -VGREAT).
SourceFiles
probes.C
probesTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_probes_H
#define Foam_probes_H
#include "fvMeshFunctionObject.H"
#include "HashPtrTable.H"
#include "OFstream.H"
#include "polyMesh.H"
#include "pointField.H"
#include "volFieldsFwd.H"
#include "surfaceFieldsFwd.H"
#include "surfaceMesh.H"
#include "wordRes.H"
#include "IOobjectList.H"
#include "ProbesBase.H"
#include "internalPointProber.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward Declarations
class Time;
class objectRegistry;
class dictionary;
class fvMesh;
class mapPolyMesh;
/*---------------------------------------------------------------------------*\
Class probes Declaration
\*---------------------------------------------------------------------------*/
class probes
:
public functionObjects::fvMeshFunctionObject,
public pointField
public ProbesBase<internalPointProber>
{
protected:
// Private Data
// Protected Classes
//- Use simpler synonym for the base type
using Base = ProbesBase<internalPointProber>;
//- 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
{
ACTION_NONE = 0,
ACTION_WRITE = 0x1,
ACTION_STORE = 0x2,
ACTION_ALL = 0xF
};
// Protected Data
//- Load fields from files (not from objectRegistry)
bool loadFromFiles_;
//- Fixed locations (default: true)
// Note: set to false for moving mesh calculations where locations
// should move with the mesh
bool fixedLocations_;
//- Include probes that were not found (default: true)
bool includeOutOfBounds_;
//- Output verbosity
bool verbose_;
//- Perform sample actions on execute as well
bool onExecute_;
//- Requested names of fields to probe
wordRes fieldSelection_;
//- 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_;
//- Faces to be probed
labelList faceList_;
//- Processor holding the cell or face (-1 if point not found
// on any processor)
labelList processor_;
//- Current open files (non-empty on master only)
HashPtrTable<OFstream> probeFilePtrs_;
//- Patch IDs on which the new probes are located (for patchProbes)
labelList patchIDList_;
//- Original probes location (only used for patchProbes)
pointField oldPoints_;
// Protected Member Functions
//- Find cells and faces containing probes
virtual void findElements(const fvMesh& mesh);
//- 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:
// Private Member Functions
//- Create new streams as required
void createProbeFiles(const wordList& fieldNames);
//- Write field values
template<class Type>
void writeValues
(
const word& fieldName,
const Field<Type>& values,
const scalar timeValue
);
//- Sample and store/write all applicable sampled 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
probes(const probes&) = delete;
//- No copy assignment
void operator=(const probes&) = delete;
public:
@ -288,86 +139,19 @@ public:
// Member Functions
//- Enable/disable verbose output
// \return old value
bool verbose(const bool on) noexcept;
//- Bring Base::prober into this class's public scope.
using Base::prober;
//- Return names of fields to probe
virtual const wordRes& fieldNames() const noexcept
{
return fieldSelection_;
}
//- Return locations to probe
virtual const pointField& probeLocations() const noexcept
{
return *this;
}
//- Return location for probe i
virtual const point& probe(const label i) const
{
return operator[](i);
}
//- Cells to be probed (obtained from the locations)
const labelList& elements() const noexcept
{
return elementList_;
}
//- Read the probes
//- Read the settings from the dictionary
virtual bool read(const dictionary&);
//- Sample and store result if the sampleOnExecute is enabled.
virtual bool execute();
//- Sample and write
virtual bool write();
//- Update for changes of mesh
virtual void updateMesh(const mapPolyMesh&);
//- Update for changes of mesh
virtual void movePoints(const polyMesh&);
//- Update for changes of mesh due to readUpdate
virtual void readUpdate(const polyMesh::readUpdateState state)
{}
// 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 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;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "probesTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,35 +0,0 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2306 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object probesDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Fields to be probed. runTime modifiable!
fields
(
p
);
// Locations to be probed. runTime modifiable!
probeLocations
(
(0.0254 0.0253 0.0)
(0.0508 0.0253 0.0)
(0.0762 0.0253 0.0)
(0.1016 0.0253 0.0)
(0.1270 0.0253 0.0)
(0.1524 0.0253 0.0)
(0.1778 0.0253 0.0)
);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -240,6 +240,9 @@ public:
return radiation_;
}
//- Return the radiation solver frequency
label solverFreq() const noexcept { return solverFreq_; }
//- Source term component (for power of T^4)
virtual tmp<volScalarField> Rp() const = 0;